Migrate test_utils from acts to acts_contrib
Bug: 171076051
Test: local
Change-Id: Idddaf0a715a5d3e48f13614328191fd270ee5582
Merged-In: Idddaf0a715a5d3e48f13614328191fd270ee5582
diff --git a/acts_tests/acts_contrib/test_utils b/acts_tests/acts_contrib/test_utils
deleted file mode 120000
index 8a6ce16..0000000
--- a/acts_tests/acts_contrib/test_utils
+++ /dev/null
@@ -1 +0,0 @@
-../../acts/framework/acts/test_utils
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/OWNERS b/acts_tests/acts_contrib/test_utils/OWNERS
new file mode 100644
index 0000000..bf3ed6c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/OWNERS
@@ -0,0 +1 @@
+include /acts_tests/tests/OWNERS
diff --git a/acts_tests/acts_contrib/test_utils/__init__.py b/acts_tests/acts_contrib/test_utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/__init__.py b/acts_tests/acts_contrib/test_utils/abstract_devices/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py
new file mode 100644
index 0000000..1e02d89
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py
@@ -0,0 +1,1471 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import inspect
+import logging
+
+from queue import Empty
+
+from acts.controllers.android_device import AndroidDevice
+from acts.controllers.fuchsia_device import FuchsiaDevice
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
+
+import acts_contrib.test_utils.bt.bt_test_utils as bt_test_utils
+
+
+def create_bluetooth_device(hardware_device):
+ """Creates a generic Bluetooth device based on type of device that is sent
+ to the functions.
+
+ Args:
+ hardware_device: A Bluetooth hardware device that is supported by ACTS.
+ """
+ if isinstance(hardware_device, FuchsiaDevice):
+ return FuchsiaBluetoothDevice(hardware_device)
+ elif isinstance(hardware_device, AndroidDevice):
+ return AndroidBluetoothDevice(hardware_device)
+ else:
+ raise ValueError('Unable to create BluetoothDevice for type %s' %
+ type(hardware_device))
+
+
+class BluetoothDevice(object):
+ """Class representing a generic Bluetooth device.
+
+ Each object of this class represents a generic Bluetooth device.
+ Android device and Fuchsia devices are the currently supported devices.
+
+ Attributes:
+ device: A generic Bluetooth device.
+ """
+ def __init__(self, device):
+ self.device = device
+ self.log = logging
+
+ def a2dp_initiate_open_stream(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def start_profile_a2dp_sink(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def stop_profile_a2dp_sink(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def start_pairing_helper(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def set_discoverable(self, is_discoverable):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def bluetooth_toggle_state(self, state):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_discover_characteristic_by_uuid(self, peer_identifier,
+ uuid):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def initialize_bluetooth_controller(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def get_pairing_pin(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def input_pairing_pin(self, pin):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def get_bluetooth_local_address(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_connect(self, peer_identifier, transport, autoconnect):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_write_characteristic_without_response_by_handle(
+ self, peer_identifier, handle, value):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_write_characteristic_by_handle(self, peer_identifier,
+ handle, offset, value):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_read_characteristic_by_handle(self, peer_identifier,
+ handle):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_read_characteristic_by_uuid(self, peer_identifier, uuid):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
+ handle, offset,
+ max_bytes):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_enable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_disable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
+ offset, value):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_long_read_descriptor_by_handle(self, peer_identifier,
+ handle, offset, max_bytes):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_disconnect(self, peer_identifier):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_refresh(self, peer_identifier):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def le_scan_with_name_filter(self, name, timeout):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def log_info(self, log):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def reset_bluetooth(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def sdp_add_search(self, attribute_list, profile_id):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def sdp_add_service(self, sdp_record):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def sdp_clean_up(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def sdp_init(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def sdp_remove_service(self, service_id):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def start_le_advertisement(self, adv_data, scan_response, adv_interval,
+ connectable):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def stop_le_advertisement(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def set_bluetooth_local_name(self, name):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def setup_gatt_server(self, database):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def close_gatt_server(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def unbond_device(self, peer_identifier):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def unbond_all_known_devices(self):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def init_pair(self, peer_identifier, security_level, non_bondable,
+ transport):
+ """Base generic Bluetooth interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+
+class AndroidBluetoothDevice(BluetoothDevice):
+ """Class wrapper for an Android Bluetooth device.
+
+ Each object of this class represents a generic Bluetooth device.
+ Android device and Fuchsia devices are the currently supported devices/
+
+ Attributes:
+ android_device: An Android Bluetooth device.
+ """
+ def __init__(self, android_device):
+ super().__init__(android_device)
+ self.gatt_timeout = 10
+ self.peer_mapping = {}
+ self.discovered_services_index = None
+
+ def _client_wait(self, gatt_event, gatt_callback):
+ return self._timed_pop(gatt_event, gatt_callback)
+
+ def _timed_pop(self, gatt_event, gatt_callback):
+ expected_event = gatt_event["evt"].format(gatt_callback)
+ try:
+ return self.device.ed.pop_event(expected_event, self.gatt_timeout)
+ except Empty as emp:
+ raise AssertionError(gatt_event["err"].format(expected_event))
+
+ def _setup_discovered_services_index(self, bluetooth_gatt):
+ """ Sets the discovered services index for the gatt connection
+ related to the Bluetooth GATT callback object.
+
+ Args:
+ bluetooth_gatt: The BluetoothGatt callback id
+ """
+ if not self.discovered_services_index:
+ self.device.droid.gattClientDiscoverServices(bluetooth_gatt)
+ expected_event = gatt_cb_strings['gatt_serv_disc'].format(
+ self.gatt_callback)
+ event = self.dut.ed.pop_event(expected_event, self.gatt_timeout)
+ self.discovered_services_index = event['data']['ServicesIndex']
+
+ def a2dp_initiate_open_stream(self):
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def start_profile_a2dp_sink(self):
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def stop_profile_a2dp_sink(self):
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def bluetooth_toggle_state(self, state):
+ self.device.droid.bluetoothToggleState(state)
+
+ def set_discoverable(self, is_discoverable):
+ """ Sets the device's discoverability.
+
+ Args:
+ is_discoverable: True if discoverable, false if not discoverable
+ """
+ if is_discoverable:
+ self.device.droid.bluetoothMakeDiscoverable()
+ else:
+ self.device.droid.bluetoothMakeUndiscoverable()
+
+ def initialize_bluetooth_controller(self):
+ """ Just pass for Android as there is no concept of initializing
+ a Bluetooth controller.
+ """
+ pass
+
+ def start_pairing_helper(self):
+ """ Starts the Android pairing helper.
+ """
+ self.device.droid.bluetoothStartPairingHelper(True)
+
+ def gatt_client_write_characteristic_without_response_by_handle(
+ self, peer_identifier, handle, value):
+ """ Perform a GATT Client write Characteristic without response to
+ remote peer GATT server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The characteristic handle (or instance id).
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.device.droid.gattClientWriteCharacteristicByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle, value)
+ try:
+ event = self._client_wait(gatt_event['char_write'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to write Characteristic: {}".format(err))
+ return True
+
+ def gatt_client_write_characteristic_by_handle(self, peer_identifier,
+ handle, offset, value):
+ """ Perform a GATT Client write Characteristic without response to
+ remote peer GATT server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The characteristic handle (or instance id).
+ offset: Not used yet.
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.device.droid.gattClientWriteCharacteristicByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle, value)
+ try:
+ event = self._client_wait(gatt_event['char_write'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to write Characteristic: {}".format(err))
+ return True
+
+ def gatt_client_read_characteristic_by_handle(self, peer_identifier,
+ handle):
+ """ Perform a GATT Client read Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The characteristic handle (or instance id).
+ Returns:
+ Value of Characteristic if success, None if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadCharacteristicByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle)
+ try:
+ event = self._client_wait(gatt_event['char_read'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to read Characteristic: {}".format(err))
+
+ return event['data']['CharacteristicValue']
+
+ def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
+ handle, offset,
+ max_bytes):
+ """ Perform a GATT Client read Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ offset: Not used yet.
+ handle: The characteristic handle (or instance id).
+ max_bytes: Not used yet.
+ Returns:
+ Value of Characteristic if success, None if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadCharacteristicByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle)
+ try:
+ event = self._client_wait(gatt_event['char_read'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to read Characteristic: {}".format(err))
+
+ return event['data']['CharacteristicValue']
+
+ def gatt_client_enable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """ Perform a GATT Client enable Characteristic notification to remote
+ peer GATT server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The characteristic handle.
+ Returns:
+ True is success, False if failure.
+ """
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_disable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """ Perform a GATT Client disable Characteristic notification to remote
+ peer GATT server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The characteristic handle.
+ Returns:
+ True is success, False if failure.
+ """
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
+ """ Perform a GATT Client read Descriptor to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The Descriptor handle (or instance id).
+ Returns:
+ Value of Descriptor if success, None if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadDescriptorByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle)
+ try:
+ event = self._client_wait(gatt_event['desc_read'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to read Descriptor: {}".format(err))
+ # TODO: Implement sending Descriptor value in SL4A such that the data
+ # can be represented by: event['data']['DescriptorValue']
+ return ""
+
+ def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
+ offset, value):
+ """ Perform a GATT Client write Descriptor to the remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The mac address associated with the GATT connection
+ handle: The Descriptor handle (or instance id).
+ offset: Not used yet
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "Peer idenifier {} not currently connected or unknown.".format(
+ peer_identifier))
+ return False
+ self._setup_discovered_services_index()
+ self.device.droid.gattClientWriteDescriptorByInstanceId(
+ peer_info.get('bluetooth_gatt'), self.discovered_services_index,
+ handle, value)
+ try:
+ event = self._client_wait(gatt_event['desc_write'],
+ peer_info.get('gatt_callback'))
+ except AssertionError as err:
+ self.log.error("Failed to write Characteristic: {}".format(err))
+ return True
+
+ def gatt_connect(self, peer_identifier, transport, autoconnect=False):
+ """ Perform a GATT connection to a perihperal.
+
+ Args:
+ peer_identifier: The mac address to connect to.
+ transport: Which transport to use.
+ autoconnect: Set autocnnect to True or False.
+ Returns:
+ True if success, False if failure.
+ """
+ try:
+ bluetooth_gatt, gatt_callback = setup_gatt_connection(
+ self.device, peer_identifier, autoconnect, transport)
+ self.peer_mapping[peer_identifier] = {
+ "bluetooth_gatt": bluetooth_gatt,
+ "gatt_callback": gatt_callback
+ }
+ except GattTestUtilsError as err:
+ self.log.error(err)
+ return False
+ return True
+
+ def gatt_disconnect(self, peer_identifier):
+ """ Perform a GATT disconnect from a perihperal.
+
+ Args:
+ peer_identifier: The peer to disconnect from.
+ Returns:
+ True if success, False if failure.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "No previous connections made to {}".format(peer_identifier))
+ return False
+
+ try:
+ disconnect_gatt_connection(self.device,
+ peer_info.get("bluetooth_gatt"),
+ peer_info.get("gatt_callback"))
+ self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt"))
+ except GattTestUtilsError as err:
+ self.log.error(err)
+ return False
+ self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt"))
+
+ def gatt_client_refresh(self, peer_identifier):
+ """ Perform a GATT Client Refresh of a perihperal.
+
+ Clears the internal cache and forces a refresh of the services from the
+ remote device.
+
+ Args:
+ peer_identifier: The peer to refresh.
+ """
+ peer_info = self.peer_mapping.get(peer_identifier)
+ if not peer_info:
+ self.log.error(
+ "No previous connections made to {}".format(peer_identifier))
+ return False
+ self.device.droid.gattClientRefresh(peer_info["bluetooth_gatt"])
+
+ def le_scan_with_name_filter(self, name, timeout):
+ """ Scan over LE for a specific device name.
+
+ Args:
+ name: The name filter to set.
+ timeout: The timeout to wait to find the advertisement.
+ Returns:
+ Discovered mac address or None
+ """
+ self.device.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
+ filter_list = self.device.droid.bleGenFilterList()
+ scan_settings = self.device.droid.bleBuildScanSetting()
+ scan_callback = self.device.droid.bleGenScanCallback()
+ self.device.droid.bleSetScanFilterDeviceName(name)
+ self.device.droid.bleBuildScanFilter(filter_list)
+ self.device.droid.bleSetScanFilterDeviceName(self.name)
+ self.device.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+ try:
+ event = self.device.ed.pop_event(scan_result.format(scan_callback),
+ timeout)
+ return event['data']['Result']['deviceInfo']['address']
+ except Empty as err:
+ self.log.info("Scanner did not find advertisement {}".format(err))
+ return None
+
+ def log_info(self, log):
+ """ Log directly onto the device.
+
+ Args:
+ log: The informative log.
+ """
+ self.device.droid.log.logI(log)
+
+ def set_bluetooth_local_name(self, name):
+ """ Sets the Bluetooth controller's local name
+ Args:
+ name: The name to set.
+ """
+ self.device.droid.bluetoothSetLocalName(name)
+
+ def get_local_bluetooth_address(self):
+ """ Returns the Bluetooth local address.
+ """
+ return self.device.droid.bluetoothGetLocalAddress()
+
+ def reset_bluetooth(self):
+ """ Resets Bluetooth on the Android Device.
+ """
+ bt_test_utils.reset_bluetooth([self.device])
+
+ def sdp_add_search(self, attribute_list, profile_id):
+ """Adds an SDP search record.
+ Args:
+ attribute_list: The list of attributes to set
+ profile_id: The profile ID to set.
+ """
+ # Android devices currently have no hooks to modify the SDP record.
+ pass
+
+ def sdp_add_service(self, sdp_record):
+ """Adds an SDP service record.
+ Args:
+ sdp_record: The dictionary representing the search record to add.
+ Returns:
+ service_id: The service id to track the service record published.
+ None if failed.
+ """
+ # Android devices currently have no hooks to modify the SDP record.
+ pass
+
+ def sdp_clean_up(self):
+ """Cleans up all objects related to SDP.
+ """
+ self.device.sdp_lib.cleanUp()
+
+ def sdp_init(self):
+ """Initializes SDP on the device.
+ """
+ # Android devices currently have no hooks to modify the SDP record.
+ pass
+
+ def sdp_remove_service(self, service_id):
+ """Removes a service based on an input id.
+ Args:
+ service_id: The service ID to remove.
+ """
+ # Android devices currently have no hooks to modify the SDP record.
+ pass
+
+ def unbond_all_known_devices(self):
+ """ Unbond all known remote devices.
+ """
+ self.device.droid.bluetoothFactoryReset()
+
+ def unbond_device(self, peer_identifier):
+ """ Unbond peer identifier.
+
+ Args:
+ peer_identifier: The mac address for the peer to unbond.
+
+ """
+ self.device.droid.bluetoothUnbond(peer_identifier)
+
+ def init_pair(self, peer_identifier, security_level, non_bondable,
+ transport):
+ """ Send an outgoing pairing request the input peer_identifier.
+
+ Android currently does not support setting various security levels or
+ bondable modes. Making them available for other bluetooth_device
+ variants. Depending on the Address type, Android will figure out the
+ transport to pair automatically.
+
+ Args:
+ peer_identifier: A string representing the device id.
+ security_level: Not yet implemented. See Fuchsia device impl.
+ non_bondable: Not yet implemented. See Fuchsia device impl.
+ transport: Not yet implemented. See Fuchsia device impl.
+
+ """
+ self.dut.droid.bluetoothBond(self.peer_identifier)
+
+
+class FuchsiaBluetoothDevice(BluetoothDevice):
+ """Class wrapper for an Fuchsia Bluetooth device.
+
+ Each object of this class represents a generic luetooth device.
+ Android device and Fuchsia devices are the currently supported devices/
+
+ Attributes:
+ fuchsia_device: A Fuchsia Bluetooth device.
+ """
+ def __init__(self, fuchsia_device):
+ super().__init__(fuchsia_device)
+
+ def a2dp_initiate_open_stream(self):
+ raise NotImplementedError("{} not yet implemented.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def start_profile_a2dp_sink(self):
+ """ Starts the A2DP sink profile.
+ """
+ self.device.control_daemon("bt-a2dp-sink.cmx", "start")
+
+ def stop_profile_a2dp_sink(self):
+ """ Stops the A2DP sink profile.
+ """
+ self.device.control_daemon("bt-a2dp-sink.cmx", "stop")
+
+ def start_pairing_helper(self):
+ self.device.bts_lib.acceptPairing()
+
+ def bluetooth_toggle_state(self, state):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def set_discoverable(self, is_discoverable):
+ """ Sets the device's discoverability.
+
+ Args:
+ is_discoverable: True if discoverable, false if not discoverable
+ """
+ self.device.bts_lib.setDiscoverable(is_discoverable)
+
+ def get_pairing_pin(self):
+ """ Get the pairing pin from the active pairing delegate.
+ """
+ return self.device.bts_lib.getPairingPin()['result']
+
+ def input_pairing_pin(self, pin):
+ """ Input pairing pin to active pairing delegate.
+
+ Args:
+ pin: The pin to input.
+ """
+ self.device.bts_lib.inputPairingPin(pin)
+
+ def initialize_bluetooth_controller(self):
+ """ Initialize Bluetooth controller for first time use.
+ """
+ self.device.bts_lib.initBluetoothSys()
+
+ def get_local_bluetooth_address(self):
+ """ Returns the Bluetooth local address.
+ """
+ return self.device.bts_lib.getActiveAdapterAddress().get("result")
+
+ def set_bluetooth_local_name(self, name):
+ """ Sets the Bluetooth controller's local name
+ Args:
+ name: The name to set.
+ """
+ self.device.bts_lib.setName(name)
+
+ def gatt_client_write_characteristic_without_response_by_handle(
+ self, peer_identifier, handle, value):
+ """ Perform a GATT Client write Characteristic without response to
+ remote peer GATT server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.writeCharByIdWithoutResponse(
+ handle, value)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to write characteristic handle {} with err: {}".format(
+ handle, result.get("error")))
+ return False
+ return True
+
+ def gatt_client_write_characteristic_by_handle(self, peer_identifier,
+ handle, offset, value):
+ """ Perform a GATT Client write Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ offset: The offset to start writing to.
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.writeCharById(handle, offset, value)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to write characteristic handle {} with err: {}".format(
+ handle, result.get("error")))
+ return False
+ return True
+
+ def gatt_client_write_long_characteristic_by_handle(
+ self, peer_identifier, handle, offset, value, reliable_mode=False):
+ """ Perform a GATT Client write long Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ offset: The offset to start writing to.
+ value: The list of bytes to write.
+ reliable_mode: A bool value representing a reliable write or not.
+ Returns:
+ True if success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.error(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ return False
+ result = self.device.gattc_lib.writeLongCharById(
+ handle, offset, value, reliable_mode)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to write long characteristic handle {} with err: {}".
+ format(peer_identifier, result.get("error")))
+ return False
+ return True
+
+ def gatt_client_write_long_descriptor_by_handle(self, peer_identifier,
+ handle, offset, value):
+ """ Perform a GATT Client write long Descriptor to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The descriptor handle.
+ offset: The offset to start writing to.
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.error(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ return False
+ result = self.device.gattc_lib.writeLongDescById(handle, offset, value)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to write long descriptor handle {} with err: {}".
+ format(peer_identifier, result.get("error")))
+ return False
+ return True
+
+ def gatt_client_read_characteristic_by_handle(self, peer_identifier,
+ handle):
+ """ Perform a GATT Client read Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ Returns:
+ Value of Characteristic if success, None if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.readCharacteristicById(handle)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to read characteristic handle {} with err: {}".format(
+ handle, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_read_characteristic_by_uuid(self, peer_identifier, uuid):
+ """ Perform a GATT Client read Characteristic by uuid to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ uuid: The characteristic uuid.
+ Returns:
+ Value of Characteristic if success, None if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, uuid, uuid=True)):
+ self.log.warn(
+ "Unable to find uuid {} in GATT server db.".format(uuid))
+ result = self.device.gattc_lib.readCharacteristicByType(uuid)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to read characteristic uuid {} with err: {}".format(
+ uuid, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
+ handle, offset,
+ max_bytes):
+ """ Perform a GATT Client read Characteristic to remote peer GATT
+ server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ offset: The offset to start reading.
+ max_bytes: The max bytes to return for each read.
+ Returns:
+ Value of Characteristic if success, None if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.readLongCharacteristicById(
+ handle, offset, max_bytes)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to read characteristic handle {} with err: {}".format(
+ handle, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_enable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """ Perform a GATT Client enable Characteristic notification to remote
+ peer GATT server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ Returns:
+ True is success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.enableNotifyCharacteristic(handle)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to enable characteristic notifications for handle {} "
+ "with err: {}".format(handle, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_disable_notifiy_characteristic_by_handle(
+ self, peer_identifier, handle):
+ """ Perform a GATT Client disable Characteristic notification to remote
+ peer GATT server database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The characteristic handle.
+ Returns:
+ True is success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.disableNotifyCharacteristic(handle)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to disable characteristic notifications for handle {} "
+ "with err: {}".format(peer_identifier, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
+ """ Perform a GATT Client read Descriptor to remote peer GATT server
+ database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The Descriptor handle.
+ Returns:
+ Value of Descriptor if success, None if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.readDescriptorById(handle)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to read descriptor for handle {} with err: {}".format(
+ peer_identifier, result.get("error")))
+ return None
+ return result.get("result")
+
+ def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
+ offset, value):
+ """ Perform a GATT Client write Descriptor to remote peer GATT server
+ database.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ handle: The Descriptor handle.
+ offset: The offset to start writing at.
+ value: The list of bytes to write.
+ Returns:
+ True if success, False if failure.
+ """
+ if (not self._find_service_id_and_connect_to_service_for_handle(
+ peer_identifier, handle)):
+ self.log.warn(
+ "Unable to find handle {} in GATT server db.".format(handle))
+ result = self.device.gattc_lib.writeDescriptorById(
+ handle, offset, value)
+ if result.get("error") is not None:
+ self.log.error(
+ "Failed to write descriptor for handle {} with err: {}".format(
+ peer_identifier, result.get("error")))
+ return None
+ return True
+
+ def gatt_connect(self, peer_identifier, transport, autoconnect):
+ """ Perform a GATT connection to a perihperal.
+
+ Args:
+ peer_identifier: The peer to connect to.
+ transport: Not implemented.
+ autoconnect: Not implemented.
+ Returns:
+ True if success, False if failure.
+ """
+ connection_result = self.device.gattc_lib.bleConnectToPeripheral(
+ peer_identifier)
+ if connection_result.get("error") is not None:
+ self.log.error("Failed to connect to peer id {}: {}".format(
+ peer_identifier, connection_result.get("error")))
+ return False
+ return True
+
+ def gatt_client_refresh(self, peer_identifier):
+ """ Perform a GATT Client Refresh of a perihperal.
+
+ Clears the internal cache and forces a refresh of the services from the
+ remote device. In Fuchsia there is no FIDL api to automatically do this
+ yet. Therefore just read all Characteristics which satisfies the same
+ requirements.
+
+ Args:
+ peer_identifier: The peer to refresh.
+ """
+ self._read_all_characteristics(peer_identifier)
+
+ def gatt_client_discover_characteristic_by_uuid(self, peer_identifier,
+ uuid):
+ """ Perform a GATT Client Refresh of a perihperal.
+
+ Clears the internal cache and forces a refresh of the services from the
+ remote device. In Fuchsia there is no FIDL api to automatically do this
+ yet. Therefore just read all Characteristics which satisfies the same
+ requirements.
+
+ Args:
+ peer_identifier: The peer to refresh.
+ """
+ self._read_all_characteristics(peer_identifier, uuid)
+
+ def gatt_disconnect(self, peer_identifier):
+ """ Perform a GATT disconnect from a perihperal.
+
+ Args:
+ peer_identifier: The peer to disconnect from.
+ Returns:
+ True if success, False if failure.
+ """
+ disconnect_result = self.device.gattc_lib.bleDisconnectPeripheral(
+ peer_identifier)
+ if disconnect_result.get("error") is None:
+ self.log.error("Failed to disconnect from peer id {}: {}".format(
+ peer_identifier, disconnect_result.get("error")))
+ return False
+ return True
+
+ def reset_bluetooth(self):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def sdp_add_search(self, attribute_list, profile_id):
+ """Adds an SDP search record.
+ Args:
+ attribute_list: The list of attributes to set
+ profile_id: The profile ID to set.
+ """
+ return self.device.sdp_lib.addSearch(attribute_list, profile_id)
+
+ def sdp_add_service(self, sdp_record):
+ """Adds an SDP service record.
+ Args:
+ sdp_record: The dictionary representing the search record to add.
+ """
+ return self.device.sdp_lib.addService(sdp_record)
+
+ def sdp_clean_up(self):
+ """Cleans up all objects related to SDP.
+ """
+ return self.device.sdp_lib.cleanUp()
+
+ def sdp_init(self):
+ """Initializes SDP on the device.
+ """
+ return self.device.sdp_lib.init()
+
+ def sdp_remove_service(self, service_id):
+ """Removes a service based on an input id.
+ Args:
+ service_id: The service ID to remove.
+ """
+ return self.device.sdp_lib.init()
+
+ def start_le_advertisement(self, adv_data, scan_response, adv_interval,
+ connectable):
+ """ Starts an LE advertisement
+
+ Args:
+ adv_data: Advertisement data.
+ adv_interval: Advertisement interval.
+ """
+ self.device.ble_lib.bleStartBleAdvertising(adv_data, scan_response,
+ adv_interval, connectable)
+
+ def stop_le_advertisement(self):
+ """ Stop active LE advertisement.
+ """
+ self.device.ble_lib.bleStopBleAdvertising()
+
+ def setup_gatt_server(self, database):
+ """ Sets up an input GATT server.
+
+ Args:
+ database: A dictionary representing the GATT database to setup.
+ """
+ self.device.gatts_lib.publishServer(database)
+
+ def close_gatt_server(self):
+ """ Closes an existing GATT server.
+ """
+ self.device.gatts_lib.closeServer()
+
+ def le_scan_with_name_filter(self, name, timeout):
+ """ Scan over LE for a specific device name.
+
+ Args:
+ name: The name filter to set.
+ timeout: The timeout to wait to find the advertisement.
+ Returns:
+ Discovered device id or None
+ """
+ partial_match = True
+ return le_scan_for_device_by_name(self.device, self.device.log, name,
+ timeout, partial_match)
+
+ def log_info(self, log):
+ """ Log directly onto the device.
+
+ Args:
+ log: The informative log.
+ """
+ self.device.logging_lib.logI(log)
+ pass
+
+ def unbond_all_known_devices(self):
+ """ Unbond all known remote devices.
+ """
+ try:
+ device_list = self.device.bts_lib.getKnownRemoteDevices()['result']
+ for device_info in device_list:
+ device = device_list[device_info]
+ if device['bonded']:
+ self.device.bts_lib.forgetDevice(device['id'])
+ except Exception as err:
+ self.log.err("Unable to unbond all devices: {}".format(err))
+
+ def unbond_device(self, peer_identifier):
+ """ Unbond peer identifier.
+
+ Args:
+ peer_identifier: The peer identifier for the peer to unbond.
+
+ """
+ self.device.bts_lib.forgetDevice(peer_identifier)
+
+ def _find_service_id_and_connect_to_service_for_handle(
+ self, peer_identifier, handle, uuid=False):
+ fail_err = "Failed to find handle {} in Peer database."
+ if uuid:
+ handle = handle.lower()
+ try:
+ services = self.device.gattc_lib.listServices(peer_identifier)
+ for service in services['result']:
+ service_id = service['id']
+ self.device.gattc_lib.connectToService(peer_identifier,
+ service_id)
+ chars = self.device.gattc_lib.discoverCharacteristics()
+
+ for char in chars['result']:
+ char_id = char['id']
+ if uuid:
+ char_id = char['uuid_type']
+ if handle == char_id:
+ return True
+ descriptors = char['descriptors']
+ for desc in descriptors:
+ desc_id = desc["id"]
+ if uuid:
+ desc_id = desc['uuid_type']
+ if handle == desc_id:
+ return True
+ except Exception as err:
+ self.log.error(fail_err.format(err))
+ return False
+
+ def _read_all_characteristics(self, peer_identifier, uuid=None):
+ fail_err = "Failed to read all characteristics with: {}"
+ try:
+ services = self.device.gattc_lib.listServices(peer_identifier)
+ for service in services['result']:
+ service_id = service['id']
+ service_uuid = service['uuid_type']
+ self.device.gattc_lib.connectToService(peer_identifier,
+ service_id)
+ chars = self.device.gattc_lib.discoverCharacteristics()
+ self.log.info(
+ "Reading chars in service uuid: {}".format(service_uuid))
+
+ for char in chars['result']:
+ char_id = char['id']
+ char_uuid = char['uuid_type']
+ if uuid and uuid.lower() not in char_uuid.lower():
+ continue
+ try:
+ read_val = \
+ self.device.gattc_lib.readCharacteristicById(
+ char_id)
+ self.log.info(
+ "\tCharacteristic uuid / Value: {} / {}".format(
+ char_uuid, read_val['result']))
+ str_value = ""
+ for val in read_val['result']:
+ str_value += chr(val)
+ self.log.info("\t\tstr val: {}".format(str_value))
+ except Exception as err:
+ self.log.error(err)
+ pass
+ except Exception as err:
+ self.log.error(fail_err.forma(err))
+
+ def _perform_read_all_descriptors(self, peer_identifier):
+ fail_err = "Failed to read all characteristics with: {}"
+ try:
+ services = self.device.gattc_lib.listServices(peer_identifier)
+ for service in services['result']:
+ service_id = service['id']
+ service_uuid = service['uuid_type']
+ self.device.gattc_lib.connectToService(peer_identifier,
+ service_id)
+ chars = self.device.gattc_lib.discoverCharacteristics()
+ self.log.info(
+ "Reading descs in service uuid: {}".format(service_uuid))
+
+ for char in chars['result']:
+ char_id = char['id']
+ char_uuid = char['uuid_type']
+ descriptors = char['descriptors']
+ self.log.info(
+ "\tReading descs in char uuid: {}".format(char_uuid))
+ for desc in descriptors:
+ desc_id = desc["id"]
+ desc_uuid = desc["uuid_type"]
+ try:
+ read_val = self.device.gattc_lib.readDescriptorById(
+ desc_id)
+ self.log.info(
+ "\t\tDescriptor uuid / Value: {} / {}".format(
+ desc_uuid, read_val['result']))
+ except Exception as err:
+ pass
+ except Exception as err:
+ self.log.error(fail_err.format(err))
+
+ def init_pair(self, peer_identifier, security_level, non_bondable,
+ transport):
+ """ Send an outgoing pairing request the input peer_identifier.
+
+ Android currently does not support setting various security levels or
+ bondable modes. Making them available for other bluetooth_device
+ variants. Depending on the Address type, Android will figure out the
+ transport to pair automatically.
+
+ Args:
+ peer_identifier: A string representing the device id.
+ security_level: The security level required for this pairing request
+ represented as a u64. (Only for LE pairing)
+ Available Values
+ 1 - ENCRYPTED: Encrypted without MITM protection
+ (unauthenticated)
+ 2 - AUTHENTICATED: Encrypted with MITM protection
+ (authenticated)
+ None: No pairing security level.
+ non_bondable: A bool representing whether the pairing mode is
+ bondable or not. None is also accepted. False if bondable, True
+ if non-bondable
+ transport: A u64 representing the transport type.
+ Available Values
+ 1 - BREDR: Classic BR/EDR transport
+ 2 - LE: Bluetooth Low Energy Transport
+ Returns:
+ True if successful, False if failed.
+ """
+ try:
+ self.device.bts_lib.pair(peer_identifier, security_level,
+ non_bondable, transport)
+ return True
+ except Exception as err:
+ fail_err = "Failed to pair to peer_identifier {} with: {}".format(
+ peer_identifier)
+ self.log.error(fail_err.format(err))
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_handsfree_abstract_device.py b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_handsfree_abstract_device.py
new file mode 100644
index 0000000..00e0c4a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_handsfree_abstract_device.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import inspect
+import time
+from acts import asserts
+from acts.controllers.buds_lib.dev_utils import apollo_sink_events
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+
+
+
+def validate_controller(controller, abstract_device_class):
+ """Ensure controller has all methods in abstract_device_class.
+ Also checks method signatures to ensure parameters are satisfied.
+
+ Args:
+ controller: instance of a device controller.
+ abstract_device_class: class definition of an abstract_device interface.
+ Raises:
+ NotImplementedError: if controller is missing one or more methods.
+ """
+ ctlr_methods = inspect.getmembers(controller, predicate=callable)
+ reqd_methods = inspect.getmembers(
+ abstract_device_class, predicate=inspect.ismethod)
+ expected_func_names = {method[0] for method in reqd_methods}
+ controller_func_names = {method[0] for method in ctlr_methods}
+
+ if not controller_func_names.issuperset(expected_func_names):
+ raise NotImplementedError(
+ 'Controller {} is missing the following functions: {}'.format(
+ controller.__class__.__name__,
+ repr(expected_func_names - controller_func_names)))
+
+ for func_name in expected_func_names:
+ controller_func = getattr(controller, func_name)
+ required_func = getattr(abstract_device_class, func_name)
+ required_signature = inspect.signature(required_func)
+ if inspect.signature(controller_func) != required_signature:
+ raise NotImplementedError(
+ 'Method {} must have the signature {}{}.'.format(
+ controller_func.__qualname__, controller_func.__name__,
+ required_signature))
+
+
+class BluetoothHandsfreeAbstractDevice:
+ """Base class for all Bluetooth handsfree abstract devices.
+
+ Desired controller classes should have a corresponding Bluetooth handsfree
+ abstract device class defined in this module.
+ """
+
+ @property
+ def mac_address(self):
+ raise NotImplementedError
+
+ def accept_call(self):
+ raise NotImplementedError()
+
+ def end_call(self):
+ raise NotImplementedError()
+
+ def enter_pairing_mode(self):
+ raise NotImplementedError()
+
+ def next_track(self):
+ raise NotImplementedError()
+
+ def pause(self):
+ raise NotImplementedError()
+
+ def play(self):
+ raise NotImplementedError()
+
+ def power_off(self):
+ raise NotImplementedError()
+
+ def power_on(self):
+ raise NotImplementedError()
+
+ def previous_track(self):
+ raise NotImplementedError()
+
+ def reject_call(self):
+ raise NotImplementedError()
+
+ def volume_down(self):
+ raise NotImplementedError()
+
+ def volume_up(self):
+ raise NotImplementedError()
+
+
+class PixelBudsBluetoothHandsfreeAbstractDevice(
+ BluetoothHandsfreeAbstractDevice):
+
+ CMD_EVENT = 'EvtHex'
+
+ def __init__(self, pixel_buds_controller):
+ self.pixel_buds_controller = pixel_buds_controller
+
+ def format_cmd(self, cmd_name):
+ return self.CMD_EVENT + ' ' + apollo_sink_events.SINK_EVENTS[cmd_name]
+
+ @property
+ def mac_address(self):
+ return self.pixel_buds_controller.bluetooth_address
+
+ def accept_call(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrAnswer'))
+
+ def end_call(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrCancelEnd'))
+
+ def enter_pairing_mode(self):
+ return self.pixel_buds_controller.set_pairing_mode()
+
+ def next_track(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrAvrcpSkipForward'))
+
+ def pause(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrAvrcpPause'))
+
+ def play(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrAvrcpPlay'))
+
+ def power_off(self):
+ return self.pixel_buds_controller.power('Off')
+
+ def power_on(self):
+ return self.pixel_buds_controller.power('On')
+
+ def previous_track(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrAvrcpSkipBackward'))
+
+ def reject_call(self):
+ return self.pixel_buds_controller.cmd(
+ self.format_cmd('EventUsrReject'))
+
+ def volume_down(self):
+ return self.pixel_buds_controller.volume('Down')
+
+ def volume_up(self):
+ return self.pixel_buds_controller.volume('Up')
+
+
+class EarstudioReceiverBluetoothHandsfreeAbstractDevice(
+ BluetoothHandsfreeAbstractDevice):
+ def __init__(self, earstudio_controller):
+ self.earstudio_controller = earstudio_controller
+
+ @property
+ def mac_address(self):
+ return self.earstudio_controller.mac_address
+
+ def accept_call(self):
+ return self.earstudio_controller.press_accept_call()
+
+ def end_call(self):
+ return self.earstudio_controller.press_end_call()
+
+ def enter_pairing_mode(self):
+ return self.earstudio_controller.enter_pairing_mode()
+
+ def next_track(self):
+ return self.earstudio_controller.press_next()
+
+ def pause(self):
+ return self.earstudio_controller.press_play_pause()
+
+ def play(self):
+ return self.earstudio_controller.press_play_pause()
+
+ def power_off(self):
+ return self.earstudio_controller.power_off()
+
+ def power_on(self):
+ return self.earstudio_controller.power_on()
+
+ def previous_track(self):
+ return self.earstudio_controller.press_previous()
+
+ def reject_call(self):
+ return self.earstudio_controller.press_reject_call()
+
+ def volume_down(self):
+ return self.earstudio_controller.press_volume_down()
+
+ def volume_up(self):
+ return self.earstudio_controller.press_volume_up()
+
+
+class JaybirdX3EarbudsBluetoothHandsfreeAbstractDevice(
+ BluetoothHandsfreeAbstractDevice):
+ def __init__(self, jaybird_controller):
+ self.jaybird_controller = jaybird_controller
+
+ @property
+ def mac_address(self):
+ return self.jaybird_controller.mac_address
+
+ def accept_call(self):
+ return self.jaybird_controller.press_accept_call()
+
+ def end_call(self):
+ return self.jaybird_controller.press_reject_call()
+
+ def enter_pairing_mode(self):
+ return self.jaybird_controller.enter_pairing_mode()
+
+ def next_track(self):
+ return self.jaybird_controller.press_next()
+
+ def pause(self):
+ return self.jaybird_controller.press_play_pause()
+
+ def play(self):
+ return self.jaybird_controller.press_play_pause()
+
+ def power_off(self):
+ return self.jaybird_controller.power_off()
+
+ def power_on(self):
+ return self.jaybird_controller.power_on()
+
+ def previous_track(self):
+ return self.jaybird_controller.press_previous()
+
+ def reject_call(self):
+ return self.jaybird_controller.press_reject_call()
+
+ def volume_down(self):
+ return self.jaybird_controller.press_volume_down()
+
+ def volume_up(self):
+ return self.jaybird_controller.press_volume_up()
+
+
+class AndroidHeadsetBluetoothHandsfreeAbstractDevice(
+ BluetoothHandsfreeAbstractDevice):
+ def __init__(self, ad_controller):
+ self.ad_controller = ad_controller
+
+ @property
+ def mac_address(self):
+ """Getting device mac with more stability ensurance.
+
+ Sometime, getting mac address is flaky that it returns None. Adding a
+ loop to add more ensurance of getting correct mac address.
+ """
+ device_mac = None
+ start_time = time.time()
+ end_time = start_time + bt_default_timeout
+ while not device_mac and time.time() < end_time:
+ device_mac = self.ad_controller.droid.bluetoothGetLocalAddress()
+ asserts.assert_true(device_mac, 'Can not get the MAC address')
+ return device_mac
+
+ def accept_call(self):
+ return self.ad_controller.droid.telecomAcceptRingingCall(None)
+
+ def end_call(self):
+ return self.ad_controller.droid.telecomEndCall()
+
+ def enter_pairing_mode(self):
+ self.ad_controller.droid.bluetoothStartPairingHelper(True)
+ return self.ad_controller.droid.bluetoothMakeDiscoverable()
+
+ def next_track(self):
+ return (self.ad_controller.droid.bluetoothMediaPassthrough("skipNext"))
+
+ def pause(self):
+ return self.ad_controller.droid.bluetoothMediaPassthrough("pause")
+
+ def play(self):
+ return self.ad_controller.droid.bluetoothMediaPassthrough("play")
+
+ def power_off(self):
+ return self.ad_controller.droid.bluetoothToggleState(False)
+
+ def power_on(self):
+ return self.ad_controller.droid.bluetoothToggleState(True)
+
+ def previous_track(self):
+ return (self.ad_controller.droid.bluetoothMediaPassthrough("skipPrev"))
+
+ def reject_call(self):
+ return self.ad_controller.droid.telecomCallDisconnect(
+ self.ad_controller.droid.telecomCallGetCallIds()[0])
+
+ def reset(self):
+ return self.ad_controller.droid.bluetoothFactoryReset()
+
+ def volume_down(self):
+ target_step = self.ad_controller.droid.getMediaVolume() - 1
+ target_step = max(target_step, 0)
+ return self.ad_controller.droid.setMediaVolume(target_step)
+
+ def volume_up(self):
+ target_step = self.ad_controller.droid.getMediaVolume() + 1
+ max_step = self.ad_controller.droid.getMaxMediaVolume()
+ target_step = min(target_step, max_step)
+ return self.ad_controller.droid.setMediaVolume(target_step)
+
+
+class BluetoothHandsfreeAbstractDeviceFactory:
+ """Generates a BluetoothHandsfreeAbstractDevice for any device controller.
+ """
+
+ _controller_abstract_devices = {
+ 'EarstudioReceiver': EarstudioReceiverBluetoothHandsfreeAbstractDevice,
+ 'JaybirdX3Earbuds': JaybirdX3EarbudsBluetoothHandsfreeAbstractDevice,
+ 'ParentDevice': PixelBudsBluetoothHandsfreeAbstractDevice,
+ 'AndroidDevice': AndroidHeadsetBluetoothHandsfreeAbstractDevice
+ }
+
+ def generate(self, controller):
+ class_name = controller.__class__.__name__
+ if class_name in self._controller_abstract_devices:
+ return self._controller_abstract_devices[class_name](controller)
+ else:
+ validate_controller(controller, BluetoothHandsfreeAbstractDevice)
+ return controller
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_policy_utils.py b/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_policy_utils.py
new file mode 100644
index 0000000..ef89d95
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_policy_utils.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SAVED_NETWORKS = "saved_networks"
+CLIENT_STATE = "client_connections_state"
+CONNECTIONS_ENABLED = "ConnectionsEnabled"
+CONNECTIONS_DISABLED = "ConnectionsDisabled"
+
+
+def setup_policy_tests(fuchsia_devices):
+ """ Preserves networks already saved on devices before removing them to
+ setup up for a clean test environment. Records the state of client
+ connections before tests. Initializes the client controller
+ and enables connections.
+ Args:
+ fuchsia_devices: the devices under test
+ Returns:
+ A dict of the data to restore after tests indexed by device. The data
+ for each device is a dict of the saved data, ie saved networks and
+ state of client connections.
+ """
+ preserved_data_by_device = {}
+ for fd in fuchsia_devices:
+ data = {}
+ # Collect and delete networks saved on the device.
+ fd.wlan_policy_lib.wlanCreateClientController()
+ result_get = fd.wlan_policy_lib.wlanGetSavedNetworks()
+ if result_get.get("result") != None:
+ data[SAVED_NETWORKS] = result_get['result']
+ fd.wlan_policy_lib.wlanRemoveAllNetworks()
+
+ # Get the currect client connection state (connections enabled or disabled)
+ # and enable connections by default.
+ fd.wlan_policy_lib.wlanSetNewListener()
+ result_update = fd.wlan_policy_lib.wlanGetUpdate()
+ if result_update.get("result") != None and result_update.get(
+ "result").get("state") != None:
+ data[CLIENT_STATE] = result_update.get("result").get("state")
+ else:
+ fd.log.warn("Failed to get update; test will not start or "
+ "stop client connections at the end of the test.")
+ fd.wlan_policy_lib.wlanStartClientConnections()
+
+ preserved_data_by_device[fd] = data
+ return preserved_data_by_device
+
+
+def restore_state(fuchsia_devices, preserved_data):
+ """ Restore the state of the test device to what it was before tests began.
+ Remove any remaining saved networks, and restore the saved networks and
+ client connections state recorded by setup_policy_tests
+ Args:
+ fuchsia_devices: The fuchsia devices under test
+ preserved data: Dict of data indexed by fuchsia device, as returned
+ by setup_policy_tests
+ """
+ for fd in fuchsia_devices:
+ data = preserved_data[fd]
+ fd.wlan_policy_lib.wlanRemoveAllNetworks()
+ for network in data[SAVED_NETWORKS]:
+ save_network(fd, network["ssid"], network["security_type"],
+ network["credential_value"])
+ for starting_state in data[CLIENT_STATE]:
+ if starting_state == CONNECTIONS_ENABLED:
+ fd.wlan_policy_lib.wlanStartClientConnections()
+ elif starting_state == CONNECTIONS_DISABLED:
+ fd.wlan_policy_lib.wlanStopClientConnections()
+
+
+def save_network(fd, ssid, security_type, password=""):
+ """ Saves a network as specified on the given device and verify that the operation succeeded.
+ Returns true if there was no error, and false otherwise
+ Args:
+ fd: The Fuchsia device to save the network on
+ ssid: The SSID or name of the network to save.
+ security_type: The security type to save the network as, ie "none",
+ "wep", "wpa", "wpa2", or "wpa3"
+ password: The password to save for the network. Empty string represents
+ no password, and PSK should be provided as 64 character hex string.
+ """
+ result_save = fd.wlan_policy_lib.wlanSaveNetwork(ssid, security_type,
+ password)
+ if result_save.get("error") != None:
+ fd.log.info("Failed to save network %s with error: %s" %
+ (ssid, result_save["error"]))
+ return False
+ else:
+ return True
+
+
+def start_connections(fd):
+ """ Starts client connections on the specified device and verifies that it
+ succeeds, and raises a test failure if not.
+ Returns:
+ True if there are no errors, False if there are errors.
+ """
+ resultStart = fd.wlan_policy_lib.wlanStartClientConnections()
+ if resultStart.get("error") != None:
+ fd.log.error(
+ "Error occurred when starting client connections in test setup: %s"
+ % resultStart.get("error"))
+ return False
+ else:
+ return True
+
+
+def stop_connections(fd):
+ """ Stops client connections on the device and verify that there are no
+ errors are returned, and raises a test failure if there are.
+ Returns:
+ True if there are noe errors, False otherwise.
+ """
+ result_stop = fd.wlan_policy_lib.wlanStopClientConnections()
+ if result_stop.get("error") != None:
+ fd.log.error("Error occurred stopping client connections: %s" %
+ result_stop.get("error"))
+ return False
+ else:
+ return True
+
+
+def reboot_device(fd):
+ """ Reboot the device and reinitialize the device after.
+ Args:
+ fd: The device to reboot.
+ """
+ fd.reboot()
+ fd.wlan_policy_lib.wlanCreateClientController()
+ fd.wlan_policy_lib.wlanStartClientConnections()
+ fd.wlan_policy_lib.wlanSetNewListener()
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_utils.py b/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_utils.py
new file mode 100644
index 0000000..bebcb1e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/utils_lib/wlan_utils.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from acts import asserts
+from acts.controllers.ap_lib import hostapd_ap_preset
+
+
+def validate_setup_ap_and_associate(*args, **kwargs):
+ """Validates if setup_ap_and_associate was a success or not
+
+ Args: Args match setup_ap_and_associate
+ """
+ asserts.assert_true(setup_ap_and_associate(*args, **kwargs),
+ 'Failed to associate.')
+ asserts.explicit_pass('Successfully associated.')
+
+
+def setup_ap_and_associate(access_point,
+ client,
+ profile_name,
+ channel,
+ ssid,
+ mode=None,
+ preamble=None,
+ beacon_interval=None,
+ dtim_period=None,
+ frag_threshold=None,
+ rts_threshold=None,
+ force_wmm=None,
+ hidden=False,
+ security=None,
+ additional_ap_parameters=None,
+ password=None,
+ check_connectivity=False,
+ n_capabilities=None,
+ ac_capabilities=None,
+ vht_bandwidth=None,
+ setup_bridge=False,
+ target_security=None,
+ association_mechanism=None):
+ """Sets up the AP and associates a client.
+
+ Args:
+ access_point: An ACTS access_point controller
+ client: A WlanDevice.
+ profile_name: The profile name of one of the hostapd ap presets.
+ channel: What channel to set the AP to.
+ preamble: Whether to set short or long preamble (True or False)
+ beacon_interval: The beacon interval (int)
+ dtim_period: Length of dtim period (int)
+ frag_threshold: Fragmentation threshold (int)
+ rts_threshold: RTS threshold (int)
+ force_wmm: Enable WMM or not (True or False)
+ hidden: Advertise the SSID or not (True or False)
+ security: What security to enable.
+ additional_ap_parameters: Additional parameters to send the AP.
+ password: Password to connect to WLAN if necessary.
+ check_connectivity: Whether to check for internet connectivity.
+ target_security: The security to try to associate to if using policy
+ to associate.
+ association_mechanism: The way we will connect, through the core or
+ policy layer of WLAN on the device.
+ """
+ setup_ap(access_point, profile_name, channel, ssid, mode, preamble,
+ beacon_interval, dtim_period, frag_threshold, rts_threshold,
+ force_wmm, hidden, security, additional_ap_parameters, password,
+ check_connectivity, n_capabilities, ac_capabilities,
+ vht_bandwidth, setup_bridge)
+
+ if not security:
+ target_security = "none"
+
+ if security and security.wpa3:
+ return associate(client,
+ ssid,
+ password,
+ target_security=target_security,
+ key_mgmt='SAE',
+ check_connectivity=check_connectivity,
+ hidden=hidden,
+ association_mechanism=association_mechanism)
+ else:
+ return associate(client,
+ ssid,
+ password=password,
+ target_security=target_security,
+ check_connectivity=check_connectivity,
+ hidden=hidden,
+ association_mechanism=association_mechanism)
+
+
+def setup_ap(access_point,
+ profile_name,
+ channel,
+ ssid,
+ mode=None,
+ preamble=None,
+ beacon_interval=None,
+ dtim_period=None,
+ frag_threshold=None,
+ rts_threshold=None,
+ force_wmm=None,
+ hidden=False,
+ security=None,
+ additional_ap_parameters=None,
+ password=None,
+ check_connectivity=False,
+ n_capabilities=None,
+ ac_capabilities=None,
+ vht_bandwidth=None,
+ setup_bridge=False):
+ """Sets up the AP.
+
+ Args:
+ access_point: An ACTS access_point controller
+ profile_name: The profile name of one of the hostapd ap presets.
+ channel: What channel to set the AP to.
+ preamble: Whether to set short or long preamble (True or False)
+ beacon_interval: The beacon interval (int)
+ dtim_period: Length of dtim period (int)
+ frag_threshold: Fragmentation threshold (int)
+ rts_threshold: RTS threshold (int)
+ force_wmm: Enable WMM or not (True or False)
+ hidden: Advertise the SSID or not (True or False)
+ security: What security to enable.
+ additional_ap_parameters: Additional parameters to send the AP.
+ password: Password to connect to WLAN if necessary.
+ check_connectivity: Whether to check for internet connectivity.
+ """
+ ap = hostapd_ap_preset.create_ap_preset(profile_name=profile_name,
+ iface_wlan_2g=access_point.wlan_2g,
+ iface_wlan_5g=access_point.wlan_5g,
+ channel=channel,
+ ssid=ssid,
+ mode=mode,
+ short_preamble=preamble,
+ beacon_interval=beacon_interval,
+ dtim_period=dtim_period,
+ frag_threshold=frag_threshold,
+ rts_threshold=rts_threshold,
+ force_wmm=force_wmm,
+ hidden=hidden,
+ bss_settings=[],
+ security=security,
+ n_capabilities=n_capabilities,
+ ac_capabilities=ac_capabilities,
+ vht_bandwidth=vht_bandwidth)
+ access_point.start_ap(hostapd_config=ap,
+ setup_bridge=setup_bridge,
+ additional_parameters=additional_ap_parameters)
+
+
+def associate(client,
+ ssid,
+ password=None,
+ key_mgmt=None,
+ check_connectivity=True,
+ hidden=False,
+ security=None,
+ association_mechanism=None,
+ target_security=None):
+ """Associates a client to a WLAN network.
+
+ Args:
+ client: A WlanDevice
+ ssid: SSID of the ap we are looking for.
+ password: The password for the WLAN, if applicable.
+ key_mgmt: The hostapd wpa_key_mgmt value.
+ check_connectivity: Whether to check internet connectivity.
+ hidden: If the WLAN is hidden or not.
+ """
+ return client.associate(ssid,
+ password,
+ key_mgmt=key_mgmt,
+ check_connectivity=check_connectivity,
+ hidden=hidden,
+ association_mechanism=association_mechanism,
+ target_security=target_security)
+
+
+def status(client):
+ """Requests the state of WLAN network.
+
+ Args:
+ None
+ """
+ status = ''
+ status_response = client.status()
+
+ if status_response.get('error') is None:
+ # No error, so get the result
+ status = status_response['result']
+
+ logging.debug('status: %s' % status)
+ return status
+
+
+def disconnect(client):
+ """Disconnect client from its WLAN network.
+
+ Args:
+ client: A WlanDevice
+ """
+ client.disconnect()
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py
new file mode 100644
index 0000000..f1f1b72
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py
@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import inspect
+import logging
+
+import acts_contrib.test_utils.wifi.wifi_test_utils as awutils
+import acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils as fwutils
+from acts.utils import get_interface_ip_addresses
+from acts.utils import adb_shell_ping
+
+from acts import asserts
+from acts.controllers.fuchsia_device import FuchsiaDevice
+from acts.controllers.android_device import AndroidDevice
+
+
+def create_wlan_device(hardware_device):
+ """Creates a generic WLAN device based on type of device that is sent to
+ the functions.
+
+ Args:
+ hardware_device: A WLAN hardware device that is supported by ACTS.
+ """
+ if isinstance(hardware_device, FuchsiaDevice):
+ return FuchsiaWlanDevice(hardware_device)
+ elif isinstance(hardware_device, AndroidDevice):
+ return AndroidWlanDevice(hardware_device)
+ else:
+ raise ValueError('Unable to create WlanDevice for type %s' %
+ type(hardware_device))
+
+
+FUCHSIA_VALID_SECURITY_TYPES = {"none", "wep", "wpa", "wpa2", "wpa3"}
+
+
+class WlanDevice(object):
+ """Class representing a generic WLAN device.
+
+ Each object of this class represents a generic WLAN device.
+ Android device and Fuchsia devices are the currently supported devices/
+
+ Attributes:
+ device: A generic WLAN device.
+ """
+ def __init__(self, device):
+ self.device = device
+ self.log = logging
+ self.identifier = None
+
+ def wifi_toggle_state(self, state):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def reset_wifi(self):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def take_bug_report(self, test_name, begin_time):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def get_log(self, test_name, begin_time):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def associate(self,
+ target_ssid,
+ target_pwd=None,
+ check_connectivity=True,
+ hidden=False,
+ association_mechanism=None,
+ target_security=None):
+ """Base generic WLAN interface. Only called if not overriden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def disconnect(self, association_mechanism=None):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def get_wlan_interface_id_list(self):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def destroy_wlan_interface(self, iface_id):
+ """Base generic WLAN interface. Only called if not overridden by
+ another supported device.
+ """
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def send_command(self, command):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def get_interface_ip_addresses(self, interface):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def is_connected(self, ssid=None):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def can_ping(self,
+ dest_ip,
+ count=3,
+ interval=1000,
+ timeout=1000,
+ size=25,
+ additional_ping_params=None):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def ping(self,
+ dest_ip,
+ count=3,
+ interval=1000,
+ timeout=1000,
+ size=25,
+ additional_ping_params=None):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def hard_power_cycle(self, pdus=None):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def save_network(self, ssid):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+ def clear_saved_networks(self):
+ raise NotImplementedError("{} must be defined.".format(
+ inspect.currentframe().f_code.co_name))
+
+
+class AndroidWlanDevice(WlanDevice):
+ """Class wrapper for an Android WLAN device.
+
+ Each object of this class represents a generic WLAN device.
+ Android device and Fuchsia devices are the currently supported devices/
+
+ Attributes:
+ android_device: An Android WLAN device.
+ """
+ def __init__(self, android_device):
+ super().__init__(android_device)
+ self.identifier = android_device.serial
+
+ def wifi_toggle_state(self, state):
+ awutils.wifi_toggle_state(self.device, state)
+
+ def reset_wifi(self):
+ awutils.reset_wifi(self.device)
+
+ def take_bug_report(self, test_name, begin_time):
+ self.device.take_bug_report(test_name, begin_time)
+
+ def get_log(self, test_name, begin_time):
+ self.device.cat_adb_log(test_name, begin_time)
+
+ def turn_location_off_and_scan_toggle_off(self):
+ awutils.turn_location_off_and_scan_toggle_off(self.device)
+
+ def associate(self,
+ target_ssid,
+ target_pwd=None,
+ key_mgmt=None,
+ check_connectivity=True,
+ hidden=False,
+ association_mechanism=None,
+ target_security=None):
+ """Function to associate an Android WLAN device.
+
+ Args:
+ target_ssid: SSID to associate to.
+ target_pwd: Password for the SSID, if necessary.
+ key_mgmt: The hostapd wpa_key_mgmt value, distinguishes wpa3 from
+ wpa2 for android tests.
+ check_connectivity: Whether to check for internet connectivity.
+ hidden: Whether the network is hidden.
+ Returns:
+ True if successfully connected to WLAN, False if not.
+ """
+ network = {'SSID': target_ssid, 'hiddenSSID': hidden}
+ if target_pwd:
+ network['password'] = target_pwd
+ if key_mgmt:
+ network['security'] = key_mgmt
+ try:
+ awutils.connect_to_wifi_network(
+ self.device,
+ network,
+ check_connectivity=check_connectivity,
+ hidden=hidden)
+ return True
+ except Exception as e:
+ self.device.log.info('Failed to associated (%s)' % e)
+ return False
+
+ def disconnect(self, association_mechanism=None):
+ awutils.turn_location_off_and_scan_toggle_off(self.device)
+
+ def get_wlan_interface_id_list(self):
+ pass
+
+ def destroy_wlan_interface(self, iface_id):
+ pass
+
+ def send_command(self, command):
+ return self.device.adb.shell(str(command))
+
+ def get_interface_ip_addresses(self, interface):
+ return get_interface_ip_addresses(self.device, interface)
+
+ def is_connected(self, ssid=None):
+ wifi_info = self.device.droid.wifiGetConnectionInfo()
+ if ssid:
+ return 'BSSID' in wifi_info and wifi_info['SSID'] == ssid
+ return 'BSSID' in wifi_info
+
+ def can_ping(self,
+ dest_ip,
+ count=3,
+ interval=1000,
+ timeout=1000,
+ size=25,
+ additional_ping_params=None):
+ return adb_shell_ping(self.device,
+ dest_ip=dest_ip,
+ count=count,
+ timeout=timeout)
+
+ def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25):
+ pass
+
+ def hard_power_cycle(self, pdus):
+ pass
+
+ def save_network(self, ssid):
+ pass
+
+ def clear_saved_networks(self):
+ pass
+
+
+class FuchsiaWlanDevice(WlanDevice):
+ """Class wrapper for an Fuchsia WLAN device.
+
+ Each object of this class represents a generic WLAN device.
+ Android device and Fuchsia devices are the currently supported devices/
+
+ Attributes:
+ fuchsia_device: A Fuchsia WLAN device.
+ """
+ def __init__(self, fuchsia_device):
+ super().__init__(fuchsia_device)
+ self.identifier = fuchsia_device.ip
+
+ def wifi_toggle_state(self, state):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def reset_wifi(self):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def take_bug_report(self, test_name, begin_time):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def get_log(self, test_name, begin_time):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """Stub for Fuchsia implementation."""
+ pass
+
+ def associate(self,
+ target_ssid,
+ target_pwd=None,
+ key_mgmt=None,
+ check_connectivity=True,
+ hidden=False,
+ association_mechanism=None,
+ target_security=None):
+ """Function to associate a Fuchsia WLAN device.
+
+ Args:
+ target_ssid: SSID to associate to.
+ target_pwd: Password for the SSID, if necessary.
+ key_mgmt: the hostapd wpa_key_mgmt, if specified.
+ check_connectivity: Whether to check for internet connectivity.
+ hidden: Whether the network is hidden.
+ Returns:
+ True if successfully connected to WLAN, False if not.
+ """
+ if association_mechanism == 'policy':
+ return self.device.policy_save_and_connect(target_ssid,
+ target_security,
+ password=target_pwd)
+ elif not association_mechanism or association_mechanism == 'drivers':
+ connection_response = self.device.wlan_lib.wlanConnectToNetwork(
+ target_ssid, target_pwd=target_pwd)
+ return self.device.check_connect_response(connection_response)
+ else:
+ self.log.error(
+ "Association mechanism %s is not recognized. Acceptable values are 'drivers' and 'policy'"
+ % association_mechanism)
+ return False
+
+ def disconnect(self, association_mechanism=None):
+ """Function to disconnect from a Fuchsia WLAN device.
+ Asserts if disconnect was not successful.
+ """
+ if association_mechanism == 'policy':
+ asserts.assert_true(self.device.remove_all_and_disconnect(),
+ 'Failed to disconnect')
+ elif not association_mechanism or association_mechanism == 'drivers':
+ disconnect_response = self.device.wlan_lib.wlanDisconnect()
+ asserts.assert_true(
+ self.device.check_disconnect_response(disconnect_response),
+ 'Failed to disconnect.')
+ else:
+ self.log.error(
+ "Association mechanism %s is not recognized. Acceptable values are 'drivers' and 'policy'"
+ % association_mechanism)
+ raise ValueError(
+ 'Invalid association_mechanism "%s". Valid options are "policy" or "drivers".'
+ % association_mechanism)
+
+ def status(self):
+ return self.device.wlan_lib.wlanStatus()
+
+ def can_ping(self,
+ dest_ip,
+ count=3,
+ interval=1000,
+ timeout=1000,
+ size=25,
+ additional_ping_params=None):
+ return self.device.can_ping(
+ dest_ip,
+ count=count,
+ interval=interval,
+ timeout=timeout,
+ size=size,
+ additional_ping_params=additional_ping_params)
+
+ def ping(self,
+ dest_ip,
+ count=3,
+ interval=1000,
+ timeout=1000,
+ size=25,
+ additional_ping_params=None):
+ return self.device.ping(dest_ip,
+ count=count,
+ interval=interval,
+ timeout=timeout,
+ size=size,
+ additional_ping_params=additional_ping_params)
+
+ def get_wlan_interface_id_list(self):
+ """Function to list available WLAN interfaces.
+
+ Returns:
+ A list of wlan interface IDs.
+ """
+ return self.device.wlan_lib.wlanGetIfaceIdList().get('result')
+
+ def destroy_wlan_interface(self, iface_id):
+ """Function to associate a Fuchsia WLAN device.
+
+ Args:
+ target_ssid: SSID to associate to.
+ target_pwd: Password for the SSID, if necessary.
+ check_connectivity: Whether to check for internet connectivity.
+ hidden: Whether the network is hidden.
+ Returns:
+ True if successfully destroyed wlan interface, False if not.
+ """
+ result = self.device.wlan_lib.wlanDestroyIface(iface_id)
+ if result.get('error') is None:
+ return True
+ else:
+ self.log.error("Failed to destroy interface with: {}".format(
+ result.get('error')))
+ return False
+
+ def send_command(self, command):
+ return self.device.send_command_ssh(str(command)).stdout
+
+ def get_interface_ip_addresses(self, interface):
+ return get_interface_ip_addresses(self.device, interface)
+
+ def is_connected(self, ssid=None):
+ """ Determines if wlan_device is connected to wlan network.
+
+ Args:
+ ssid (optional): string, to check if device is connect to a specific
+ network.
+
+ Returns:
+ True, if connected to a network or to the correct network when SSID
+ is provided.
+ False, if not connected or connect to incorrect network when SSID is
+ provided.
+ """
+ response = self.status()
+ if response.get('error'):
+ raise ConnectionError(
+ 'Failed to get client network connection status')
+
+ status = response.get('result')
+ if status and status.get('connected_to'):
+ if ssid:
+ connected_ssid = ''.join(
+ chr(i) for i in status['connected_to']['ssid'])
+ if ssid != connected_ssid:
+ return False
+ return True
+ return False
+
+ def hard_power_cycle(self, pdus):
+ self.device.reboot(reboot_type='hard', testbed_pdus=pdus)
+
+ def save_network(self, target_ssid, security_type=None, target_pwd=None):
+ if security_type and security_type not in FUCHSIA_VALID_SECURITY_TYPES:
+ raise TypeError('Invalid security type: %s' % security_type)
+ response = self.device.wlan_policy_lib.wlanSaveNetwork(
+ target_ssid, security_type, target_pwd=target_pwd)
+ if response.get('error'):
+ raise EnvironmentError('Failed to save network %s. Err: %s' %
+ (target_ssid, response.get('error')))
+
+ def clear_saved_networks(self):
+ response = self.device.wlan_policy_lib.wlanRemoveAllNetworks()
+ if response.get('error'):
+ raise EnvironmentError('Failed to clear saved networks: %s' %
+ response.get('error'))
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
new file mode 100644
index 0000000..f8b33dc
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class AbstractDeviceWlanDeviceBaseTest(WifiBaseTest):
+ def setup_class(self):
+ super().setup_class()
+
+ def on_fail(self, test_name, begin_time):
+ try:
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.get_log(test_name, begin_time)
+ except Exception:
+ pass
+
+ try:
+ if self.dut.device.hard_reboot_on_fail:
+ self.dut.hard_power_cycle(self.pdu_devices)
+ except AttributeError:
+ pass
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/wmm_transceiver.py b/acts_tests/acts_contrib/test_utils/abstract_devices/wmm_transceiver.py
new file mode 100644
index 0000000..f1aad98
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/wmm_transceiver.py
@@ -0,0 +1,665 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import multiprocessing
+import time
+
+from datetime import datetime
+from uuid import uuid4
+
+from acts import signals
+from acts import tracelogger
+from acts import utils
+from acts.controllers import iperf_client
+from acts.controllers import iperf_server
+
+AC_VO = 'AC_VO'
+AC_VI = 'AC_VI'
+AC_BE = 'AC_BE'
+AC_BK = 'AC_BK'
+
+# TODO(fxb/61421): Add tests to check all DSCP classes are mapped to the correct
+# AC (there are many that aren't included here). Requires implementation of
+# sniffer.
+DEFAULT_AC_TO_TOS_TAG_MAP = {
+ AC_VO: '0xC0',
+ AC_VI: '0x80',
+ AC_BE: '0x0',
+ AC_BK: '0x20'
+}
+UDP = 'udp'
+TCP = 'tcp'
+DEFAULT_IPERF_PORT = 5201
+DEFAULT_STREAM_TIME = 10
+DEFAULT_IP_ADDR_TIMEOUT = 15
+PROCESS_JOIN_TIMEOUT = 60
+AVAILABLE = True
+UNAVAILABLE = False
+
+
+class WmmTransceiverError(signals.ControllerError):
+ pass
+
+
+def create(config, identifier=None, wlan_devices=None, access_points=None):
+ """Creates a WmmTransceiver from a config.
+
+ Args:
+ config: dict, config parameters for the transceiver. Contains:
+ - iperf_config: dict, the config to use for creating IPerfClients
+ and IPerfServers (excluding port).
+ - port_range_start: int, the lower bound of the port range to use
+ for creating IPerfServers. Defaults to 5201.
+ - wlan_device: string, the identifier of the wlan_device used for
+ this WmmTransceiver (optional)
+
+ identifier: string, identifier for the WmmTransceiver. Must be provided
+ either as arg or in the config.
+ wlan_devices: list of WlanDevice objects from which to get the
+ wlan_device, if any, used as this transceiver
+ access_points: list of AccessPoint objects from which to get the
+ access_point, if any, used as this transceiver
+ """
+ try:
+ # If identifier is not provided as func arg, it must be provided via
+ # config file.
+ if not identifier:
+ identifier = config['identifier']
+ iperf_config = config['iperf_config']
+
+ except KeyError as err:
+ raise WmmTransceiverError(
+ 'Parameter not provided as func arg, nor found in config: %s' %
+ err)
+
+ if wlan_devices is None:
+ wlan_devices = []
+
+ if access_points is None:
+ access_points = []
+
+ port_range_start = config.get('port_range_start', DEFAULT_IPERF_PORT)
+
+ wd = None
+ ap = None
+ if 'wlan_device' in config:
+ wd = _find_wlan_device(config['wlan_device'], wlan_devices)
+ elif 'access_point' in config:
+ ap = _find_access_point(config['access_point'], access_points)
+
+ return WmmTransceiver(iperf_config,
+ identifier,
+ wlan_device=wd,
+ access_point=ap,
+ port_range_start=port_range_start)
+
+
+def _find_wlan_device(wlan_device_identifier, wlan_devices):
+ """Returns WlanDevice based on string identifier (e.g. ip, serial, etc.)
+
+ Args:
+ wlan_device_identifier: string, identifier for the desired WlanDevice
+ wlan_devices: list, WlanDevices to search through
+
+ Returns:
+ WlanDevice, with identifier matching wlan_device_identifier
+
+ Raises:
+ WmmTransceiverError, if no WlanDevice matches identifier
+ """
+ for wd in wlan_devices:
+ if wlan_device_identifier == wd.identifier:
+ return wd
+ raise WmmTransceiverError('No WlanDevice with identifier: %s' %
+ wlan_device_identifier)
+
+
+def _find_access_point(access_point_ip, access_points):
+ """Returns AccessPoint based on string ip address
+
+ Args:
+ access_point_ip: string, control plane ip addr of the desired AP,
+ access_points: list, AccessPoints to search through
+
+ Returns:
+ AccessPoint, with hostname matching access_point_ip
+
+ Raises:
+ WmmTransceiverError, if no AccessPoint matches ip"""
+ for ap in access_points:
+ if ap.ssh_settings.hostname == access_point_ip:
+ return ap
+ raise WmmTransceiverError('No AccessPoint with ip: %s' % access_point_ip)
+
+
+class WmmTransceiver(object):
+ """Object for handling WMM tagged streams between devices"""
+ def __init__(self,
+ iperf_config,
+ identifier,
+ wlan_device=None,
+ access_point=None,
+ port_range_start=5201):
+
+ self.identifier = identifier
+ self.log = tracelogger.TraceLogger(
+ WmmTransceiverLoggerAdapter(logging.getLogger(),
+ {'identifier': self.identifier}))
+ # WlanDevice or AccessPoint, that is used as the transceiver. Only one
+ # will be set. This helps consolodate association, setup, teardown, etc.
+ self.wlan_device = wlan_device
+ self.access_point = access_point
+
+ # Parameters used to create IPerfClient and IPerfServer objects on
+ # device
+ self._iperf_config = iperf_config
+ self._test_interface = self._iperf_config.get('test_interface')
+ self._port_range_start = port_range_start
+ self._next_server_port = port_range_start
+
+ # Maps IPerfClients, used for streams from this device, to True if
+ # available, False if reserved
+ self._iperf_clients = {}
+
+ # Maps IPerfServers, used to receive streams from other devices, to True
+ # if available, False if reserved
+ self._iperf_servers = {}
+
+ # Maps ports of servers, which are provided to other transceivers, to
+ # the actual IPerfServer objects
+ self._iperf_server_ports = {}
+
+ # Maps stream UUIDs to IPerfClients reserved for that streams use
+ self._reserved_clients = {}
+
+ # Maps stream UUIDs to (WmmTransceiver, IPerfServer) tuples, where the
+ # server is reserved on the transceiver for that streams use
+ self._reserved_servers = {}
+
+ # Maps with shared memory functionality to be used across the parallel
+ # streams. active_streams holds UUIDs of streams that are currently
+ # running on this device (mapped to True, since there is no
+ # multiprocessing set). stream_results maps UUIDs of streams completed
+ # on this device to IPerfResult results for that stream.
+ self._manager = multiprocessing.Manager()
+ self._active_streams = self._manager.dict()
+ self._stream_results = self._manager.dict()
+
+ # Holds parameters for streams that are prepared to run asynchronously
+ # (i.e. resources have been allocated). Maps UUIDs of the future streams
+ # to a dict, containing the stream parameters.
+ self._pending_async_streams = {}
+
+ # Set of UUIDs of asynchronous streams that have at least started, but
+ # have not had their resources reclaimed yet
+ self._ran_async_streams = set()
+
+ # Set of stream parallel process, which can be joined if completed
+ # successfully, or terminated and joined in the event of an error
+ self._running_processes = set()
+
+ def run_synchronous_traffic_stream(self, stream_parameters, subnet):
+ """Runs a traffic stream with IPerf3 between two WmmTransceivers and
+ saves the results.
+
+ Args:
+ stream_parameters: dict, containing parameters to used for the
+ stream. See _parse_stream_parameters for details.
+ subnet: string, the subnet of the network to use for the stream
+
+ Returns:
+ uuid: UUID object, identifier of the stream
+ """
+ (receiver, access_category, bandwidth,
+ stream_time) = self._parse_stream_parameters(stream_parameters)
+ uuid = uuid4()
+
+ (client, server_ip,
+ server_port) = self._get_stream_resources(uuid, receiver, subnet)
+
+ self._validate_server_address(server_ip, uuid)
+
+ self.log.info('Running synchronous stream to %s WmmTransceiver' %
+ receiver.identifier)
+ self._run_traffic(uuid,
+ client,
+ server_ip,
+ server_port,
+ self._active_streams,
+ self._stream_results,
+ access_category=access_category,
+ bandwidth=bandwidth,
+ stream_time=stream_time)
+
+ self._return_stream_resources(uuid)
+ return uuid
+
+ def prepare_asynchronous_stream(self, stream_parameters, subnet):
+ """Reserves resources and saves configs for upcoming asynchronous
+ traffic streams, so they can be started more simultaneously.
+
+ Args:
+ stream_parameters: dict, containing parameters to used for the
+ stream. See _parse_stream_parameters for details.
+ subnet: string, the subnet of the network to use for the stream
+
+ Returns:
+ uuid: UUID object, identifier of the stream
+ """
+ (receiver, access_category, bandwidth,
+ time) = self._parse_stream_parameters(stream_parameters)
+ uuid = uuid4()
+
+ (client, server_ip,
+ server_port) = self._get_stream_resources(uuid, receiver, subnet)
+
+ self._validate_server_address(server_ip, uuid)
+
+ pending_stream_config = {
+ 'client': client,
+ 'server_ip': server_ip,
+ 'server_port': server_port,
+ 'access_category': access_category,
+ 'bandwidth': bandwidth,
+ 'time': time
+ }
+
+ self._pending_async_streams[uuid] = pending_stream_config
+ self.log.info('Stream to %s WmmTransceiver prepared.' %
+ receiver.identifier)
+ return uuid
+
+ def start_asynchronous_streams(self, start_time=None):
+ """Starts pending asynchronous streams between two WmmTransceivers as
+ parallel processes.
+
+ Args:
+ start_time: float, time, seconds since epoch, at which to start the
+ stream (for better synchronicity). If None, start immediately.
+ """
+ for uuid in self._pending_async_streams:
+ pending_stream_config = self._pending_async_streams[uuid]
+ client = pending_stream_config['client']
+ server_ip = pending_stream_config['server_ip']
+ server_port = pending_stream_config['server_port']
+ access_category = pending_stream_config['access_category']
+ bandwidth = pending_stream_config['bandwidth']
+ time = pending_stream_config['time']
+
+ process = multiprocessing.Process(target=self._run_traffic,
+ args=[
+ uuid, client, server_ip,
+ server_port,
+ self._active_streams,
+ self._stream_results
+ ],
+ kwargs={
+ 'access_category':
+ access_category,
+ 'bandwidth': bandwidth,
+ 'stream_time': time,
+ 'start_time': start_time
+ })
+
+ # This needs to be set here to ensure its marked active before
+ # it even starts.
+ self._active_streams[uuid] = True
+ process.start()
+ self._ran_async_streams.add(uuid)
+ self._running_processes.add(process)
+
+ self._pending_async_streams.clear()
+
+ def cleanup_asynchronous_streams(self, timeout=PROCESS_JOIN_TIMEOUT):
+ """Releases reservations on resources (IPerfClients and IPerfServers)
+ that were held for asynchronous streams, both pending and finished.
+ Attempts to join any running processes, logging an error if timeout is
+ exceeded.
+
+ Args:
+ timeout: time, in seconds, to wait for each running process, if any,
+ to join
+ """
+ self.log.info('Cleaning up any asynchronous streams.')
+
+ # Releases resources for any streams that were prepared, but no run
+ for uuid in self._pending_async_streams:
+ self.log.error(
+ 'Pending asynchronous stream %s never ran. Cleaning.' % uuid)
+ self._return_stream_resources(uuid)
+ self._pending_async_streams.clear()
+
+ # Attempts to join any running streams, terminating them after timeout
+ # if necessary.
+ while self._running_processes:
+ process = self._running_processes.pop()
+ process.join(timeout)
+ if process.is_alive():
+ self.log.error(
+ 'Stream process failed to join in %s seconds. Terminating.'
+ % timeout)
+ process.terminate()
+ process.join()
+ self._active_streams.clear()
+
+ # Release resources for any finished streams
+ while self._ran_async_streams:
+ uuid = self._ran_async_streams.pop()
+ self._return_stream_resources(uuid)
+
+ def get_results(self, uuid):
+ """Retrieves a streams IPerfResults from stream_results
+
+ Args:
+ uuid: UUID object, identifier of the stream
+ """
+ return self._stream_results.get(uuid, None)
+
+ def destroy_resources(self):
+ for server in self._iperf_servers:
+ server.stop()
+ self._iperf_servers.clear()
+ self._iperf_server_ports.clear()
+ self._iperf_clients.clear()
+ self._next_server_port = self._port_range_start
+ self._stream_results.clear()
+
+ @property
+ def has_active_streams(self):
+ return bool(self._active_streams)
+
+ # Helper Functions
+
+ def _run_traffic(self,
+ uuid,
+ client,
+ server_ip,
+ server_port,
+ active_streams,
+ stream_results,
+ access_category=None,
+ bandwidth=None,
+ stream_time=DEFAULT_STREAM_TIME,
+ start_time=None):
+ """Runs an iperf3 stream.
+
+ 1. Adds stream UUID to active_streams
+ 2. Runs stream
+ 3. Saves results to stream_results
+ 4. Removes stream UUID from active_streams
+
+ Args:
+ uuid: UUID object, identifier for stream
+ client: IPerfClient object on device
+ server_ip: string, ip address of IPerfServer for stream
+ server_port: int, port of the IPerfServer for stream
+ active_streams: multiprocessing.Manager.dict, which holds stream
+ UUIDs of active streams on the device
+ stream_results: multiprocessing.Manager.dict, which maps stream
+ UUIDs of streams to IPerfResult objects
+ access_category: string, WMM access category to use with iperf
+ (AC_BK, AC_BE, AC_VI, AC_VO). Unset if None.
+ bandwidth: int, bandwidth in mbps to use with iperf. Implies UDP.
+ Unlimited if None.
+ stream_time: int, time in seconds, to run iperf stream
+ start_time: float, time, seconds since epoch, at which to start the
+ stream (for better synchronicity). If None, start immediately.
+ """
+ active_streams[uuid] = True
+ # SSH sessions must be started within the process that is going to
+ # use it.
+ if type(client) == iperf_client.IPerfClientOverSsh:
+ with utils.SuppressLogOutput():
+ client.start_ssh()
+
+ ac_flag = ''
+ bandwidth_flag = ''
+ time_flag = '-t %s' % stream_time
+
+ if access_category:
+ ac_flag = ' -S %s' % DEFAULT_AC_TO_TOS_TAG_MAP[access_category]
+
+ if bandwidth:
+ bandwidth_flag = ' -u -b %sM' % bandwidth
+
+ iperf_flags = '-p %s -i 1 %s%s%s -J' % (server_port, time_flag,
+ ac_flag, bandwidth_flag)
+ if not start_time:
+ start_time = time.time()
+ time_str = datetime.fromtimestamp(start_time).strftime('%H:%M:%S.%f')
+ self.log.info(
+ 'At %s, starting %s second stream to %s:%s with (AC: %s, Bandwidth: %s)'
+ % (time_str, stream_time, server_ip, server_port, access_category,
+ bandwidth if bandwidth else 'Unlimited'))
+
+ # If present, wait for stream start time
+ if start_time:
+ current_time = time.time()
+ while current_time < start_time:
+ current_time = time.time()
+ path = client.start(server_ip, iperf_flags, '%s' % uuid)
+ stream_results[uuid] = iperf_server.IPerfResult(
+ path, reporting_speed_units='mbps')
+
+ if type(client) == iperf_client.IPerfClientOverSsh:
+ client.close_ssh()
+ active_streams.pop(uuid)
+
+ def _get_stream_resources(self, uuid, receiver, subnet):
+ """Reserves an IPerfClient and IPerfServer for a stream.
+
+ Args:
+ uuid: UUID object, identifier of the stream
+ receiver: WmmTransceiver object, which will be the streams receiver
+ subnet: string, subnet of test network, to retrieve the appropriate
+ server address
+
+ Returns:
+ (IPerfClient, string, int) representing the client, server address,
+ and server port to use for the stream
+ """
+ client = self._get_client(uuid)
+ server_ip, server_port = self._get_server(receiver, uuid, subnet)
+ return (client, server_ip, server_port)
+
+ def _return_stream_resources(self, uuid):
+ """Releases reservations on a streams IPerfClient and IPerfServer, so
+ they can be used by a future stream.
+
+ Args:
+ uuid: UUID object, identifier of the stream
+ """
+ if uuid in self._active_streams:
+ raise EnvironmentError('Resource still being used by stream %s' %
+ uuid)
+ (receiver, server_port) = self._reserved_servers.pop(uuid)
+ receiver._release_server(server_port)
+ client = self._reserved_clients.pop(uuid)
+ self._iperf_clients[client] = AVAILABLE
+
+ def _get_client(self, uuid):
+ """Retrieves and reserves IPerfClient for use in a stream. If none are
+ available, a new one is created.
+
+ Args:
+ uuid: UUID object, identifier for stream, used to link client to
+ stream for teardown
+
+ Returns:
+ IPerfClient on device
+ """
+ reserved_client = None
+ for client in self._iperf_clients:
+ if self._iperf_clients[client] == AVAILABLE:
+ reserved_client = client
+ break
+ else:
+ reserved_client = iperf_client.create([self._iperf_config])[0]
+ # Due to the nature of multiprocessing, ssh connections must
+ # be started inside the parallel processes, so it must be closed
+ # here.
+ if type(reserved_client) == iperf_client.IPerfClientOverSsh:
+ reserved_client.close_ssh()
+
+ self._iperf_clients[reserved_client] = UNAVAILABLE
+ self._reserved_clients[uuid] = reserved_client
+ return reserved_client
+
+ def _get_server(self, receiver, uuid, subnet):
+ """Retrieves the address and port of a reserved IPerfServer object from
+ the receiver object for use in a stream.
+
+ Args:
+ receiver: WmmTransceiver, to get an IPerfServer from
+ uuid: UUID, identifier for stream, used to link server to stream
+ for teardown
+ subnet: string, subnet of test network, to retrieve the appropriate
+ server address
+
+ Returns:
+ (string, int) representing the IPerfServer address and port
+ """
+ (server_ip, server_port) = receiver._reserve_server(subnet)
+ self._reserved_servers[uuid] = (receiver, server_port)
+ return (server_ip, server_port)
+
+ def _reserve_server(self, subnet):
+ """Reserves an available IPerfServer for use in a stream from another
+ WmmTransceiver. If none are available, a new one is created.
+
+ Args:
+ subnet: string, subnet of test network, to retrieve the appropriate
+ server address
+
+ Returns:
+ (string, int) representing the IPerfServer address and port
+ """
+ reserved_server = None
+ for server in self._iperf_servers:
+ if self._iperf_servers[server] == AVAILABLE:
+ reserved_server = server
+ break
+ else:
+ iperf_server_config = self._iperf_config
+ iperf_server_config.update({'port': self._next_server_port})
+ self._next_server_port += 1
+ reserved_server = iperf_server.create([iperf_server_config])[0]
+ self._iperf_server_ports[reserved_server.port] = reserved_server
+
+ self._iperf_servers[reserved_server] = UNAVAILABLE
+ reserved_server.start()
+ end_time = time.time() + DEFAULT_IP_ADDR_TIMEOUT
+ while time.time() < end_time:
+ if self.wlan_device:
+ addresses = utils.get_interface_ip_addresses(
+ self.wlan_device.device, self._test_interface)
+ else:
+ addresses = reserved_server.get_interface_ip_addresses(
+ self._test_interface)
+ for addr in addresses['ipv4_private']:
+ if utils.ip_in_subnet(addr, subnet):
+ return (addr, reserved_server.port)
+ raise AttributeError(
+ 'Reserved server has no ipv4 address in the %s subnet' % subnet)
+
+ def _release_server(self, server_port):
+ """Releases reservation on IPerfServer, which was held for a stream
+ from another WmmTransceiver.
+
+ Args:
+ server_port: int, the port of the IPerfServer being returned (since)
+ it is the identifying characteristic
+ """
+ server = self._iperf_server_ports[server_port]
+ server.stop()
+ self._iperf_servers[server] = AVAILABLE
+
+ def _validate_server_address(self, server_ip, uuid, timeout=60):
+ """ Verifies server address can be pinged before attempting to run
+ traffic, since iperf is unforgiving when the server is unreachable.
+
+ Args:
+ server_ip: string, ip address of the iperf server
+ uuid: string, uuid of the stream to use this server
+ timeout: int, time in seconds to wait for server to respond to pings
+
+ Raises:
+ WmmTransceiverError, if, after timeout, server ip is unreachable.
+ """
+ self.log.info('Verifying server address (%s) is reachable.' %
+ server_ip)
+ end_time = time.time() + timeout
+ while time.time() < end_time:
+ if self.can_ping(server_ip):
+ break
+ else:
+ self.log.debug(
+ 'Could not ping server address (%s). Retrying in 1 second.'
+ % (server_ip))
+ time.sleep(1)
+ else:
+ self._return_stream_resources(uuid)
+ raise WmmTransceiverError('IPerfServer address (%s) unreachable.' %
+ server_ip)
+
+ def can_ping(self, dest_ip):
+ """ Utilizes can_ping function in wlan_device or access_point device to
+ ping dest_ip
+
+ Args:
+ dest_ip: string, ip address to ping
+
+ Returns:
+ True, if dest address is reachable
+ False, otherwise
+ """
+ if self.wlan_device:
+ return self.wlan_device.can_ping(dest_ip)
+ else:
+ return self.access_point.can_ping(dest_ip)
+
+ def _parse_stream_parameters(self, stream_parameters):
+ """Parses stream_parameters from dictionary.
+
+ Args:
+ stream_parameters: dict of stream parameters
+ 'receiver': WmmTransceiver, the receiver for the stream
+ 'access_category': String, the access category to use for the
+ stream. Unset if None.
+ 'bandwidth': int, bandwidth in mbps for the stream. If set,
+ implies UDP. If unset, implies TCP and unlimited bandwidth.
+ 'time': int, time in seconds to run stream.
+
+ Returns:
+ (receiver, access_category, bandwidth, time) as
+ (WmmTransceiver, String, int, int)
+ """
+ receiver = stream_parameters['receiver']
+ access_category = stream_parameters.get('access_category', None)
+ bandwidth = stream_parameters.get('bandwidth', None)
+ time = stream_parameters.get('time', DEFAULT_STREAM_TIME)
+ return (receiver, access_category, bandwidth, time)
+
+
+class WmmTransceiverLoggerAdapter(logging.LoggerAdapter):
+ def process(self, msg, kwargs):
+ if self.extra['identifier']:
+ log_identifier = ' | %s' % self.extra['identifier']
+ else:
+ log_identifier = ''
+ msg = "[WmmTransceiver%s] %s" % (log_identifier, msg)
+ return (msg, kwargs)
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/__init__.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py
new file mode 100644
index 0000000..a5b9fe1
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py
@@ -0,0 +1,667 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""This module provides utilities to do audio data analysis."""
+
+import logging
+import numpy
+import soundfile
+from scipy.signal import blackmanharris
+from scipy.signal import iirnotch
+from scipy.signal import lfilter
+
+# The default block size of pattern matching.
+ANOMALY_DETECTION_BLOCK_SIZE = 120
+
+# Only peaks with coefficient greater than 0.01 of the first peak should be
+# considered. Note that this correspond to -40dB in the spectrum.
+DEFAULT_MIN_PEAK_RATIO = 0.01
+
+# The minimum RMS value of meaningful audio data.
+MEANINGFUL_RMS_THRESHOLD = 0.001
+
+# The minimal signal norm value.
+_MINIMUM_SIGNAL_NORM = 0.001
+
+# The default pattern mathing threshold. By experiment, this threshold
+# can tolerate normal noise of 0.3 amplitude when sine wave signal
+# amplitude is 1.
+PATTERN_MATCHING_THRESHOLD = 0.85
+
+# The default number of samples within the analysis step size that the
+# difference between two anomaly time values can be to be grouped together.
+ANOMALY_GROUPING_TOLERANCE = 1.0
+
+# Window size for peak detection.
+PEAK_WINDOW_SIZE_HZ = 20
+
+
+class RMSTooSmallError(Exception):
+ """Error when signal RMS is too small."""
+ pass
+
+
+class EmptyDataError(Exception):
+ """Error when signal is empty."""
+ pass
+
+
+def normalize_signal(signal, saturate_value):
+ """Normalizes the signal with respect to the saturate value.
+
+ Args:
+ signal: A list for one-channel PCM data.
+ saturate_value: The maximum value that the PCM data might be.
+
+ Returns:
+ A numpy array containing normalized signal. The normalized signal has
+ value -1 and 1 when it saturates.
+
+ """
+ signal = numpy.array(signal)
+ return signal / float(saturate_value)
+
+
+def spectral_analysis(signal,
+ rate,
+ min_peak_ratio=DEFAULT_MIN_PEAK_RATIO,
+ peak_window_size_hz=PEAK_WINDOW_SIZE_HZ):
+ """Gets the dominant frequencies by spectral analysis.
+
+ Args:
+ signal: A list of numbers for one-channel PCM data. This should be
+ normalized to [-1, 1] so the function can check if signal RMS
+ is too small to be meaningful.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ min_peak_ratio: The minimum peak_i/peak_0 ratio such that the
+ peaks other than the greatest one should be
+ considered.
+ This is to ignore peaks that are too small compared
+ to the first peak peak_0.
+ peak_window_size_hz: The window size in Hz to find the peaks.
+ The minimum differences between found peaks will
+ be half of this value.
+
+ Returns:
+ A list of tuples:
+ [(peak_frequency_0, peak_coefficient_0),
+ (peak_frequency_1, peak_coefficient_1),
+ (peak_frequency_2, peak_coefficient_2), ...]
+ where the tuples are sorted by coefficients. The last
+ peak_coefficient will be no less than peak_coefficient *
+ min_peak_ratio. If RMS is less than MEANINGFUL_RMS_THRESHOLD,
+ returns [(0, 0)].
+
+ """
+ # Checks the signal is meaningful.
+ if len(signal) == 0:
+ raise EmptyDataError('Signal data is empty')
+
+ signal_rms = numpy.linalg.norm(signal) / numpy.sqrt(len(signal))
+ logging.debug('signal RMS = %s', signal_rms)
+
+ # If RMS is too small, set dominant frequency and coefficient to 0.
+ if signal_rms < MEANINGFUL_RMS_THRESHOLD:
+ logging.warning(
+ 'RMS %s is too small to be meaningful. Set frequency to 0.',
+ signal_rms)
+ return [(0, 0)]
+
+ logging.debug('Doing spectral analysis ...')
+
+ # First, pass signal through a window function to mitigate spectral leakage.
+ y_conv_w = signal * numpy.hanning(len(signal))
+
+ length = len(y_conv_w)
+
+ # x_f is the frequency in Hz, y_f is the transformed coefficient.
+ x_f = _rfft_freq(length, rate)
+ y_f = 2.0 / length * numpy.fft.rfft(y_conv_w)
+
+ # y_f is complex so consider its absolute value for magnitude.
+ abs_y_f = numpy.abs(y_f)
+ threshold = max(abs_y_f) * min_peak_ratio
+
+ # Suppresses all coefficients that are below threshold.
+ for i in range(len(abs_y_f)):
+ if abs_y_f[i] < threshold:
+ abs_y_f[i] = 0
+
+ # Gets the peak detection window size in indice.
+ # x_f[1] is the frequency difference per index.
+ peak_window_size = int(peak_window_size_hz / x_f[1])
+
+ # Detects peaks.
+ peaks = peak_detection(abs_y_f, peak_window_size)
+
+ # Transform back the peak location from index to frequency.
+ results = []
+ for index, value in peaks:
+ results.append((x_f[int(index)], value))
+ return results
+
+
+def _rfft_freq(length, rate):
+ """Gets the frequency at each index of real FFT.
+
+ Args:
+ length: The window length of FFT.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+ Returns:
+ A numpy array containing frequency corresponding to numpy.fft.rfft
+ result at each index.
+
+ """
+ # The difference in Hz between each index.
+ val = rate / float(length)
+ # Only care half of frequencies for FFT on real signal.
+ result_length = length // 2 + 1
+ return numpy.linspace(0, (result_length - 1) * val, result_length)
+
+
+def peak_detection(array, window_size):
+ """Detects peaks in an array.
+
+ A point (i, array[i]) is a peak if array[i] is the maximum among
+ array[i - half_window_size] to array[i + half_window_size].
+ If array[i - half_window_size] to array[i + half_window_size] are all equal,
+ then there is no peak in this window.
+ Note that we only consider peak with value greater than 0.
+
+ Args:
+ array: The input array to detect peaks in. Array is a list of
+ absolute values of the magnitude of transformed coefficient.
+
+ window_size: The window to detect peaks.
+
+ Returns:
+ A list of tuples:
+ [(peak_index_1, peak_value_1), (peak_index_2, peak_value_2), ...]
+ where the tuples are sorted by peak values.
+
+ """
+ half_window_size = window_size / 2
+ length = len(array)
+
+ def mid_is_peak(array, mid, left, right):
+ """Checks if value at mid is the largest among left to right in array.
+
+ Args:
+ array: A list of numbers.
+ mid: The mid index.
+ left: The left index.
+ rigth: The right index.
+
+ Returns:
+ A tuple (is_peak, next_candidate)
+ is_peak is True if array[index] is the maximum among numbers
+ in array between index [left, right] inclusively.
+ next_candidate is the index of next candidate for peak if
+ is_peak is False. It is the index of maximum value in
+ [mid + 1, right]. If is_peak is True, next_candidate is
+ right + 1.
+
+ """
+ value_mid = array[int(mid)]
+ is_peak = True
+ next_peak_candidate_index = None
+
+ # Check the left half window.
+ for index in range(int(left), int(mid)):
+ if array[index] >= value_mid:
+ is_peak = False
+ break
+
+ # Mid is at the end of array.
+ if mid == right:
+ return is_peak, right + 1
+
+ # Check the right half window and also record next candidate.
+ # Favor the larger index for next_peak_candidate_index.
+ for index in range(int(right), int(mid), -1):
+ if (next_peak_candidate_index is None or
+ array[index] > array[next_peak_candidate_index]):
+ next_peak_candidate_index = index
+
+ if array[next_peak_candidate_index] >= value_mid:
+ is_peak = False
+
+ if is_peak:
+ next_peak_candidate_index = right + 1
+
+ return is_peak, next_peak_candidate_index
+
+ results = []
+ mid = 0
+ next_candidate_idx = None
+ while mid < length:
+ left = max(0, mid - half_window_size)
+ right = min(length - 1, mid + half_window_size)
+
+ # Only consider value greater than 0.
+ if array[int(mid)] == 0:
+ mid = mid + 1
+ continue
+
+ is_peak, next_candidate_idx = mid_is_peak(array, mid, left, right)
+
+ if is_peak:
+ results.append((mid, array[int(mid)]))
+
+ # Use the next candidate found in [mid + 1, right], or right + 1.
+ mid = next_candidate_idx
+
+ # Sort the peaks by values.
+ return sorted(results, key=lambda x: x[1], reverse=True)
+
+
+def anomaly_detection(signal,
+ rate,
+ freq,
+ block_size=ANOMALY_DETECTION_BLOCK_SIZE,
+ threshold=PATTERN_MATCHING_THRESHOLD):
+ """Detects anomaly in a sine wave signal.
+
+ This method detects anomaly in a sine wave signal by matching
+ patterns of each block.
+ For each moving window of block in the test signal, checks if there
+ is any block in golden signal that is similar to this block of test signal.
+ If there is such a block in golden signal, then this block of test
+ signal is matched and there is no anomaly in this block of test signal.
+ If there is any block in test signal that is not matched, then this block
+ covers an anomaly.
+ The block of test signal starts from index 0, and proceeds in steps of
+ half block size. The overlapping of test signal blocks makes sure there must
+ be at least one block covering the transition from sine wave to anomaly.
+
+ Args:
+ signal: A 1-D array-like object for 1-channel PCM data.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ freq: The expected frequency of signal.
+ block_size: The block size in samples to detect anomaly.
+ threshold: The threshold of correlation index to be judge as matched.
+
+ Returns:
+ A list containing time markers in seconds that have an anomaly within
+ block_size samples.
+
+ """
+ if len(signal) == 0:
+ raise EmptyDataError('Signal data is empty')
+
+ golden_y = _generate_golden_pattern(rate, freq, block_size)
+
+ results = []
+
+ for start in range(0, len(signal), int(block_size / 2)):
+ end = start + block_size
+ test_signal = signal[start:end]
+ matched = _moving_pattern_matching(golden_y, test_signal, threshold)
+ if not matched:
+ results.append(start)
+
+ results = [float(x) / rate for x in results]
+
+ return results
+
+
+def get_anomaly_durations(signal,
+ rate,
+ freq,
+ block_size=ANOMALY_DETECTION_BLOCK_SIZE,
+ threshold=PATTERN_MATCHING_THRESHOLD,
+ tolerance=ANOMALY_GROUPING_TOLERANCE):
+ """Detect anomalies in a sine wav and return their start and end times.
+
+ Run anomaly_detection function and parse resulting array of time values into
+ discrete anomalies defined by a start and end time tuple. Time values are
+ judged to be part of the same anomaly if they lie within a given tolerance
+ of half the block_size number of samples of each other.
+
+ Args:
+ signal: A 1-D array-like object for 1-channel PCM data.
+ rate (int): Sampling rate in samples per second.
+ Example inputs: 44100, 48000
+ freq (int): The expected frequency of signal.
+ block_size (int): The block size in samples to detect anomaly.
+ threshold (float): The threshold of correlation index to be judge as
+ matched.
+ tolerance (float): The number of samples greater than block_size / 2
+ that the sample distance between two anomaly time values can be and
+ still be grouped as the same anomaly.
+ Returns:
+ bounds (list): a list of (start, end) tuples where start and end are the
+ boundaries in seconds of the detected anomaly.
+ """
+ bounds = []
+ anoms = anomaly_detection(signal, rate, freq, block_size, threshold)
+ if len(anoms) == 0:
+ return bounds
+ end = anoms[0]
+ start = anoms[0]
+ for i in range(len(anoms)-1):
+ end = anoms[i]
+ sample_diff = abs(anoms[i] - anoms[i+1]) * rate
+ # We require a tolerance because sample_diff may be slightly off due to
+ # float rounding errors in Python.
+ if sample_diff > block_size / 2 + tolerance:
+ bounds.append((start, end))
+ start = anoms[i+1]
+ bounds.append((start, end))
+ return bounds
+
+
+def _generate_golden_pattern(rate, freq, block_size):
+ """Generates a golden pattern of certain frequency.
+
+ The golden pattern must cover all the possibilities of waveforms in a
+ block. So, we need a golden pattern covering 1 period + 1 block size,
+ such that the test block can start anywhere in a period, and extends
+ a block size.
+
+ |period |1 bk|
+ | | |
+ . . . .
+ . . . .
+ . . .
+
+ Args:
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ freq: The frequency of golden pattern.
+ block_size: The block size in samples to detect anomaly.
+
+ Returns:
+ A 1-D array for golden pattern.
+
+ """
+ samples_in_a_period = int(rate / freq) + 1
+ samples_in_golden_pattern = samples_in_a_period + block_size
+ golden_x = numpy.linspace(0.0, (samples_in_golden_pattern - 1) * 1.0 /
+ rate, samples_in_golden_pattern)
+ golden_y = numpy.sin(freq * 2.0 * numpy.pi * golden_x)
+ return golden_y
+
+
+def _moving_pattern_matching(golden_signal, test_signal, threshold):
+ """Checks if test_signal is similar to any block of golden_signal.
+
+ Compares test signal with each block of golden signal by correlation
+ index. If there is any block of golden signal that is similar to
+ test signal, then it is matched.
+
+ Args:
+ golden_signal: A 1-D array for golden signal.
+ test_signal: A 1-D array for test signal.
+ threshold: The threshold of correlation index to be judge as matched.
+
+ Returns:
+ True if there is a match. False otherwise.
+
+ ValueError: if test signal is longer than golden signal.
+
+ """
+ if len(golden_signal) < len(test_signal):
+ raise ValueError('Test signal is longer than golden signal')
+
+ block_length = len(test_signal)
+ number_of_movings = len(golden_signal) - block_length + 1
+ correlation_indices = []
+ for moving_index in range(number_of_movings):
+ # Cuts one block of golden signal from start index.
+ # The block length is the same as test signal.
+ start = moving_index
+ end = start + block_length
+ golden_signal_block = golden_signal[start:end]
+ try:
+ correlation_index = _get_correlation_index(golden_signal_block,
+ test_signal)
+ except TestSignalNormTooSmallError:
+ logging.info(
+ 'Caught one block of test signal that has no meaningful norm')
+ return False
+ correlation_indices.append(correlation_index)
+
+ # Checks if the maximum correlation index is high enough.
+ max_corr = max(correlation_indices)
+ if max_corr < threshold:
+ logging.debug('Got one unmatched block with max_corr: %s', max_corr)
+ return False
+ return True
+
+
+class GoldenSignalNormTooSmallError(Exception):
+ """Exception when golden signal norm is too small."""
+ pass
+
+
+class TestSignalNormTooSmallError(Exception):
+ """Exception when test signal norm is too small."""
+ pass
+
+
+def _get_correlation_index(golden_signal, test_signal):
+ """Computes correlation index of two signal of same length.
+
+ Args:
+ golden_signal: An 1-D array-like object.
+ test_signal: An 1-D array-like object.
+
+ Raises:
+ ValueError: if two signal have different lengths.
+ GoldenSignalNormTooSmallError: if golden signal norm is too small
+ TestSignalNormTooSmallError: if test signal norm is too small.
+
+ Returns:
+ The correlation index.
+ """
+ if len(golden_signal) != len(test_signal):
+ raise ValueError('Only accepts signal of same length: %s, %s' %
+ (len(golden_signal), len(test_signal)))
+
+ norm_golden = numpy.linalg.norm(golden_signal)
+ norm_test = numpy.linalg.norm(test_signal)
+ if norm_golden <= _MINIMUM_SIGNAL_NORM:
+ raise GoldenSignalNormTooSmallError(
+ 'No meaningful data as norm is too small.')
+ if norm_test <= _MINIMUM_SIGNAL_NORM:
+ raise TestSignalNormTooSmallError(
+ 'No meaningful data as norm is too small.')
+
+ # The 'valid' cross correlation result of two signals of same length will
+ # contain only one number.
+ correlation = numpy.correlate(golden_signal, test_signal, 'valid')[0]
+ return correlation / (norm_golden * norm_test)
+
+
+def fundamental_freq(signal, rate):
+ """Return fundamental frequency of signal by finding max in freq domain.
+ """
+ dft = numpy.fft.rfft(signal)
+ fund_freq = rate * (numpy.argmax(numpy.abs(dft)) / len(signal))
+ return fund_freq
+
+
+def rms(array):
+ """Return the root mean square of array.
+ """
+ return numpy.sqrt(numpy.mean(numpy.absolute(array)**2))
+
+
+def THDN(signal, rate, q, freq):
+ """Measure the THD+N for a signal and return the results.
+ Subtract mean to center signal around 0, remove fundamental frequency from
+ dft using notch filter and transform back into signal to get noise. Compute
+ ratio of RMS of noise signal to RMS of entire signal.
+
+ Args:
+ signal: array of values representing an audio signal.
+ rate: sample rate in Hz of the signal.
+ q: quality factor for the notch filter.
+ freq: fundamental frequency of the signal. All other frequencies
+ are noise. If not specified, will be calculated using FFT.
+ Returns:
+ THDN: THD+N ratio calculated from the ratio of RMS of pure harmonics
+ and noise signal to RMS of original signal.
+ """
+ # Normalize and window signal.
+ signal -= numpy.mean(signal)
+ windowed = signal * blackmanharris(len(signal))
+ # Find fundamental frequency to remove if not specified.
+ freq = freq or fundamental_freq(windowed, rate)
+ # Create notch filter to isolate noise.
+ w0 = freq / (rate / 2.0)
+ b, a = iirnotch(w0, q)
+ noise = lfilter(b, a, windowed)
+ # Calculate THD+N.
+ THDN = rms(noise) / rms(windowed)
+ return THDN
+
+
+def max_THDN(signal, rate, step_size, window_size, q, freq):
+ """Analyze signal with moving window and find maximum THD+N value.
+ Args:
+ signal: array representing the signal
+ rate: sample rate of the signal.
+ step_size: how many samples to move the window by for each analysis.
+ window_size: how many samples to analyze each time.
+ q: quality factor for the notch filter.
+ freq: fundamental frequency of the signal. All other frequencies
+ are noise. If not specified, will be calculated using FFT.
+ Returns:
+ greatest_THDN: the greatest THD+N value found across all windows
+ """
+ greatest_THDN = 0
+ cur = 0
+ while cur + window_size < len(signal):
+ window = signal[cur:cur + window_size]
+ res = THDN(window, rate, q, freq)
+ cur += step_size
+ if res > greatest_THDN:
+ greatest_THDN = res
+ return greatest_THDN
+
+
+def get_file_THDN(filename, q, freq=None):
+ """Get THD+N values for each channel of an audio file.
+
+ Args:
+ filename (str): path to the audio file.
+ (supported file types: http://www.mega-nerd.com/libsndfile/#Features)
+ q (float): quality factor for the notch filter.
+ freq (int|float): fundamental frequency of the signal. All other
+ frequencies are noise. If None, will be calculated with FFT.
+ Returns:
+ channel_results (list): THD+N value for each channel's signal.
+ List index corresponds to channel index.
+ """
+ audio_file = soundfile.SoundFile(filename)
+ channel_results = []
+ if audio_file.channels == 1:
+ channel_results.append(THDN(signal=audio_file.read(),
+ rate=audio_file.samplerate,
+ q=q,
+ freq=freq))
+ else:
+ for ch_no, channel in enumerate(audio_file.read().transpose()):
+ channel_results.append(THDN(signal=channel,
+ rate=audio_file.samplerate,
+ q=q,
+ freq=freq))
+ return channel_results
+
+
+def get_file_max_THDN(filename, step_size, window_size, q, freq=None):
+ """Get max THD+N value across analysis windows for each channel of file.
+
+ Args:
+ filename (str): path to the audio file.
+ (supported file types: http://www.mega-nerd.com/libsndfile/#Features)
+ step_size: how many samples to move the window by for each analysis.
+ window_size: how many samples to analyze each time.
+ q (float): quality factor for the notch filter.
+ freq (int|float): fundamental frequency of the signal. All other
+ frequencies are noise. If None, will be calculated with FFT.
+ Returns:
+ channel_results (list): max THD+N value for each channel's signal.
+ List index corresponds to channel index.
+ """
+ audio_file = soundfile.SoundFile(filename)
+ channel_results = []
+ if audio_file.channels == 1:
+ channel_results.append(max_THDN(signal=audio_file.read(),
+ rate=audio_file.samplerate,
+ step_size=step_size,
+ window_size=window_size,
+ q=q,
+ freq=freq))
+ else:
+ for ch_no, channel in enumerate(audio_file.read().transpose()):
+ channel_results.append(max_THDN(signal=channel,
+ rate=audio_file.samplerate,
+ step_size=step_size,
+ window_size=window_size,
+ q=q,
+ freq=freq))
+ return channel_results
+
+
+def get_file_anomaly_durations(filename, freq=None,
+ block_size=ANOMALY_DETECTION_BLOCK_SIZE,
+ threshold=PATTERN_MATCHING_THRESHOLD,
+ tolerance=ANOMALY_GROUPING_TOLERANCE):
+ """Get durations of anomalies for each channel of audio file.
+
+ Args:
+ filename (str): path to the audio file.
+ (supported file types: http://www.mega-nerd.com/libsndfile/#Features)
+ freq (int|float): fundamental frequency of the signal. All other
+ frequencies are noise. If None, will be calculated with FFT.
+ block_size (int): The block size in samples to detect anomaly.
+ threshold (float): The threshold of correlation index to be judge as
+ matched.
+ tolerance (float): The number of samples greater than block_size / 2
+ that the sample distance between two anomaly time values can be and
+ still be grouped as the same anomaly.
+ Returns:
+ channel_results (list): anomaly durations for each channel's signal.
+ List index corresponds to channel index.
+ """
+ audio_file = soundfile.SoundFile(filename)
+ signal = audio_file.read()
+ freq = freq or fundamental_freq(signal, audio_file.samplerate)
+ channel_results = []
+ if audio_file.channels == 1:
+ channel_results.append(get_anomaly_durations(
+ signal=signal,
+ rate=audio_file.samplerate,
+ freq=freq,
+ block_size=block_size,
+ threshold=threshold,
+ tolerance=tolerance))
+ else:
+ for ch_no, channel in enumerate(signal.transpose()):
+ channel_results.append(get_anomaly_durations(
+ signal=channel,
+ rate=audio_file.samplerate,
+ freq=freq,
+ block_size=block_size,
+ threshold=threshold,
+ tolerance=tolerance))
+ return channel_results
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py
new file mode 100644
index 0000000..5867a48
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""This module provides abstraction of audio data."""
+
+import contextlib
+import copy
+import numpy
+import struct
+from io import StringIO
+"""The dict containing information on how to parse sample from raw data.
+
+Keys: The sample format as in aplay command.
+Values: A dict containing:
+ message: Human-readable sample format.
+ dtype_str: Data type used in numpy dtype. Check
+ https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
+ for supported data type.
+ size_bytes: Number of bytes for one sample.
+"""
+SAMPLE_FORMATS = dict(
+ S32_LE=dict(
+ message='Signed 32-bit integer, little-endian',
+ dtype_str='<i',
+ size_bytes=4),
+ S16_LE=dict(
+ message='Signed 16-bit integer, little-endian',
+ dtype_str='<i',
+ size_bytes=2))
+
+
+def get_maximum_value_from_sample_format(sample_format):
+ """Gets the maximum value from sample format.
+
+ Args:
+ sample_format: A key in SAMPLE_FORMAT.
+
+ Returns:The maximum value the sample can hold + 1.
+
+ """
+ size_bits = SAMPLE_FORMATS[sample_format]['size_bytes'] * 8
+ return 1 << (size_bits - 1)
+
+
+class AudioRawDataError(Exception):
+ """Error in AudioRawData."""
+ pass
+
+
+class AudioRawData(object):
+ """The abstraction of audio raw data.
+
+ @property channel: The number of channels.
+ @property channel_data: A list of lists containing samples in each channel.
+ E.g., The third sample in the second channel is
+ channel_data[1][2].
+ @property sample_format: The sample format which should be one of the keys
+ in audio_data.SAMPLE_FORMATS.
+ """
+
+ def __init__(self, binary, channel, sample_format):
+ """Initializes an AudioRawData.
+
+ Args:
+ binary: A string containing binary data. If binary is not None,
+ The samples in binary will be parsed and be filled into
+ channel_data.
+ channel: The number of channels.
+ sample_format: One of the keys in audio_data.SAMPLE_FORMATS.
+ """
+ self.channel = channel
+ self.channel_data = [[] for _ in range(self.channel)]
+ self.sample_format = sample_format
+ if binary:
+ self.read_binary(binary)
+
+ def read_binary(self, binary):
+ """Reads samples from binary and fills channel_data.
+
+ Reads samples of fixed width from binary string into a numpy array
+ and shapes them into each channel.
+
+ Args:
+ binary: A string containing binary data.
+ """
+ sample_format_dict = SAMPLE_FORMATS[self.sample_format]
+
+ # The data type used in numpy fromstring function. For example,
+ # <i4 for 32-bit signed int.
+ np_dtype = '%s%d' % (sample_format_dict['dtype_str'],
+ sample_format_dict['size_bytes'])
+
+ # Reads data from a string into 1-D array.
+ np_array = numpy.fromstring(binary, dtype=np_dtype)
+
+ n_frames = len(np_array) / self.channel
+ # Reshape np_array into an array of shape (n_frames, channel).
+ np_array = np_array.reshape(int(n_frames), self.channel)
+ # Transpose np_arrya so it becomes of shape (channel, n_frames).
+ self.channel_data = np_array.transpose()
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py
new file mode 100644
index 0000000..c241fde
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py
@@ -0,0 +1,929 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""This module provides utilities to detect some artifacts and measure the
+ quality of audio."""
+
+import logging
+import math
+import numpy
+
+import acts_contrib.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+
+# The input signal should be one sine wave with fixed frequency which
+# can have silence before and/or after sine wave.
+# For example:
+# silence sine wave silence
+# -----------|VVVVVVVVVVVVV|-----------
+# (a) (b) (c)
+# This module detects these artifacts:
+# 1. Detect noise in (a) and (c).
+# 2. Detect delay in (b).
+# 3. Detect burst in (b).
+# Assume the transitions between (a)(b) and (b)(c) are smooth and
+# amplitude increases/decreases linearly.
+# This module will detect artifacts in the sine wave.
+# This module also estimates the equivalent noise level by teager operator.
+# This module also detects volume changes in the sine wave. However, volume
+# changes may be affected by delay or burst.
+# Some artifacts may cause each other.
+
+# In this module, amplitude and frequency are derived from Hilbert transform.
+# Both amplitude and frequency are a function of time.
+
+# To detect each artifact, each point will be compared with
+# average amplitude of its block. The block size will be 1.5 ms.
+# Using average amplitude can mitigate the error caused by
+# Hilbert transform and noise.
+# In some case, for more accuracy, the block size may be modified
+# to other values.
+DEFAULT_BLOCK_SIZE_SECS = 0.0015
+
+# If the difference between average frequency of this block and
+# dominant frequency of full signal is less than 0.5 times of
+# dominant frequency, this block is considered to be within the
+# sine wave. In most cases, if there is no sine wave(only noise),
+# average frequency will be much greater than 5 times of
+# dominant frequency.
+# Also, for delay during playback, the frequency will be about 0
+# in perfect situation or much greater than 5 times of dominant
+# frequency if it's noised.
+DEFAULT_FREQUENCY_ERROR = 0.5
+
+# If the amplitude of some sample is less than 0.6 times of the
+# average amplitude of its left/right block, it will be considered
+# as a delay during playing.
+DEFAULT_DELAY_AMPLITUDE_THRESHOLD = 0.6
+
+# If the average amplitude of the block before or after playing
+# is more than 0.5 times to the average amplitude of the wave,
+# it will be considered as a noise artifact.
+DEFAULT_NOISE_AMPLITUDE_THRESHOLD = 0.5
+
+# In the sine wave, if the amplitude is more than 1.4 times of
+# its left side and its right side, it will be considered as
+# a burst.
+DEFAULT_BURST_AMPLITUDE_THRESHOLD = 1.4
+
+# When detecting burst, if the amplitude is lower than 0.5 times
+# average amplitude, we ignore it.
+DEFAULT_BURST_TOO_SMALL = 0.5
+
+# For a signal which is the combination of sine wave with fixed frequency f and
+# amplitude 1 and standard noise with amplitude k, the average teager value is
+# nearly linear to the noise level k.
+# Given frequency f, we simulate a sine wave with default noise level and
+# calculate its average teager value. Then, we can estimate the equivalent
+# noise level of input signal by the average teager value of input signal.
+DEFAULT_STANDARD_NOISE = 0.005
+
+# For delay, burst, volume increasing/decreasing, if two delay(
+# burst, volume increasing/decreasing) happen within
+# DEFAULT_SAME_EVENT_SECS seconds, we consider they are the
+# same event.
+DEFAULT_SAME_EVENT_SECS = 0.001
+
+# When detecting increasing/decreasing volume of signal, if the amplitude
+# is lower than 0.1 times average amplitude, we ignore it.
+DEFAULT_VOLUME_CHANGE_TOO_SMALL = 0.1
+
+# If average amplitude of right block is less/more than average
+# amplitude of left block times DEFAULT_VOLUME_CHANGE_AMPLITUDE, it will be
+# considered as decreasing/increasing on volume.
+DEFAULT_VOLUME_CHANGE_AMPLITUDE = 0.1
+
+# If the increasing/decreasing volume event is too close to the start or the end
+# of sine wave, we consider its volume change as part of rising/falling phase in
+# the start/end.
+NEAR_START_OR_END_SECS = 0.01
+
+# After applying Hilbert transform, the resulting amplitude and frequency may be
+# extremely large in the start and/or the end part. Thus, we will append zeros
+# before and after the whole wave for 0.1 secs.
+APPEND_ZEROS_SECS = 0.1
+
+# If the noise event is too close to the start or the end of the data, we
+# consider its noise as part of artifacts caused by edge effect of Hilbert
+# transform.
+# For example, originally, the data duration is 10 seconds.
+# We append 0.1 seconds of zeros in the beginning and the end of the data, so
+# the data becomes 10.2 seocnds long.
+# Then, we apply Hilbert transform to 10.2 seconds of data.
+# Near 0.1 seconds and 10.1 seconds, there will be edge effect of Hilbert
+# transform. We do not want these be treated as noise.
+# If NEAR_DATA_START_OR_END_SECS is set to 0.01, then the noise happened
+# at [0, 0.11] and [10.09, 10.1] will be ignored.
+NEAR_DATA_START_OR_END_SECS = 0.01
+
+# If the noise event is too close to the start or the end of the sine wave in
+# the data, we consider its noise as part of artifacts caused by edge effect of
+# Hilbert transform.
+# A |-------------|vvvvvvvvvvvvvvvvvvvvvvv|-------------|
+# B |ooooooooo| d | | d |ooooooooo|
+#
+# A is full signal. It contains a sine wave and silence before and after sine
+# wave.
+# In B, |oooo| shows the parts that we are going to check for noise before/after
+# sine wave. | d | is determined by NEAR_SINE_START_OR_END_SECS.
+NEAR_SINE_START_OR_END_SECS = 0.01
+
+
+class SineWaveNotFound(Exception):
+ """Error when there's no sine wave found in the signal"""
+ pass
+
+
+def hilbert(x):
+ """Hilbert transform copied from scipy.
+
+ More information can be found here:
+ http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.hilbert.html
+
+ Args:
+ x: Real signal data to transform.
+
+ Returns:
+ Analytic signal of x, we can further extract amplitude and
+ frequency from it.
+
+ """
+ x = numpy.asarray(x)
+ if numpy.iscomplexobj(x):
+ raise ValueError("x must be real.")
+ axis = -1
+ N = x.shape[axis]
+ if N <= 0:
+ raise ValueError("N must be positive.")
+
+ Xf = numpy.fft.fft(x, N, axis=axis)
+ h = numpy.zeros(N)
+ if N % 2 == 0:
+ h[0] = h[N // 2] = 1
+ h[1:N // 2] = 2
+ else:
+ h[0] = 1
+ h[1:(N + 1) // 2] = 2
+
+ if len(x.shape) > 1:
+ ind = [newaxis] * x.ndim
+ ind[axis] = slice(None)
+ h = h[ind]
+ x = numpy.fft.ifft(Xf * h, axis=axis)
+ return x
+
+
+def noised_sine_wave(frequency, rate, noise_level):
+ """Generates a sine wave of 2 second with specified noise level.
+
+ Args:
+ frequency: Frequency of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ noise_level: Required noise level.
+
+ Returns:
+ A sine wave with specified noise level.
+
+ """
+ wave = []
+ for index in range(0, rate * 2):
+ sample = 2.0 * math.pi * frequency * float(index) / float(rate)
+ sine_wave = math.sin(sample)
+ noise = noise_level * numpy.random.standard_normal()
+ wave.append(sine_wave + noise)
+ return wave
+
+
+def average_teager_value(wave, amplitude):
+ """Computes the normalized average teager value.
+
+ After averaging the teager value, we will normalize the value by
+ dividing square of amplitude.
+
+ Args:
+ wave: Wave to apply teager operator.
+ amplitude: Average amplitude of given wave.
+
+ Returns:
+ Average teager value.
+
+ """
+ teager_value, length = 0, len(wave)
+ for i in range(1, length - 1):
+ ith_teager_value = abs(wave[i] * wave[i] - wave[i - 1] * wave[i + 1])
+ ith_teager_value *= max(1, abs(wave[i]))
+ teager_value += ith_teager_value
+ teager_value = (float(teager_value) / length) / (amplitude**2)
+ return teager_value
+
+
+def noise_level(amplitude, frequency, rate, teager_value_of_input):
+ """Computes the noise level compared with standard_noise.
+
+ For a signal which is the combination of sine wave with fixed frequency f
+ and amplitude 1 and standard noise with amplitude k, the average teager
+ value is nearly linear to the noise level k.
+ Thus, we can compute the average teager value of a sine wave with
+ standard_noise. Then, we can estimate the noise level of given input.
+
+ Args:
+ amplitude: Amplitude of input audio.
+ frequency: Dominant frequency of input audio.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ teager_value_of_input: Average teager value of input audio.
+
+ Returns:
+ A float value denotes the audio is equivalent to have how many times of
+ noise compared with its amplitude.For example, 0.02 denotes that the
+ wave has a noise which has standard distribution with standard
+ deviation being 0.02 times the amplitude of the wave.
+
+ """
+ standard_noise = DEFAULT_STANDARD_NOISE
+
+ # Generates the standard sine wave with stdandard_noise level of noise.
+ standard_wave = noised_sine_wave(frequency, rate, standard_noise)
+
+ # Calculates the average teager value.
+ teager_value_of_std_wave = average_teager_value(standard_wave, amplitude)
+
+ return (teager_value_of_input / teager_value_of_std_wave) * standard_noise
+
+
+def error(f1, f2):
+ """Calculates the relative error between f1 and f2.
+
+ Args:
+ f1: Exact value.
+ f2: Test value.
+
+ Returns:
+ Relative error between f1 and f2.
+
+ """
+ return abs(float(f1) - float(f2)) / float(f1)
+
+
+def hilbert_analysis(signal, rate, block_size):
+ """Finds amplitude and frequency of each time of signal by Hilbert transform.
+
+ Args:
+ signal: The wave to analyze.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ block_size: The size of block to transform.
+
+ Returns:
+ A tuple of list: (amplitude, frequency) composed of amplitude and
+ frequency of each time.
+
+ """
+ # To apply Hilbert transform, the wave will be transformed
+ # segment by segment. For each segment, its size will be
+ # block_size and we will only take middle part of it.
+ # Thus, each segment looks like: |-----|=====|=====|-----|.
+ # "=...=" part will be taken while "-...-" part will be ignored.
+ #
+ # The whole size of taken part will be half of block_size
+ # which will be hilbert_block.
+ # The size of each ignored part will be half of hilbert_block
+ # which will be half_hilbert_block.
+ hilbert_block = block_size // 2
+ half_hilbert_block = hilbert_block // 2
+ # As mentioned above, for each block, we will only take middle
+ # part of it. Thus, the whole transformation will be completed as:
+ # |=====|=====|-----| |-----|=====|=====|-----|
+ # |-----|=====|=====|-----| |-----|=====|=====|
+ # |-----|=====|=====|-----|
+ # Specially, beginning and ending part may not have ignored part.
+ length = len(signal)
+ result = []
+ for left_border in range(0, length, hilbert_block):
+ right_border = min(length, left_border + hilbert_block)
+ temp_left_border = max(0, left_border - half_hilbert_block)
+ temp_right_border = min(length, right_border + half_hilbert_block)
+ temp = hilbert(signal[temp_left_border:temp_right_border])
+ for index in range(left_border, right_border):
+ result.append(temp[index - temp_left_border])
+ result = numpy.asarray(result)
+ amplitude = numpy.abs(result)
+ phase = numpy.unwrap(numpy.angle(result))
+ frequency = numpy.diff(phase) / (2.0 * numpy.pi) * rate
+ #frequency.append(frequency[len(frequency)-1])
+ frequecny = numpy.append(frequency, frequency[len(frequency) - 1])
+ return (amplitude, frequency)
+
+
+def find_block_average_value(arr, side_block_size, block_size):
+ """For each index, finds average value of its block, left block, right block.
+
+ It will find average value for each index in the range.
+
+ For each index, the range of its block is
+ [max(0, index - block_size / 2), min(length - 1, index + block_size / 2)]
+ For each index, the range of its left block is
+ [max(0, index - size_block_size), index]
+ For each index, the range of its right block is
+ [index, min(length - 1, index + side_block_size)]
+
+ Args:
+ arr: The array to be computed.
+ side_block_size: the size of the left_block and right_block.
+ block_size: the size of the block.
+
+ Returns:
+ A tuple of lists: (left_block_average_array,
+ right_block_average_array,
+ block_average_array)
+ """
+ length = len(arr)
+ left_border, right_border = 0, 1
+ left_block_sum = arr[0]
+ right_block_sum = arr[0]
+ left_average_array = numpy.zeros(length)
+ right_average_array = numpy.zeros(length)
+ block_average_array = numpy.zeros(length)
+ for index in range(0, length):
+ while left_border < index - side_block_size:
+ left_block_sum -= arr[left_border]
+ left_border += 1
+ while right_border < min(length, index + side_block_size):
+ right_block_sum += arr[right_border]
+ right_border += 1
+
+ left_average_value = float(left_block_sum) / (index - left_border + 1)
+ right_average_value = float(right_block_sum) / (right_border - index)
+ left_average_array[index] = left_average_value
+ right_average_array[index] = right_average_value
+
+ if index + 1 < length:
+ left_block_sum += arr[index + 1]
+ right_block_sum -= arr[index]
+ left_border, right_border = 0, 1
+ block_sum = 0
+ for index in range(0, length):
+ while left_border < index - block_size / 2:
+ block_sum -= arr[left_border]
+ left_border += 1
+ while right_border < min(length, index + block_size / 2):
+ block_sum += arr[right_border]
+ right_border += 1
+
+ average_value = float(block_sum) / (right_border - left_border)
+ block_average_array[index] = average_value
+ return (left_average_array, right_average_array, block_average_array)
+
+
+def find_start_end_index(dominant_frequency, block_frequency_delta, block_size,
+ frequency_error_threshold):
+ """Finds start and end index of sine wave.
+
+ For each block with size of block_size, we check that whether its frequency
+ is close enough to the dominant_frequency. If yes, we will consider this
+ block to be within the sine wave.
+ Then, it will return the start and end index of sine wave indicating that
+ sine wave is between [start_index, end_index)
+ It's okay if the whole signal only contains sine wave.
+
+ Args:
+ dominant_frequency: Dominant frequency of signal.
+ block_frequency_delta: Average absolute difference between dominant
+ frequency and frequency of each block. For
+ each index, its block is
+ [max(0, index - block_size / 2),
+ min(length - 1, index + block_size / 2)]
+ block_size: Block size in samples.
+
+ Returns:
+ A tuple composed of (start_index, end_index)
+
+ """
+ length = len(block_frequency_delta)
+
+ # Finds the start/end time index of playing based on dominant frequency
+ start_index, end_index = length - 1, 0
+ for index in range(0, length):
+ left_border = max(0, index - block_size / 2)
+ right_border = min(length - 1, index + block_size / 2)
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+ if frequency_error < frequency_error_threshold:
+ start_index = min(start_index, left_border)
+ end_index = max(end_index, right_border + 1)
+ return (start_index, end_index)
+
+
+def noise_detection(start_index, end_index, block_amplitude, average_amplitude,
+ rate, noise_amplitude_threshold):
+ """Detects noise before/after sine wave.
+
+ If average amplitude of some sample's block before start of wave or after
+ end of wave is more than average_amplitude times noise_amplitude_threshold,
+ it will be considered as a noise.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ noise_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of the wave times
+ noise_amplitude_threshold, it will be considered as
+ noise before/after playback.
+
+ Returns:
+ A tuple of lists indicating the time that noise happens:
+ (noise_before_playing, noise_after_playing).
+
+ """
+ length = len(block_amplitude)
+ amplitude_threshold = average_amplitude * noise_amplitude_threshold
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+
+ # Detects noise before playing.
+ noise_time_point = []
+ last_noise_end_time_point = []
+ previous_noise_index = None
+ times = 0
+ for index in range(0, length):
+ # Ignore noise too close to the beginning or the end of sine wave.
+ # Check the docstring of NEAR_SINE_START_OR_END_SECS.
+ if ((start_index - rate * NEAR_SINE_START_OR_END_SECS) <= index and
+ (index < end_index + rate * NEAR_SINE_START_OR_END_SECS)):
+ continue
+
+ # Ignore noise too close to the beginning or the end of original data.
+ # Check the docstring of NEAR_DATA_START_OR_END_SECS.
+ if (float(index) / rate <=
+ NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
+ continue
+ if (float(length - index) / rate <=
+ NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
+ continue
+ if block_amplitude[index] > amplitude_threshold:
+ same_event = False
+ if previous_noise_index:
+ same_event = (index - previous_noise_index
+ ) < same_event_samples
+ if not same_event:
+ index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ noise_time_point.append(index_start_sec)
+ last_noise_end_time_point.append(index_end_sec)
+ times += 1
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ last_noise_end_time_point[times - 1] = index_end_sec
+ previous_noise_index = index
+
+ noise_before_playing, noise_after_playing = [], []
+ for i in range(times):
+ duration = last_noise_end_time_point[i] - noise_time_point[i]
+ if noise_time_point[i] < float(start_index) / rate - APPEND_ZEROS_SECS:
+ noise_before_playing.append((noise_time_point[i], duration))
+ else:
+ noise_after_playing.append((noise_time_point[i], duration))
+
+ return (noise_before_playing, noise_after_playing)
+
+
+def delay_detection(start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude,
+ right_block_amplitude, block_frequency_delta,
+ delay_amplitude_threshold, frequency_error_threshold):
+ """Detects delay during playing.
+
+ For each sample, we will check whether the average amplitude of its block
+ is less than average amplitude of its left block and its right block times
+ delay_amplitude_threshold. Also, we will check whether the frequency of
+ its block is far from the dominant frequency.
+ If at least one constraint fulfilled, it will be considered as a delay.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ dominant_frequency: Dominant frequency of signal.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ block_frequency_delta: Average absolute difference frequency to
+ dominant frequency of block of each index.
+ Ref to find_block_average_value function.
+ delay_amplitude_threshold: If the average amplitude of a block is
+ lower than average amplitude of the wave times
+ delay_amplitude_threshold, it will be considered
+ as delay.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR
+
+ Returns:
+ List of delay occurrence:
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ where time and duration are in seconds.
+
+ """
+ delay_time_points = []
+ last_delay_end_time_points = []
+ previous_delay_index = None
+ times = 0
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ start_time = float(start_index) / rate - APPEND_ZEROS_SECS
+ end_time = float(end_index) / rate - APPEND_ZEROS_SECS
+ for index in range(int(start_index), int(end_index)):
+ if block_amplitude[
+ index] > average_amplitude * delay_amplitude_threshold:
+ continue
+ now_time = float(index) / rate - APPEND_ZEROS_SECS
+ if abs(now_time - start_time) < NEAR_START_OR_END_SECS:
+ continue
+ if abs(now_time - end_time) < NEAR_START_OR_END_SECS:
+ continue
+ # If amplitude less than its left/right side and small enough,
+ # it will be considered as a delay.
+ amp_threshold = average_amplitude * delay_amplitude_threshold
+ left_threshold = delay_amplitude_threshold * left_block_amplitude[
+ index]
+ amp_threshold = min(amp_threshold, left_threshold)
+ right_threshold = delay_amplitude_threshold * right_block_amplitude[
+ index]
+ amp_threshold = min(amp_threshold, right_threshold)
+
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+
+ amplitude_too_small = block_amplitude[index] < amp_threshold
+ frequency_not_match = frequency_error > frequency_error_threshold
+
+ if amplitude_too_small or frequency_not_match:
+ same_event = False
+ if previous_delay_index:
+ same_event = (index - previous_delay_index
+ ) < same_event_samples
+ if not same_event:
+ index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ delay_time_points.append(index_start_sec)
+ last_delay_end_time_points.append(index_end_sec)
+ times += 1
+ previous_delay_index = index
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ last_delay_end_time_points[times - 1] = index_end_sec
+
+ delay_list = []
+ for i in range(len(delay_time_points)):
+ duration = last_delay_end_time_points[i] - delay_time_points[i]
+ delay_list.append((delay_time_points[i], duration))
+ return delay_list
+
+
+def burst_detection(start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude,
+ right_block_amplitude, block_frequency_delta,
+ burst_amplitude_threshold, frequency_error_threshold):
+ """Detects burst during playing.
+
+ For each sample, we will check whether the average amplitude of its block is
+ more than average amplitude of its left block and its right block times
+ burst_amplitude_threshold. Also, we will check whether the frequency of
+ its block is not compatible to the dominant frequency.
+ If at least one constraint fulfilled, it will be considered as a burst.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ dominant_frequency: Dominant frequency of signal.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ block_frequency_delta: Average absolute difference frequency to
+ dominant frequency of block of each index.
+ burst_amplitude_threshold: If the amplitude is higher than average
+ amplitude of its left block and its right block
+ times burst_amplitude_threshold. It will be
+ considered as a burst.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR
+
+ Returns:
+ List of burst occurence: [time_1, time_2, ...],
+ where time is in seconds.
+
+ """
+ burst_time_points = []
+ previous_burst_index = None
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ for index in range(int(start_index), int(end_index)):
+ # If amplitude higher than its left/right side and large enough,
+ # it will be considered as a burst.
+ if block_amplitude[
+ index] <= average_amplitude * DEFAULT_BURST_TOO_SMALL:
+ continue
+ if abs(index - start_index) < rate * NEAR_START_OR_END_SECS:
+ continue
+ if abs(index - end_index) < rate * NEAR_START_OR_END_SECS:
+ continue
+ amp_threshold = average_amplitude * DEFAULT_BURST_TOO_SMALL
+ left_threshold = burst_amplitude_threshold * left_block_amplitude[
+ index]
+ amp_threshold = max(amp_threshold, left_threshold)
+ right_threshold = burst_amplitude_threshold * right_block_amplitude[
+ index]
+ amp_threshold = max(amp_threshold, right_threshold)
+
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+
+ amplitude_too_large = block_amplitude[index] > amp_threshold
+ frequency_not_match = frequency_error > frequency_error_threshold
+
+ if amplitude_too_large or frequency_not_match:
+ same_event = False
+ if previous_burst_index:
+ same_event = index - previous_burst_index < same_event_samples
+ if not same_event:
+ burst_time_points.append(
+ float(index) / rate - APPEND_ZEROS_SECS)
+ previous_burst_index = index
+
+ return burst_time_points
+
+
+def changing_volume_detection(start_index, end_index, average_amplitude, rate,
+ left_block_amplitude, right_block_amplitude,
+ volume_changing_amplitude_threshold):
+ """Finds volume changing during playback.
+
+ For each index, we will compare average amplitude of its left block and its
+ right block. If average amplitude of right block is more than average
+ amplitude of left block times (1 + DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
+ be considered as an increasing volume. If the one of right block is less
+ than that of left block times (1 - DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
+ be considered as a decreasing volume.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ average_amplitude: Average amplitude of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ volume_changing_amplitude_threshold: If the average amplitude of right
+ block is higher or lower than
+ that of left one times this
+ value, it will be considered as
+ a volume change.
+ Also refer to
+ DEFAULT_VOLUME_CHANGE_AMPLITUDE
+
+ Returns:
+ List of volume changing composed of 1 for increasing and -1 for
+ decreasing.
+
+ """
+ length = len(left_block_amplitude)
+
+ # Detects rising and/or falling volume.
+ previous_rising_index, previous_falling_index = None, None
+ changing_time = []
+ changing_events = []
+ amplitude_threshold = average_amplitude * DEFAULT_VOLUME_CHANGE_TOO_SMALL
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ for index in range(int(start_index), int(end_index)):
+ # Skips if amplitude is too small.
+ if left_block_amplitude[index] < amplitude_threshold:
+ continue
+ if right_block_amplitude[index] < amplitude_threshold:
+ continue
+ # Skips if changing is from start or end time
+ if float(abs(start_index - index)) / rate < NEAR_START_OR_END_SECS:
+ continue
+ if float(abs(end_index - index)) / rate < NEAR_START_OR_END_SECS:
+ continue
+
+ delta_margin = volume_changing_amplitude_threshold
+ if left_block_amplitude[index] > 0:
+ delta_margin *= left_block_amplitude[index]
+
+ increasing_threshold = left_block_amplitude[index] + delta_margin
+ decreasing_threshold = left_block_amplitude[index] - delta_margin
+
+ if right_block_amplitude[index] > increasing_threshold:
+ same_event = False
+ if previous_rising_index:
+ same_event = index - previous_rising_index < same_event_samples
+ if not same_event:
+ changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
+ changing_events.append(+1)
+ previous_rising_index = index
+ if right_block_amplitude[index] < decreasing_threshold:
+ same_event = False
+ if previous_falling_index:
+ same_event = index - previous_falling_index < same_event_samples
+ if not same_event:
+ changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
+ changing_events.append(-1)
+ previous_falling_index = index
+
+ # Combines consecutive increasing/decreasing event.
+ combined_changing_events, prev = [], 0
+ for i in range(len(changing_events)):
+ if changing_events[i] == prev:
+ continue
+ combined_changing_events.append((changing_time[i], changing_events[i]))
+ prev = changing_events[i]
+ return combined_changing_events
+
+
+def quality_measurement(
+ signal,
+ rate,
+ dominant_frequency=None,
+ block_size_secs=DEFAULT_BLOCK_SIZE_SECS,
+ frequency_error_threshold=DEFAULT_FREQUENCY_ERROR,
+ delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
+ noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
+ burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
+ volume_changing_amplitude_threshold=DEFAULT_VOLUME_CHANGE_AMPLITUDE):
+ """Detects several artifacts and estimates the noise level.
+
+ This method detects artifact before playing, after playing, and delay
+ during playing. Also, it estimates the noise level of the signal.
+ To avoid the influence of noise, it calculates amplitude and frequency
+ block by block.
+
+ Args:
+ signal: A list of numbers for one-channel PCM data. The data should
+ be normalized to [-1,1].
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ dominant_frequency: Dominant frequency of signal. Set None to
+ recalculate the frequency in this function.
+ block_size_secs: Block size in seconds. The measurement will be done
+ block-by-block using average amplitude and frequency
+ in each block to avoid noise.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR.
+ delay_amplitude_threshold: If the average amplitude of a block is
+ lower than average amplitude of the wave
+ times delay_amplitude_threshold, it will
+ be considered as delay.
+ Also refer to delay_detection and
+ DEFAULT_DELAY_AMPLITUDE_THRESHOLD.
+ noise_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of the wave
+ times noise_amplitude_threshold, it will
+ be considered as noise before/after
+ playback.
+ Also refer to noise_detection and
+ DEFAULT_NOISE_AMPLITUDE_THRESHOLD.
+ burst_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of its left
+ block and its right block times
+ burst_amplitude_threshold. It will be
+ considered as a burst.
+ Also refer to burst_detection and
+ DEFAULT_BURST_AMPLITUDE_THRESHOLD.
+ volume_changing_amplitude_threshold: If the average amplitude of right
+ block is higher or lower than
+ that of left one times this
+ value, it will be considered as
+ a volume change.
+ Also refer to
+ changing_volume_detection and
+ DEFAULT_VOLUME_CHANGE_AMPLITUDE
+
+ Returns:
+ A dictoinary of detection/estimation:
+ {'artifacts':
+ {'noise_before_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'noise_after_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'delay_during_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'burst_during_playback':
+ [time_1, time_2, ...]
+ },
+ 'volume_changes':
+ [(time_1, flag_1), (time_2, flag_2), ...],
+ 'equivalent_noise_level': level
+ }
+ where durations and time points are in seconds. And,
+ equivalence_noise_level is the quotient of noise and wave which
+ refers to DEFAULT_STANDARD_NOISE. volume_changes is a list of
+ tuples containing time stamps and decreasing/increasing flags for
+ volume change events.
+
+ """
+ # Calculates the block size, from seconds to samples.
+ block_size = int(block_size_secs * rate)
+
+ signal = numpy.concatenate(
+ (numpy.zeros(int(rate * APPEND_ZEROS_SECS)), signal,
+ numpy.zeros(int(rate * APPEND_ZEROS_SECS))))
+ signal = numpy.array(signal, dtype=float)
+ length = len(signal)
+
+ # Calculates the amplitude and frequency.
+ amplitude, frequency = hilbert_analysis(signal, rate, block_size)
+
+ # Finds the dominant frequency.
+ if not dominant_frequency:
+ dominant_frequency = audio_analysis.spectral_analysis(signal,
+ rate)[0][0]
+
+ # Finds the array which contains absolute difference between dominant
+ # frequency and frequency at each time point.
+ frequency_delta = abs(frequency - dominant_frequency)
+
+ # Computes average amplitude of each type of block
+ res = find_block_average_value(amplitude, block_size * 2, block_size)
+ left_block_amplitude, right_block_amplitude, block_amplitude = res
+
+ # Computes average absolute difference of frequency and dominant frequency
+ # of the block of each index
+ _, _, block_frequency_delta = find_block_average_value(
+ frequency_delta, block_size * 2, block_size)
+
+ # Finds start and end index of sine wave.
+ start_index, end_index = find_start_end_index(
+ dominant_frequency, block_frequency_delta, block_size,
+ frequency_error_threshold)
+
+ if start_index > end_index:
+ raise SineWaveNotFound('No sine wave found in signal')
+
+ logging.debug('Found sine wave: start: %s, end: %s',
+ float(start_index) / rate - APPEND_ZEROS_SECS,
+ float(end_index) / rate - APPEND_ZEROS_SECS)
+
+ sum_of_amplitude = float(sum(amplitude[int(start_index):int(end_index)]))
+ # Finds average amplitude of sine wave.
+ average_amplitude = sum_of_amplitude / (end_index - start_index)
+
+ # Finds noise before and/or after playback.
+ noise_before_playing, noise_after_playing = noise_detection(
+ start_index, end_index, block_amplitude, average_amplitude, rate,
+ noise_amplitude_threshold)
+
+ # Finds delay during playback.
+ delays = delay_detection(start_index, end_index, block_amplitude,
+ average_amplitude, dominant_frequency, rate,
+ left_block_amplitude, right_block_amplitude,
+ block_frequency_delta, delay_amplitude_threshold,
+ frequency_error_threshold)
+
+ # Finds burst during playback.
+ burst_time_points = burst_detection(
+ start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude, right_block_amplitude,
+ block_frequency_delta, burst_amplitude_threshold,
+ frequency_error_threshold)
+
+ # Finds volume changing during playback.
+ volume_changes = changing_volume_detection(
+ start_index, end_index, average_amplitude, rate, left_block_amplitude,
+ right_block_amplitude, volume_changing_amplitude_threshold)
+
+ # Calculates the average teager value.
+ teager_value = average_teager_value(
+ signal[int(start_index):int(end_index)], average_amplitude)
+
+ # Finds out the noise level.
+ noise = noise_level(average_amplitude, dominant_frequency, rate,
+ teager_value)
+
+ return {
+ 'artifacts': {
+ 'noise_before_playback': noise_before_playing,
+ 'noise_after_playback': noise_after_playing,
+ 'delay_during_playback': delays,
+ 'burst_during_playback': burst_time_points
+ },
+ 'volume_changes': volume_changes,
+ 'equivalent_noise_level': noise
+ }
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py
new file mode 100644
index 0000000..ad41759
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py
@@ -0,0 +1,555 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Audio Analysis tool to analyze wave file and detect artifacts."""
+
+import argparse
+import collections
+import json
+import logging
+import math
+import numpy
+import os
+import pprint
+import subprocess
+import tempfile
+import wave
+
+import acts_contrib.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+import acts_contrib.test_utils.audio_analysis_lib.audio_data as audio_data
+import acts_contrib.test_utils.audio_analysis_lib.audio_quality_measurement as \
+ audio_quality_measurement
+
+# Holder for quality parameters used in audio_quality_measurement module.
+QualityParams = collections.namedtuple('QualityParams', [
+ 'block_size_secs', 'frequency_error_threshold',
+ 'delay_amplitude_threshold', 'noise_amplitude_threshold',
+ 'burst_amplitude_threshold'
+])
+
+DEFAULT_QUALITY_BLOCK_SIZE_SECS = 0.0015
+DEFAULT_BURST_AMPLITUDE_THRESHOLD = 1.4
+DEFAULT_DELAY_AMPLITUDE_THRESHOLD = 0.6
+DEFAULT_FREQUENCY_ERROR_THRESHOLD = 0.5
+DEFAULT_NOISE_AMPLITUDE_THRESHOLD = 0.5
+
+
+class WaveFileException(Exception):
+ """Error in WaveFile."""
+ pass
+
+
+class WaveFormatExtensibleException(Exception):
+ """Wave file is in WAVE_FORMAT_EXTENSIBLE format which is not supported."""
+ pass
+
+
+class WaveFile(object):
+ """Class which handles wave file reading.
+
+ Properties:
+ raw_data: audio_data.AudioRawData object for data in wave file.
+ rate: sampling rate.
+
+ """
+
+ def __init__(self, filename):
+ """Inits a wave file.
+
+ Args:
+ filename: file name of the wave file.
+
+ """
+ self.raw_data = None
+ self.rate = None
+
+ self._wave_reader = None
+ self._n_channels = None
+ self._sample_width_bits = None
+ self._n_frames = None
+ self._binary = None
+
+ try:
+ self._read_wave_file(filename)
+ except WaveFormatExtensibleException:
+ logging.warning(
+ 'WAVE_FORMAT_EXTENSIBLE is not supproted. '
+ 'Try command "sox in.wav -t wavpcm out.wav" to convert '
+ 'the file to WAVE_FORMAT_PCM format.')
+ self._convert_and_read_wav_file(filename)
+
+ def _convert_and_read_wav_file(self, filename):
+ """Converts the wav file and read it.
+
+ Converts the file into WAVE_FORMAT_PCM format using sox command and
+ reads its content.
+
+ Args:
+ filename: The wave file to be read.
+
+ Raises:
+ RuntimeError: sox is not installed.
+
+ """
+ # Checks if sox is installed.
+ try:
+ subprocess.check_output(['sox', '--version'])
+ except:
+ raise RuntimeError('sox command is not installed. '
+ 'Try sudo apt-get install sox')
+
+ with tempfile.NamedTemporaryFile(suffix='.wav') as converted_file:
+ command = ['sox', filename, '-t', 'wavpcm', converted_file.name]
+ logging.debug('Convert the file using sox: %s', command)
+ subprocess.check_call(command)
+ self._read_wave_file(converted_file.name)
+
+ def _read_wave_file(self, filename):
+ """Reads wave file header and samples.
+
+ Args:
+ filename: The wave file to be read.
+
+ @raises WaveFormatExtensibleException: Wave file is in
+ WAVE_FORMAT_EXTENSIBLE format.
+ @raises WaveFileException: Wave file format is not supported.
+
+ """
+ try:
+ self._wave_reader = wave.open(filename, 'r')
+ self._read_wave_header()
+ self._read_wave_binary()
+ except wave.Error as e:
+ if 'unknown format: 65534' in str(e):
+ raise WaveFormatExtensibleException()
+ else:
+ logging.exception('Unsupported wave format')
+ raise WaveFileException()
+ finally:
+ if self._wave_reader:
+ self._wave_reader.close()
+
+ def _read_wave_header(self):
+ """Reads wave file header.
+
+ @raises WaveFileException: wave file is compressed.
+
+ """
+ # Header is a tuple of
+ # (nchannels, sampwidth, framerate, nframes, comptype, compname).
+ header = self._wave_reader.getparams()
+ logging.debug('Wave header: %s', header)
+
+ self._n_channels = header[0]
+ self._sample_width_bits = header[1] * 8
+ self.rate = header[2]
+ self._n_frames = header[3]
+ comptype = header[4]
+ compname = header[5]
+
+ if comptype != 'NONE' or compname != 'not compressed':
+ raise WaveFileException('Can not support compressed wav file.')
+
+ def _read_wave_binary(self):
+ """Reads in samples in wave file."""
+ self._binary = self._wave_reader.readframes(self._n_frames)
+ format_str = 'S%d_LE' % self._sample_width_bits
+ self.raw_data = audio_data.AudioRawData(
+ binary=self._binary,
+ channel=self._n_channels,
+ sample_format=format_str)
+
+
+class QualityCheckerError(Exception):
+ """Error in QualityChecker."""
+ pass
+
+
+class CompareFailure(QualityCheckerError):
+ """Exception when frequency comparison fails."""
+ pass
+
+
+class QualityFailure(QualityCheckerError):
+ """Exception when quality check fails."""
+ pass
+
+
+class QualityChecker(object):
+ """Quality checker controls the flow of checking quality of raw data."""
+
+ def __init__(self, raw_data, rate):
+ """Inits a quality checker.
+
+ Args:
+ raw_data: An audio_data.AudioRawData object.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+ """
+ self._raw_data = raw_data
+ self._rate = rate
+ self._spectrals = []
+ self._quality_result = []
+
+ def do_spectral_analysis(self, ignore_high_freq, check_quality,
+ quality_params):
+ """Gets the spectral_analysis result.
+
+ Args:
+ ignore_high_freq: Ignore high frequencies above this threshold.
+ check_quality: Check quality of each channel.
+ quality_params: A QualityParams object for quality measurement.
+
+ """
+ self.has_data()
+ for channel_idx in range(self._raw_data.channel):
+ signal = self._raw_data.channel_data[channel_idx]
+ max_abs = max(numpy.abs(signal))
+ logging.debug('Channel %d max abs signal: %f', channel_idx,
+ max_abs)
+ if max_abs == 0:
+ logging.info('No data on channel %d, skip this channel',
+ channel_idx)
+ continue
+
+ saturate_value = audio_data.get_maximum_value_from_sample_format(
+ self._raw_data.sample_format)
+ normalized_signal = audio_analysis.normalize_signal(
+ signal, saturate_value)
+ logging.debug('saturate_value: %f', saturate_value)
+ logging.debug('max signal after normalized: %f',
+ max(normalized_signal))
+ spectral = audio_analysis.spectral_analysis(
+ normalized_signal, self._rate)
+
+ logging.debug('Channel %d spectral:\n%s', channel_idx,
+ pprint.pformat(spectral))
+
+ # Ignore high frequencies above the threshold.
+ spectral = [(f, c) for (f, c) in spectral if f < ignore_high_freq]
+
+ logging.info('Channel %d spectral after ignoring high frequencies '
+ 'above %f:\n%s', channel_idx, ignore_high_freq,
+ pprint.pformat(spectral))
+
+ try:
+ if check_quality:
+ quality = audio_quality_measurement.quality_measurement(
+ signal=normalized_signal,
+ rate=self._rate,
+ dominant_frequency=spectral[0][0],
+ block_size_secs=quality_params.block_size_secs,
+ frequency_error_threshold=quality_params.
+ frequency_error_threshold,
+ delay_amplitude_threshold=quality_params.
+ delay_amplitude_threshold,
+ noise_amplitude_threshold=quality_params.
+ noise_amplitude_threshold,
+ burst_amplitude_threshold=quality_params.
+ burst_amplitude_threshold)
+
+ logging.debug('Channel %d quality:\n%s', channel_idx,
+ pprint.pformat(quality))
+ self._quality_result.append(quality)
+ self._spectrals.append(spectral)
+ except Exception as error:
+ logging.warning(
+ "Failed to analyze channel {} with error: {}".format(
+ channel_idx, error))
+
+ def has_data(self):
+ """Checks if data has been set.
+
+ Raises:
+ QualityCheckerError: if data or rate is not set yet.
+
+ """
+ if not self._raw_data or not self._rate:
+ raise QualityCheckerError('Data and rate is not set yet')
+
+ def check_freqs(self, expected_freqs, freq_threshold):
+ """Checks the dominant frequencies in the channels.
+
+ Args:
+ expected_freq: A list of frequencies. If frequency is 0, it
+ means this channel should be ignored.
+ freq_threshold: The difference threshold to compare two
+ frequencies.
+
+ """
+ logging.debug('expected_freqs: %s', expected_freqs)
+ for idx, expected_freq in enumerate(expected_freqs):
+ if expected_freq == 0:
+ continue
+ if not self._spectrals[idx]:
+ raise CompareFailure(
+ 'Failed at channel %d: no dominant frequency' % idx)
+ dominant_freq = self._spectrals[idx][0][0]
+ if abs(dominant_freq - expected_freq) > freq_threshold:
+ raise CompareFailure(
+ 'Failed at channel %d: %f is too far away from %f' %
+ (idx, dominant_freq, expected_freq))
+
+ def check_quality(self):
+ """Checks the quality measurement results on each channel.
+
+ Raises:
+ QualityFailure when there is artifact.
+
+ """
+ error_msgs = []
+
+ for idx, quality_res in enumerate(self._quality_result):
+ artifacts = quality_res['artifacts']
+ if artifacts['noise_before_playback']:
+ error_msgs.append('Found noise before playback: %s' %
+ (artifacts['noise_before_playback']))
+ if artifacts['noise_after_playback']:
+ error_msgs.append('Found noise after playback: %s' %
+ (artifacts['noise_after_playback']))
+ if artifacts['delay_during_playback']:
+ error_msgs.append('Found delay during playback: %s' %
+ (artifacts['delay_during_playback']))
+ if artifacts['burst_during_playback']:
+ error_msgs.append('Found burst during playback: %s' %
+ (artifacts['burst_during_playback']))
+ if error_msgs:
+ raise QualityFailure('Found bad quality: %s',
+ '\n'.join(error_msgs))
+
+ def dump(self, output_file):
+ """Dumps the result into a file in json format.
+
+ Args:
+ output_file: A file path to dump spectral and quality
+ measurement result of each channel.
+
+ """
+ dump_dict = {
+ 'spectrals': self._spectrals,
+ 'quality_result': self._quality_result
+ }
+ with open(output_file, 'w') as f:
+ json.dump(dump_dict, f)
+
+ def has_data(self):
+ """Checks if data has been set.
+
+ Raises:
+ QualityCheckerError: if data or rate is not set yet.
+
+ """
+ if not self._raw_data or not self._rate:
+ raise QualityCheckerError('Data and rate is not set yet')
+
+ def check_freqs(self, expected_freqs, freq_threshold):
+ """Checks the dominant frequencies in the channels.
+
+ Args:
+ expected_freq: A list of frequencies. If frequency is 0, it
+ means this channel should be ignored.
+ freq_threshold: The difference threshold to compare two
+ frequencies.
+
+ """
+ logging.debug('expected_freqs: %s', expected_freqs)
+ for idx, expected_freq in enumerate(expected_freqs):
+ if expected_freq == 0:
+ continue
+ if not self._spectrals[idx]:
+ raise CompareFailure(
+ 'Failed at channel %d: no dominant frequency' % idx)
+ dominant_freq = self._spectrals[idx][0][0]
+ if abs(dominant_freq - expected_freq) > freq_threshold:
+ raise CompareFailure(
+ 'Failed at channel %d: %f is too far away from %f' %
+ (idx, dominant_freq, expected_freq))
+
+ def check_quality(self):
+ """Checks the quality measurement results on each channel.
+
+ Raises:
+ QualityFailure when there is artifact.
+
+ """
+ error_msgs = []
+
+ for idx, quality_res in enumerate(self._quality_result):
+ artifacts = quality_res['artifacts']
+ if artifacts['noise_before_playback']:
+ error_msgs.append('Found noise before playback: %s' %
+ (artifacts['noise_before_playback']))
+ if artifacts['noise_after_playback']:
+ error_msgs.append('Found noise after playback: %s' %
+ (artifacts['noise_after_playback']))
+ if artifacts['delay_during_playback']:
+ error_msgs.append('Found delay during playback: %s' %
+ (artifacts['delay_during_playback']))
+ if artifacts['burst_during_playback']:
+ error_msgs.append('Found burst during playback: %s' %
+ (artifacts['burst_during_playback']))
+ if error_msgs:
+ raise QualityFailure('Found bad quality: %s',
+ '\n'.join(error_msgs))
+
+ def dump(self, output_file):
+ """Dumps the result into a file in json format.
+
+ Args:
+ output_file: A file path to dump spectral and quality
+ measurement result of each channel.
+
+ """
+ dump_dict = {
+ 'spectrals': self._spectrals,
+ 'quality_result': self._quality_result
+ }
+ with open(output_file, 'w') as f:
+ json.dump(dump_dict, f)
+
+
+class CheckQualityError(Exception):
+ """Error in check_quality main function."""
+ pass
+
+
+def read_audio_file(filename, channel, bit_width, rate):
+ """Reads audio file.
+
+ Args:
+ filename: The wav or raw file to check.
+ channel: For raw file. Number of channels.
+ bit_width: For raw file. Bit width of a sample.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+
+ Returns:
+ A tuple (raw_data, rate) where raw_data is audio_data.AudioRawData, rate
+ is sampling rate.
+
+ """
+ if filename.endswith('.wav'):
+ wavefile = WaveFile(filename)
+ raw_data = wavefile.raw_data
+ rate = wavefile.rate
+ elif filename.endswith('.raw'):
+ binary = None
+ with open(filename, 'rb') as f:
+ binary = f.read()
+ raw_data = audio_data.AudioRawData(
+ binary=binary, channel=channel, sample_format='S%d_LE' % bit_width)
+ else:
+ raise CheckQualityError(
+ 'File format for %s is not supported' % filename)
+
+ return raw_data, rate
+
+
+def get_quality_params(
+ quality_block_size_secs, quality_frequency_error_threshold,
+ quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
+ quality_burst_amplitude_threshold):
+ """Gets quality parameters in arguments.
+
+ Args:
+ quality_block_size_secs: Input block size in seconds.
+ quality_frequency_error_threshold: Input the frequency error
+ threshold.
+ quality_delay_amplitude_threshold: Input the delay aplitutde
+ threshold.
+ quality_noise_amplitude_threshold: Input the noise aplitutde
+ threshold.
+ quality_burst_amplitude_threshold: Input the burst aplitutde
+ threshold.
+
+ Returns:
+ A QualityParams object.
+
+ """
+ quality_params = QualityParams(
+ block_size_secs=quality_block_size_secs,
+ frequency_error_threshold=quality_frequency_error_threshold,
+ delay_amplitude_threshold=quality_delay_amplitude_threshold,
+ noise_amplitude_threshold=quality_noise_amplitude_threshold,
+ burst_amplitude_threshold=quality_burst_amplitude_threshold)
+
+ return quality_params
+
+
+def quality_analysis(
+ filename,
+ output_file,
+ bit_width,
+ rate,
+ channel,
+ freqs=None,
+ freq_threshold=5,
+ ignore_high_freq=5000,
+ spectral_only=False,
+ quality_block_size_secs=DEFAULT_QUALITY_BLOCK_SIZE_SECS,
+ quality_burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
+ quality_delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
+ quality_frequency_error_threshold=DEFAULT_FREQUENCY_ERROR_THRESHOLD,
+ quality_noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
+):
+ """ Runs various functions to measure audio quality base on user input.
+
+ Args:
+ filename: The wav or raw file to check.
+ output_file: Output file to dump analysis result in JSON format.
+ bit_width: For raw file. Bit width of a sample.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ channel: For raw file. Number of channels.
+ freqs: Expected frequencies in the channels.
+ freq_threshold: Frequency difference threshold in Hz.
+ ignore_high_freq: Frequency threshold in Hz to be ignored for high
+ frequency. Default is 5KHz
+ spectral_only: Only do spectral analysis on each channel.
+ quality_block_size_secs: Input block size in seconds.
+ quality_frequency_error_threshold: Input the frequency error
+ threshold.
+ quality_delay_amplitude_threshold: Input the delay aplitutde
+ threshold.
+ quality_noise_amplitude_threshold: Input the noise aplitutde
+ threshold.
+ quality_burst_amplitude_threshold: Input the burst aplitutde
+ threshold.
+ """
+
+ raw_data, rate = read_audio_file(filename, channel, bit_width, rate)
+
+ checker = QualityChecker(raw_data, rate)
+
+ quality_params = get_quality_params(
+ quality_block_size_secs, quality_frequency_error_threshold,
+ quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
+ quality_burst_amplitude_threshold)
+
+ checker.do_spectral_analysis(
+ ignore_high_freq=ignore_high_freq,
+ check_quality=(not spectral_only),
+ quality_params=quality_params)
+
+ checker.dump(output_file)
+
+ if freqs:
+ checker.check_freqs(freqs, freq_threshold)
+
+ if not spectral_only:
+ checker.check_quality()
+ logging.debug("Audio analysis completed.")
diff --git a/acts_tests/acts_contrib/test_utils/bt/A2dpBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/A2dpBaseTest.py
new file mode 100644
index 0000000..ba4cec3
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/A2dpBaseTest.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Stream music through connected device from phone test implementation."""
+import acts
+import os
+import shutil
+import time
+
+import acts_contrib.test_utils.coex.audio_test_utils as atu
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+from acts import asserts
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+
+PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music'
+INIT_ATTEN = 0
+WAIT_TIME = 1
+
+
+class A2dpBaseTest(BluetoothBaseTest):
+ """Stream audio file over desired Bluetooth codec configurations.
+
+ Audio file should be a sine wave. Other audio files will not work for the
+ test analysis metrics.
+
+ Device under test is Android phone, connected to headset with a controller
+ that can generate a BluetoothHandsfreeAbstractDevice from test_utils.
+ abstract_devices.bluetooth_handsfree_abstract_device.
+ BuetoothHandsfreeAbstractDeviceFactory.
+ """
+ def setup_class(self):
+
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ req_params = ['audio_params', 'music_files']
+ #'audio_params' is a dict, contains the audio device type, audio streaming
+ #settings such as volumn, duration, audio recording parameters such as
+ #channel, sampling rate/width, and thdn parameters for audio processing
+ self.unpack_userparams(req_params)
+ # Find music file and push it to the dut
+ music_src = self.music_files[0]
+ music_dest = PHONE_MUSIC_FILE_DIRECTORY
+ success = self.dut.push_system_file(music_src, music_dest)
+ if success:
+ self.music_file = os.path.join(PHONE_MUSIC_FILE_DIRECTORY,
+ os.path.basename(music_src))
+ # Initialize media_control class
+ self.media = btutils.MediaControlOverSl4a(self.dut, self.music_file)
+ # Set attenuator to minimum attenuation
+ if hasattr(self, 'attenuators'):
+ self.attenuator = self.attenuators[0]
+ self.attenuator.set_atten(INIT_ATTEN)
+ # Create the BTOE(Bluetooth-Other-End) device object
+ bt_devices = self.user_params.get('bt_devices', [])
+ if bt_devices:
+ attr, idx = bt_devices.split(':')
+ self.bt_device_controller = getattr(self, attr)[int(idx)]
+ self.bt_device = bt_factory().generate(self.bt_device_controller)
+ else:
+ self.log.error('No BT devices config is provided!')
+
+ def teardown_class(self):
+
+ super().teardown_class()
+ if hasattr(self, 'media'):
+ self.media.stop()
+ if hasattr(self, 'attenuator'):
+ self.attenuator.set_atten(INIT_ATTEN)
+ self.dut.droid.bluetoothFactoryReset()
+ self.bt_device.reset()
+ self.bt_device.power_off()
+ btutils.disable_bluetooth(self.dut.droid)
+
+ def setup_test(self):
+
+ super().setup_test()
+ # Initialize audio capture devices
+ self.audio_device = atu.get_audio_capture_device(
+ self.bt_device_controller, self.audio_params)
+ # Reset BT to factory defaults
+ self.dut.droid.bluetoothFactoryReset()
+ self.bt_device.reset()
+ self.bt_device.power_on()
+ btutils.enable_bluetooth(self.dut.droid, self.dut.ed)
+ btutils.connect_phone_to_headset(self.dut, self.bt_device, 60)
+ vol = self.dut.droid.getMaxMediaVolume() * self.audio_params['volume']
+ self.dut.droid.setMediaVolume(0)
+ time.sleep(1)
+ self.dut.droid.setMediaVolume(int(vol))
+
+ def teardown_test(self):
+
+ super().teardown_test()
+ self.dut.droid.bluetoothFactoryReset()
+ self.media.stop()
+ # Set Attenuator to the initial attenuation
+ if hasattr(self, 'attenuator'):
+ self.attenuator.set_atten(INIT_ATTEN)
+ self.bt_device.reset()
+ self.bt_device.power_off()
+ btutils.disable_bluetooth(self.dut.droid)
+
+ def play_and_record_audio(self, duration):
+ """Play and record audio for a set duration.
+
+ Args:
+ duration: duration in seconds for music playing
+ Returns:
+ audio_captured: captured audio file path
+ """
+
+ self.log.info('Play and record audio for {} second'.format(duration))
+ self.media.play()
+ self.audio_device.start()
+ time.sleep(duration + WAIT_TIME)
+ audio_captured = self.audio_device.stop()
+ self.media.stop()
+ self.log.info('Audio play and record stopped')
+ asserts.assert_true(audio_captured, 'Audio not recorded')
+ return audio_captured
+
+ def _get_bt_link_metrics(self):
+ """Get bt link metrics such as rssi and tx pwls.
+
+ Returns:
+ rssi_master: master rssi
+ pwl_master: master tx pwl
+ rssi_slave: slave rssi
+ """
+
+ self.media.play()
+ # Get master rssi and power level
+ rssi_master = btutils.get_bt_metric(self.dut)['rssi']
+ pwl_master = btutils.get_bt_metric(self.dut)['pwlv']
+ # Get slave rssi if possible
+ if isinstance(self.bt_device_controller,
+ acts.controllers.android_device.AndroidDevice):
+ rssi_slave = btutils.get_bt_rssi(self.bt_device_controller)
+ else:
+ rssi_slave = None
+ self.media.stop()
+ return [rssi_master, pwl_master, rssi_slave]
+
+ def run_thdn_analysis(self, audio_captured, tag):
+ """Calculate Total Harmonic Distortion plus Noise for latest recording.
+
+ Store result in self.metrics.
+
+ Args:
+ audio_captured: the captured audio file
+ Returns:
+ thdn: thdn value in a list
+ """
+ # Calculate Total Harmonic Distortion + Noise
+ audio_result = atu.AudioCaptureResult(audio_captured,
+ self.audio_params)
+ thdn = audio_result.THDN(**self.audio_params['thdn_params'])
+ file_name = tag + os.path.basename(audio_result.path)
+ file_new = os.path.join(os.path.dirname(audio_result.path), file_name)
+ shutil.copyfile(audio_result.path, file_new)
+ for ch_no, t in enumerate(thdn):
+ self.log.info('THD+N for channel %s: %.4f%%' % (ch_no, t * 100))
+ return thdn
+
+ def run_anomaly_detection(self, audio_captured):
+ """Detect anomalies in latest recording.
+
+ Store result in self.metrics.
+
+ Args:
+ audio_captured: the captured audio file
+ Returns:
+ anom: anom detected in the captured file
+ """
+ # Detect Anomalies
+ audio_result = atu.AudioCaptureResult(audio_captured)
+ anom = audio_result.detect_anomalies(
+ **self.audio_params['anomaly_params'])
+ num_anom = 0
+ for ch_no, anomalies in enumerate(anom):
+ if anomalies:
+ for anomaly in anomalies:
+ num_anom += 1
+ start, end = anomaly
+ self.log.warning(
+ 'Anomaly on channel {} at {}:{}. Duration '
+ '{} sec'.format(ch_no, start // 60, start % 60,
+ end - start))
+ else:
+ self.log.info('%i anomalies detected.' % num_anom)
+ return anom
diff --git a/acts_tests/acts_contrib/test_utils/bt/AvrcpBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/AvrcpBaseTest.py
new file mode 100644
index 0000000..9109ae3
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/AvrcpBaseTest.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Perform base Avrcp command from headset to dut"""
+import time
+import os
+import queue
+
+from acts import asserts
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as Factory
+from acts_contrib.test_utils.bt.simulated_carkit_device import SimulatedCarkitDevice
+from acts_contrib.test_utils.bt.bt_test_utils import connect_phone_to_headset
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.car.car_media_utils import EVENT_PLAY_RECEIVED
+from acts_contrib.test_utils.car.car_media_utils import EVENT_PAUSE_RECEIVED
+from acts_contrib.test_utils.car.car_media_utils import EVENT_SKIP_NEXT_RECEIVED
+from acts_contrib.test_utils.car.car_media_utils import EVENT_SKIP_PREV_RECEIVED
+from acts_contrib.test_utils.car.car_media_utils import CMD_MEDIA_PLAY
+from acts_contrib.test_utils.car.car_media_utils import CMD_MEDIA_PAUSE
+
+ADB_FILE_EXISTS = 'test -e %s && echo True'
+DEFAULT_TIMEOUT = 5
+EVENT_TIMEOUT = 1
+
+
+class AvrcpBaseTest(BluetoothBaseTest):
+ def __init__(self, configs):
+ super(AvrcpBaseTest, self).__init__(configs)
+ self.dut = self.android_devices[0]
+ serial = self.user_params['simulated_carkit_device']
+ controller = SimulatedCarkitDevice(serial)
+ self.controller = Factory().generate(controller)
+
+ self.phone_music_files = []
+ self.host_music_files = []
+ for music_file in self.user_params['music_file_names']:
+ self.phone_music_files.append(os.path.join(
+ self.user_params['phone_music_file_dir'], music_file))
+ self.host_music_files.append(os.path.join(
+ self.user_params['host_music_file_dir'], music_file))
+
+ self.ensure_phone_has_music_file()
+
+ def setup_class(self):
+ super().setup_class()
+ self.controller.power_on()
+ time.sleep(DEFAULT_TIMEOUT)
+
+ def teardown_class(self):
+ super().teardown_class()
+ self.dut.droid.mediaPlayStop()
+ self.controller.destroy()
+
+ def setup_test(self):
+ self.dut.droid.bluetoothMediaPhoneSL4AMBSStart()
+ time.sleep(DEFAULT_TIMEOUT)
+
+ self.dut.droid.bluetoothStartPairingHelper(True)
+ if not connect_phone_to_headset(self.dut, self.controller, 600):
+ asserts.fail('Not able to connect to hands-free device')
+
+ #make sure SL4AMBS is active MediaSession
+ self.dut.droid.bluetoothMediaHandleMediaCommandOnPhone(CMD_MEDIA_PLAY)
+ time.sleep(0.5)
+ self.dut.droid.bluetoothMediaHandleMediaCommandOnPhone(CMD_MEDIA_PAUSE)
+
+ def teardown_test(self):
+ self.dut.droid.bluetoothMediaPhoneSL4AMBSStop()
+
+ def ensure_phone_has_music_file(self):
+ """Make sure music file (based on config values) is on the phone."""
+ for host_file, phone_file in zip(self.host_music_files,
+ self.phone_music_files):
+ if self.dut.adb.shell(ADB_FILE_EXISTS % phone_file):
+ self.log.info(
+ 'Music file {} already on phone. Skipping file transfer.'
+ .format(host_file))
+ else:
+ self.dut.adb.push(host_file, phone_file)
+ has_file = self.dut.adb.shell(
+ ADB_FILE_EXISTS % phone_file)
+ if not has_file:
+ self.log.error(
+ 'Audio file {} not pushed to phone.'.format(host_file))
+ self.log.info('Music file successfully pushed to phone.')
+
+ def play_from_controller(self):
+ self.dut.ed.clear_all_events()
+ self.controller.play()
+ try:
+ self.dut.ed.pop_event(EVENT_PLAY_RECEIVED, EVENT_TIMEOUT)
+ except queue.Empty as e:
+ asserts.fail('{} Event Not received'.format(EVENT_PLAY_RECEIVED))
+ self.log.info('Event Received : {}'.format(EVENT_PLAY_RECEIVED))
+
+ def pause_from_controller(self):
+ self.dut.ed.clear_all_events()
+ self.controller.pause()
+ try:
+ self.dut.ed.pop_event(EVENT_PAUSE_RECEIVED, EVENT_TIMEOUT)
+ except queue.Empty as e:
+ asserts.fail('{} Event Not received'.format(EVENT_PAUSE_RECEIVED))
+ self.log.info('Event Received : {}'.format(EVENT_PAUSE_RECEIVED))
+
+ def skip_next_from_controller(self):
+ self.dut.ed.clear_all_events()
+ self.controller.next_track()
+ try:
+ self.dut.ed.pop_event(EVENT_SKIP_NEXT_RECEIVED, EVENT_TIMEOUT)
+ except queue.Empty as e:
+ asserts.fail('{} Event Not '
+ 'received'.format(EVENT_SKIP_NEXT_RECEIVED))
+ self.log.info('Event Received : {}'.format(EVENT_SKIP_NEXT_RECEIVED))
+
+ def skip_prev_from_controller(self):
+ self.dut.ed.clear_all_events()
+ self.controller.previous_track()
+ try:
+ self.dut.ed.pop_event(EVENT_SKIP_PREV_RECEIVED, EVENT_TIMEOUT)
+ except queue.Empty as e:
+ asserts.fail('{} Event Not '
+ 'received'.format(EVENT_SKIP_PREV_RECEIVED))
+ self.log.info('Event Received : {}'.format(EVENT_SKIP_PREV_RECEIVED))
diff --git a/acts_tests/acts_contrib/test_utils/bt/BleEnum.py b/acts_tests/acts_contrib/test_utils/bt/BleEnum.py
new file mode 100644
index 0000000..00763aa
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BleEnum.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from enum import Enum
+
+
+class ScanSettingsCallbackType(Enum):
+ CALLBACK_TYPE_ALL_MATCHES = 1
+ CALLBACK_TYPE_FIRST_MATCH = 2
+ CALLBACK_TYPE_MATCH_LOST = 4
+ CALLBACK_TYPE_FOUND_AND_LOST = 6
+
+
+class ScanSettingsMatchMode(Enum):
+ AGGRESIVE = 1
+ STICKY = 2
+
+
+class ScanSettingsMatchNum(Enum):
+ MATCH_NUM_ONE_ADVERTISEMENT = 1
+ MATCH_NUM_FEW_ADVERTISEMENT = 2
+ MATCH_NUM_MAX_ADVERTISEMENT = 3
+
+
+class ScanSettingsScanResultType(Enum):
+ SCAN_RESULT_TYPE_FULL = 0
+ SCAN_RESULT_TYPE_ABBREVIATED = 1
+
+
+class ScanSettingsScanMode(Enum):
+ SCAN_MODE_OPPORTUNISTIC = -1
+ SCAN_MODE_LOW_POWER = 0
+ SCAN_MODE_BALANCED = 1
+ SCAN_MODE_LOW_LATENCY = 2
+
+class ScanSettingsReportDelaySeconds(Enum):
+ MIN = 0
+ MAX = 9223372036854775807
+
+class ScanSettingsPhy(Enum):
+ PHY_LE_1M = 1
+ PHY_LE_CODED = 3
+ PHY_LE_ALL_SUPPORTED = 255
+
+class AdvertiseSettingsAdvertiseType(Enum):
+ ADVERTISE_TYPE_NON_CONNECTABLE = 0
+ ADVERTISE_TYPE_CONNECTABLE = 1
+
+
+class AdvertiseSettingsAdvertiseMode(Enum):
+ ADVERTISE_MODE_LOW_POWER = 0
+ ADVERTISE_MODE_BALANCED = 1
+ ADVERTISE_MODE_LOW_LATENCY = 2
+
+
+class AdvertiseSettingsAdvertiseTxPower(Enum):
+ ADVERTISE_TX_POWER_ULTRA_LOW = 0
+ ADVERTISE_TX_POWER_LOW = 1
+ ADVERTISE_TX_POWER_MEDIUM = 2
+ ADVERTISE_TX_POWER_HIGH = 3
+
+
+class JavaInteger(Enum):
+ MIN = -2147483648
+ MAX = 2147483647
+
+
+class Uuids(Enum):
+ P_Service = "0000feef-0000-1000-8000-00805f9b34fb"
+ HR_SERVICE = "0000180d-0000-1000-8000-00805f9b34fb"
+
+
+class AdvertiseErrorCode(Enum):
+ DATA_TOO_LARGE = 1
+ TOO_MANY_ADVERTISERS = 2
+ ADVERTISE_ALREADY_STARTED = 3
+ BLUETOOTH_INTERNAL_FAILURE = 4
+ FEATURE_NOT_SUPPORTED = 5
+
+
+class BluetoothAdapterState(Enum):
+ STATE_OFF = 10
+ STATE_TURNING_ON = 11
+ STATE_ON = 12
+ STATE_TURNING_OFF = 13
+ STATE_BLE_TURNING_ON = 14
+ STATE_BLE_ON = 15
+ STATE_BLE_TURNING_OFF = 16
diff --git a/acts_tests/acts_contrib/test_utils/bt/BluetoothBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BluetoothBaseTest.py
new file mode 100644
index 0000000..a531bea
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BluetoothBaseTest.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+ Base Class for Defining Common Bluetooth Test Functionality
+"""
+
+import threading
+import time
+import traceback
+import os
+from acts.base_test import BaseTestClass
+from acts.signals import TestSignal
+from acts.utils import dump_string_to_file
+
+from acts.libs.proto.proto_utils import parse_proto_to_ascii
+from acts_contrib.test_utils.bt.bt_metrics_utils import get_bluetooth_metrics
+from acts_contrib.test_utils.bt.bt_test_utils import get_device_selector_dictionary
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.ble_lib import BleLib
+from acts_contrib.test_utils.bt.bta_lib import BtaLib
+from acts_contrib.test_utils.bt.config_lib import ConfigLib
+from acts_contrib.test_utils.bt.gattc_lib import GattClientLib
+from acts_contrib.test_utils.bt.gatts_lib import GattServerLib
+from acts_contrib.test_utils.bt.rfcomm_lib import RfcommLib
+from acts_contrib.test_utils.bt.shell_commands_lib import ShellCommands
+
+
+class BluetoothBaseTest(BaseTestClass):
+ DEFAULT_TIMEOUT = 10
+ start_time = 0
+ timer_list = []
+
+ def collect_bluetooth_manager_metrics_logs(self, ads, test_name):
+ """
+ Collect Bluetooth metrics logs, save an ascii log to disk and return
+ both binary and ascii logs to caller
+ :param ads: list of active Android devices
+ :return: List of binary metrics logs,
+ List of ascii metrics logs
+ """
+ bluetooth_logs = []
+ bluetooth_logs_ascii = []
+ for ad in ads:
+ serial = ad.serial
+ out_name = "{}_{}_{}".format(serial, test_name,
+ "bluetooth_metrics.txt")
+ bluetooth_log = get_bluetooth_metrics(ad)
+ bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
+ dump_string_to_file(bluetooth_log_ascii,
+ os.path.join(ad.metrics_path, out_name))
+ bluetooth_logs.append(bluetooth_log)
+ bluetooth_logs_ascii.append(bluetooth_log_ascii)
+ return bluetooth_logs, bluetooth_logs_ascii
+
+ # Use for logging in the test cases to facilitate
+ # faster log lookup and reduce ambiguity in logging.
+ @staticmethod
+ def bt_test_wrap(fn):
+ def _safe_wrap_test_case(self, *args, **kwargs):
+ test_id = "{}:{}:{}".format(self.__class__.__name__, fn.__name__,
+ time.time())
+ log_string = "[Test ID] {}".format(test_id)
+ self.log.info(log_string)
+ try:
+ for ad in self.android_devices:
+ ad.droid.logI("Started " + log_string)
+ result = fn(self, *args, **kwargs)
+ for ad in self.android_devices:
+ ad.droid.logI("Finished " + log_string)
+ if result is not True and "bt_auto_rerun" in self.user_params:
+ self.teardown_test()
+ log_string = "[Rerun Test ID] {}. 1st run failed.".format(
+ test_id)
+ self.log.info(log_string)
+ self.setup_test()
+ for ad in self.android_devices:
+ ad.droid.logI("Rerun Started " + log_string)
+ result = fn(self, *args, **kwargs)
+ if result is True:
+ self.log.info("Rerun passed.")
+ elif result is False:
+ self.log.info("Rerun failed.")
+ else:
+ # In the event that we have a non-bool or null
+ # retval, we want to clearly distinguish this in the
+ # logs from an explicit failure, though the test will
+ # still be considered a failure for reporting purposes.
+ self.log.info("Rerun indeterminate.")
+ result = False
+ return result
+ except TestSignal:
+ raise
+ except Exception as e:
+ self.log.error(traceback.format_exc())
+ self.log.error(str(e))
+ raise
+ return fn(self, *args, **kwargs)
+
+ return _safe_wrap_test_case
+
+ def setup_class(self):
+ super().setup_class()
+ for ad in self.android_devices:
+ self._setup_bt_libs(ad)
+ if 'preferred_device_order' in self.user_params:
+ prefered_device_order = self.user_params['preferred_device_order']
+ for i, ad in enumerate(self.android_devices):
+ if ad.serial in prefered_device_order:
+ index = prefered_device_order.index(ad.serial)
+ self.android_devices[i], self.android_devices[index] = \
+ self.android_devices[index], self.android_devices[i]
+
+ if "reboot_between_test_class" in self.user_params:
+ threads = []
+ for a in self.android_devices:
+ thread = threading.Thread(
+ target=self._reboot_device, args=([a]))
+ threads.append(thread)
+ thread.start()
+ for t in threads:
+ t.join()
+ if not setup_multiple_devices_for_bt_test(self.android_devices):
+ return False
+ self.device_selector = get_device_selector_dictionary(
+ self.android_devices)
+ if "bluetooth_proto_path" in self.user_params:
+ for ad in self.android_devices:
+ ad.metrics_path = os.path.join(ad.log_path, "BluetoothMetrics")
+ os.makedirs(ad.metrics_path, exist_ok=True)
+ # Clear metrics.
+ get_bluetooth_metrics(ad)
+ return True
+
+ def teardown_class(self):
+ if "bluetooth_proto_path" in self.user_params:
+ # Collect metrics here bassed off class name
+ bluetooth_logs, bluetooth_logs_ascii = \
+ self.collect_bluetooth_manager_metrics_logs(
+ self.android_devices, self.__class__.__name__)
+
+ def setup_test(self):
+ self.timer_list = []
+ for a in self.android_devices:
+ a.ed.clear_all_events()
+ a.droid.setScreenTimeout(500)
+ a.droid.wakeUpNow()
+ return True
+
+ def on_fail(self, test_name, begin_time):
+ self.log.debug(
+ "Test {} failed. Gathering bugreport and btsnoop logs".format(
+ test_name))
+ take_btsnoop_logs(self.android_devices, self, test_name)
+ self._take_bug_report(test_name, begin_time)
+ for _ in range(5):
+ if reset_bluetooth(self.android_devices):
+ break
+ else:
+ self.log.error("Failed to reset Bluetooth... retrying.")
+ return
+
+ def _get_time_in_milliseconds(self):
+ return int(round(time.time() * 1000))
+
+ def start_timer(self):
+ self.start_time = self._get_time_in_milliseconds()
+
+ def end_timer(self):
+ total_time = self._get_time_in_milliseconds() - self.start_time
+ self.timer_list.append(total_time)
+ self.start_time = 0
+ return total_time
+
+ def log_stats(self):
+ if self.timer_list:
+ self.log.info("Overall list {}".format(self.timer_list))
+ self.log.info("Average of list {}".format(
+ sum(self.timer_list) / float(len(self.timer_list))))
+ self.log.info("Maximum of list {}".format(max(self.timer_list)))
+ self.log.info("Minimum of list {}".format(min(self.timer_list)))
+ self.log.info("Total items in list {}".format(
+ len(self.timer_list)))
+ self.timer_list = []
+
+ def _setup_bt_libs(self, android_device):
+ # Bluetooth Low Energy library.
+ setattr(android_device, "ble", BleLib(
+ log=self.log, dut=android_device))
+ # Bluetooth Adapter library.
+ setattr(android_device, "bta", BtaLib(
+ log=self.log, dut=android_device))
+ # Bluetooth stack config library.
+ setattr(android_device, "config",
+ ConfigLib(log=self.log, dut=android_device))
+ # GATT Client library.
+ setattr(android_device, "gattc",
+ GattClientLib(log=self.log, dut=android_device))
+ # GATT Server library.
+ setattr(android_device, "gatts",
+ GattServerLib(log=self.log, dut=android_device))
+ # RFCOMM library.
+ setattr(android_device, "rfcomm",
+ RfcommLib(log=self.log, dut=android_device))
+ # Shell command library
+ setattr(android_device, "shell",
+ ShellCommands(log=self.log, dut=android_device))
+ # Setup Android Device feature list
+ setattr(android_device, "features",
+ android_device.adb.shell("pm list features").split("\n"))
diff --git a/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
new file mode 100644
index 0000000..303b961
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This is base class for tests that exercises different GATT procedures between two connected devices.
+Setup/Teardown methods take care of establishing connection, and doing GATT DB initialization/discovery.
+"""
+
+import os
+import time
+from queue import Empty
+
+from acts.keys import Config
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
+
+
+class BluetoothCarHfpBaseTest(BluetoothBaseTest):
+ DEFAULT_TIMEOUT = 15
+ ag_phone_number = ""
+ re_phone_number = ""
+
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ # HF : HandsFree (CarKit role)
+ self.hf = self.android_devices[0]
+ self.hf.log.info("Role set to HF (HandsFree Carkit role).")
+ # AG : Audio Gateway (Phone role)
+ self.ag = self.android_devices[1]
+ self.ag.log.info("Role set to AG (Audio Gateway Phone role).")
+ # RE : Remote Device (Phone being talked to role)
+ if len(self.android_devices) > 2:
+ self.re = self.android_devices[2]
+ self.re.log.info("Role set to RE (Remote device).")
+ else:
+ self.re = None
+ if len(self.android_devices) > 3:
+ self.re2 = self.android_devices[3]
+ self.re2.log.info("Role set to RE2 (Remote device 2).")
+ else:
+ self.re2 = None
+
+ def setup_class(self):
+ super(BluetoothCarHfpBaseTest, self).setup_class()
+ if not "sim_conf_file" in self.user_params.keys():
+ self.log.error("Missing mandatory user config \"sim_conf_file\"!")
+ return False
+ sim_conf_file = self.user_params["sim_conf_file"][0]
+ if not os.path.isfile(sim_conf_file):
+ sim_conf_file = os.path.join(
+ self.user_params[Config.key_config_path.value], sim_conf_file)
+ if not os.path.isfile(sim_conf_file):
+ self.log.error("Unable to load user config " + sim_conf_file +
+ " from test config file.")
+ return False
+ setup_droid_properties(self.log, self.ag, sim_conf_file)
+ self.ag_phone_number = get_phone_number(self.log, self.ag)
+ self.ag.log.info("ag tel: {}".format(self.ag_phone_number))
+ if self.re:
+ setup_droid_properties(self.log, self.re, sim_conf_file)
+ self.re_phone_number = get_phone_number(self.log, self.re)
+ self.re.log.info("re tel: {}".format(self.re_phone_number))
+ if self.re2:
+ setup_droid_properties(self.log, self.re2, sim_conf_file)
+ self.re2_phone_number = get_phone_number(self.log, self.re2)
+ self.re2.log.info("re2 tel: {}".format(self.re2_phone_number))
+ # Pair and connect the devices.
+ # Grace time inbetween stack state changes
+ time.sleep(5)
+ if not pair_pri_to_sec(
+ self.hf, self.ag, attempts=4, auto_confirm=False):
+ self.log.error("Failed to pair")
+ return False
+ return True
+
+ def setup_test(self):
+ if not super(BluetoothCarHfpBaseTest, self).setup_test():
+ return False
+ return ensure_phones_default_state(self.log, self.android_devices[1:])
+
+ def teardown_test(self):
+ if not super(BluetoothCarHfpBaseTest, self).teardown_test():
+ return False
+ return ensure_phones_default_state(self.log, self.android_devices[1:])
+
+ def on_fail(self, test_name, begin_time):
+ result = True
+ if not super(BluetoothCarHfpBaseTest, self).on_fail(test_name,
+ begin_time):
+ result = False
+ ensure_phones_default_state(self.log, self.android_devices[1:])
+ return result
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtEnum.py b/acts_tests/acts_contrib/test_utils/bt/BtEnum.py
new file mode 100644
index 0000000..b9fe6e2
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtEnum.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from enum import Enum
+from enum import IntEnum
+
+
+class BluetoothScanModeType(IntEnum):
+ STATE_OFF = -1
+ SCAN_MODE_NONE = 0
+ SCAN_MODE_CONNECTABLE = 1
+ SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3
+
+
+class BluetoothAdapterState(IntEnum):
+ STATE_OFF = 10
+ STATE_TURNING_ON = 11
+ STATE_ON = 12
+ STATE_TURNING_OFF = 13
+ STATE_BLE_TURNING_ON = 14
+ STATE_BLE_ON = 15
+ STATE_BLE_TURNING_OFF = 16
+
+
+class BluetoothProfile(IntEnum):
+ # Should be kept in sync with BluetoothProfile.java
+ HEADSET = 1
+ A2DP = 2
+ HEALTH = 3
+ INPUT_DEVICE = 4
+ PAN = 5
+ PBAP_SERVER = 6
+ GATT = 7
+ GATT_SERVER = 8
+ MAP = 9
+ SAP = 10
+ A2DP_SINK = 11
+ AVRCP_CONTROLLER = 12
+ HEADSET_CLIENT = 16
+ PBAP_CLIENT = 17
+ MAP_MCE = 18
+
+
+class RfcommUuid(Enum):
+ DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66"
+ BASE_UUID = "00000000-0000-1000-8000-00805F9B34FB"
+ SDP = "00000001-0000-1000-8000-00805F9B34FB"
+ UDP = "00000002-0000-1000-8000-00805F9B34FB"
+ RFCOMM = "00000003-0000-1000-8000-00805F9B34FB"
+ TCP = "00000004-0000-1000-8000-00805F9B34FB"
+ TCS_BIN = "00000005-0000-1000-8000-00805F9B34FB"
+ TCS_AT = "00000006-0000-1000-8000-00805F9B34FB"
+ ATT = "00000007-0000-1000-8000-00805F9B34FB"
+ OBEX = "00000008-0000-1000-8000-00805F9B34FB"
+ IP = "00000009-0000-1000-8000-00805F9B34FB"
+ FTP = "0000000A-0000-1000-8000-00805F9B34FB"
+ HTTP = "0000000C-0000-1000-8000-00805F9B34FB"
+ WSP = "0000000E-0000-1000-8000-00805F9B34FB"
+ BNEP = "0000000F-0000-1000-8000-00805F9B34FB"
+ UPNP = "00000010-0000-1000-8000-00805F9B34FB"
+ HIDP = "00000011-0000-1000-8000-00805F9B34FB"
+ HARDCOPY_CONTROL_CHANNEL = "00000012-0000-1000-8000-00805F9B34FB"
+ HARDCOPY_DATA_CHANNEL = "00000014-0000-1000-8000-00805F9B34FB"
+ HARDCOPY_NOTIFICATION = "00000016-0000-1000-8000-00805F9B34FB"
+ AVCTP = "00000017-0000-1000-8000-00805F9B34FB"
+ AVDTP = "00000019-0000-1000-8000-00805F9B34FB"
+ CMTP = "0000001B-0000-1000-8000-00805F9B34FB"
+ MCAP_CONTROL_CHANNEL = "0000001E-0000-1000-8000-00805F9B34FB"
+ MCAP_DATA_CHANNEL = "0000001F-0000-1000-8000-00805F9B34FB"
+ L2CAP = "00000100-0000-1000-8000-00805F9B34FB"
+
+
+class BluetoothProfileState(Enum):
+ # Should be kept in sync with BluetoothProfile#STATE_* constants.
+ STATE_DISCONNECTED = 0
+ STATE_CONNECTING = 1
+ STATE_CONNECTED = 2
+ STATE_DISCONNECTING = 3
+
+
+class BluetoothAccessLevel(Enum):
+ # Access Levels from BluetoothDevice.
+ ACCESS_ALLOWED = 1
+ ACCESS_DENIED = 2
+
+
+class BluetoothPriorityLevel(Enum):
+ # Priority levels as defined in BluetoothProfile.java.
+ PRIORITY_AUTO_CONNECT = 1000
+ PRIORITY_ON = 100
+ PRIORITY_OFF = 0
+ PRIORITY_UNDEFINED = -1
+
+class BluetoothA2dpCodecType(Enum):
+ SBC = 0
+ AAC = 1
+ APTX = 2
+ APTX_HD = 3
+ LDAC = 4
+ MAX = 5
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py
new file mode 100644
index 0000000..127289e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test script to automate the Bluetooth Audio Funhaus.
+"""
+from acts.keys import Config
+from acts_contrib.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.utils import bypass_setup_wizard
+from acts.utils import exe_cmd
+from acts.utils import sync_device_time
+import json
+import time
+import os
+
+BT_CONF_PATH = "/data/misc/bluedroid/bt_config.conf"
+
+
+class BtFunhausBaseTest(BtMetricsBaseTest):
+ """
+ Base class for Bluetooth A2DP audio tests, this class is in charge of
+ pushing link key to Android device so that it could be paired with remote
+ A2DP device, pushing music to Android device, playing audio, monitoring
+ audio play, and stop playing audio
+ """
+ music_file_to_play = ""
+ device_fails_to_connect_list = []
+
+ def __init__(self, controllers):
+ BtMetricsBaseTest.__init__(self, controllers)
+ self.ad = self.android_devices[0]
+ self.dongle = self.relay_devices[0]
+
+ def _pair_devices(self):
+ self.ad.droid.bluetoothStartPairingHelper(False)
+ self.dongle.enter_pairing_mode()
+
+ self.ad.droid.bluetoothBond(self.dongle.mac_address)
+
+ end_time = time.time() + 20
+ self.ad.log.info("Verifying devices are bonded")
+ while time.time() < end_time:
+ bonded_devices = self.ad.droid.bluetoothGetBondedDevices()
+
+ for d in bonded_devices:
+ if d['address'] == self.dongle.mac_address:
+ self.ad.log.info("Successfully bonded to device.")
+ self.log.info("Bonded devices:\n{}".format(bonded_devices))
+ return True
+ self.ad.log.info("Failed to bond devices.")
+ return False
+
+ def setup_test(self):
+ super(BtFunhausBaseTest, self).setup_test()
+ self.dongle.setup()
+ tries = 5
+ # Since we are not concerned with pairing in this test, try 5 times.
+ while tries > 0:
+ if self._pair_devices():
+ return True
+ else:
+ tries -= 1
+ return False
+
+ def teardown_test(self):
+ super(BtFunhausBaseTest, self).teardown_test()
+ self.dongle.clean_up()
+ return True
+
+ def on_fail(self, test_name, begin_time):
+ self.dongle.clean_up()
+ self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
+ super(BtFunhausBaseTest, self).on_fail(test_name, begin_time)
+
+ def setup_class(self):
+ if not super(BtFunhausBaseTest, self).setup_class():
+ return False
+ for ad in self.android_devices:
+ sync_device_time(ad)
+ # Disable Bluetooth HCI Snoop Logs for audio tests
+ ad.adb.shell("setprop persist.bluetooth.btsnoopenable false")
+ if not bypass_setup_wizard(ad):
+ self.log.debug(
+ "Failed to bypass setup wizard, continuing test.")
+ # Add music to the Android device
+ return self._add_music_to_android_device(ad)
+
+ def _add_music_to_android_device(self, ad):
+ """
+ Add music to Android device as specified by the test config
+ :param ad: Android device
+ :return: True on success, False on failure
+ """
+ self.log.info("Pushing music to the Android device.")
+ music_path_str = "bt_music"
+ android_music_path = "/sdcard/Music/"
+ if music_path_str not in self.user_params:
+ self.log.error("Need music for audio testcases...")
+ return False
+ music_path = self.user_params[music_path_str]
+ if type(music_path) is list:
+ self.log.info("Media ready to push as is.")
+ elif not os.path.isdir(music_path):
+ music_path = os.path.join(
+ self.user_params[Config.key_config_path.value], music_path)
+ if not os.path.isdir(music_path):
+ self.log.error(
+ "Unable to find music directory {}.".format(music_path))
+ return False
+ if type(music_path) is list:
+ for item in music_path:
+ self.music_file_to_play = item
+ ad.adb.push("{} {}".format(item, android_music_path))
+ else:
+ for dirname, dirnames, filenames in os.walk(music_path):
+ for filename in filenames:
+ self.music_file_to_play = filename
+ file = os.path.join(dirname, filename)
+ # TODO: Handle file paths with spaces
+ ad.adb.push("{} {}".format(file, android_music_path))
+ ad.reboot()
+ return True
+
+ def _collect_bluetooth_manager_dumpsys_logs(self, ads):
+ """
+ Collect "adb shell dumpsys bluetooth_manager" logs
+ :param ads: list of active Android devices
+ :return: None
+ """
+ for ad in ads:
+ serial = ad.serial
+ out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
+ dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
+ os.makedirs(dumpsys_path, exist_ok=True)
+ cmd = ''.join(
+ ("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
+ dumpsys_path, "/", out_name))
+ exe_cmd(cmd)
+
+ def start_playing_music_on_all_devices(self):
+ """
+ Start playing music
+ :return: None
+ """
+ self.ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format(
+ self.music_file_to_play.split("/")[-1]))
+ self.ad.droid.mediaPlaySetLooping(True)
+ self.ad.log.info("Music is now playing.")
+
+ def monitor_music_play_util_deadline(self, end_time, sleep_interval=1):
+ """
+ Monitor music play on all devices, if a device's Bluetooth adapter is
+ OFF or if a device is not connected to any remote Bluetooth devices,
+ we add them to failure lists bluetooth_off_list and
+ device_not_connected_list respectively
+ :param end_time: The deadline in epoch floating point seconds that we
+ must stop playing
+ :param sleep_interval: How often to monitor, too small we may drain
+ too much resources on Android, too big the deadline might be passed
+ by a maximum of this amount
+ :return:
+ status: False iff all devices are off or disconnected otherwise True
+ bluetooth_off_list: List of ADs that have Bluetooth at OFF state
+ device_not_connected_list: List of ADs with no remote device
+ connected
+ """
+ device_not_connected_list = []
+ while time.time() < end_time:
+ if not self.ad.droid.bluetoothCheckState():
+ self.ad.log.error("Device {}'s Bluetooth state is off.".format(
+ self.ad.serial))
+ return False
+ if self.ad.droid.bluetoothGetConnectedDevices() == 0:
+ self.ad.log.error(
+ "Bluetooth device not connected. Failing test.")
+ time.sleep(sleep_interval)
+ return True
+
+ def play_music_for_duration(self, duration, sleep_interval=1):
+ """
+ A convenience method for above methods. It starts run music on all
+ devices, monitors the health of music play and stops playing them when
+ time passes the duration
+ :param duration: Duration in floating point seconds
+ :param sleep_interval: How often to check the health of music play
+ :return:
+ status: False iff all devices are off or disconnected otherwise True
+ bluetooth_off_list: List of ADs that have Bluetooth at OFF state
+ device_not_connected_list: List of ADs with no remote device
+ connected
+ """
+ start_time = time.time()
+ end_time = start_time + duration
+ self.start_playing_music_on_all_devices()
+ status = self.monitor_music_play_util_deadline(end_time,
+ sleep_interval)
+ self.ad.droid.mediaPlayStopAll()
+ return status
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py
new file mode 100644
index 0000000..3c49bee
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Stream music through connected device from phone across different
+attenuations."""
+
+import json
+import math
+import time
+import acts.controllers.iperf_client as ipc
+import acts.controllers.iperf_server as ipf
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+from acts import asserts
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.bt.loggers import bluetooth_metric_logger as log
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wpeutils
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power.PowerBaseTest import ObjNew
+
+MAX_ATTENUATION = 95
+TEMP_FILE = '/sdcard/Download/tmp.log'
+IPERF_CLIENT_ERROR = 'the client has unexpectedly closed the connection'
+
+
+def setup_ap_connection(dut, network, ap, bandwidth=20):
+ """Setup AP and connect DUT to it.
+
+ Args:
+ dut: the android device to connect and run traffic
+ network: the network config for the AP to be setup
+ ap: access point object
+ bandwidth: bandwidth of the WiFi network to be setup
+ Returns:
+ self.brconfigs: dict for bridge interface configs
+ """
+ wutils.wifi_toggle_state(dut, True)
+ brconfigs = wputils.ap_setup(ap, network, bandwidth=bandwidth)
+ wutils.wifi_connect(dut, network, num_of_tries=3)
+ return brconfigs
+
+
+def start_iperf_client(traffic_pair_obj, duration):
+ """Setup iperf traffic for TCP downlink.
+ Args:
+ traffic_pair_obj: obj to contain info on traffic pair
+ duration: duration of iperf traffic to run
+ """
+ # Construct the iperf command based on the test params
+ iperf_cmd = 'iperf3 -c {} -i 1 -t {} -p {} -J -R > {}'.format(
+ traffic_pair_obj.server_address, duration,
+ traffic_pair_obj.iperf_server.port, TEMP_FILE)
+ # Start IPERF client
+ traffic_pair_obj.dut.adb.shell_nb(iperf_cmd)
+
+
+def unpack_custom_file(file):
+ """Unpack the json file to .
+
+ Args:
+ file: custom json file.
+ """
+ with open(file, 'r') as f:
+ params = json.load(f)
+ return params
+
+
+def get_iperf_results(iperf_server_obj):
+ """Get the iperf results and process.
+
+ Args:
+ iperf_server_obj: the IperfServer object
+ Returns:
+ throughput: the average throughput during tests.
+ """
+ # Get IPERF results and add this to the plot title
+ iperf_file = iperf_server_obj.log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ # Compute the throughput in Mbit/s
+ if iperf_result.error == IPERF_CLIENT_ERROR:
+ rates = []
+ for item in iperf_result.result['intervals']:
+ rates.append(item['sum']['bits_per_second'] / 8 / 1024 / 1024)
+ throughput = ((math.fsum(rates) / len(rates))) * 8 * (1.024**2)
+ else:
+ throughput = (math.fsum(iperf_result.instantaneous_rates) / len(
+ iperf_result.instantaneous_rates)) * 8 * (1.024**2)
+ except (ValueError, TypeError):
+ throughput = 0
+ return throughput
+
+
+class BtInterferenceBaseTest(A2dpBaseTest):
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.bt_logger = log.BluetoothMetricLogger.for_test_case()
+ self.start_time = time.time()
+ req_params = [
+ 'attenuation_vector', 'wifi_networks', 'codecs', 'custom_files',
+ 'audio_params'
+ ]
+ self.unpack_userparams(req_params)
+ for file in self.custom_files:
+ if 'static_interference' in file:
+ self.static_wifi_interference = unpack_custom_file(file)
+ elif 'dynamic_interference' in file:
+ self.dynamic_wifi_interference = unpack_custom_file(file)
+
+ def setup_class(self):
+ super().setup_class()
+ # Build object to store all necessary information for each pair of wifi
+ # interference setup: phone, ap, network, channel, iperf server port/ip
+ # object and bridge interface configs
+ if len(self.android_devices) < 5 or len(self.attenuators) < 4:
+ self.log.error('Need a 4 channel attenuator and 5 android phones'
+ 'please update the config file')
+ self.wifi_int_pairs = []
+ for i in range(len(self.attenuators) - 1):
+ tmp_dict = {
+ 'dut': self.android_devices[i + 1],
+ 'ap': self.access_points[i],
+ 'network': self.wifi_networks[i],
+ 'channel': self.wifi_networks[i]['channel'],
+ 'iperf_server': self.iperf_servers[i],
+ 'attenuator': self.attenuators[i + 1],
+ 'ether_int': self.packet_senders[i],
+ 'iperf_client':
+ ipc.IPerfClientOverAdb(self.android_devices[i + 1])
+ }
+ tmp_obj = ObjNew(**tmp_dict)
+ self.wifi_int_pairs.append(tmp_obj)
+ ##Setup connection between WiFi APs and Phones and get DHCP address
+ # for the interface
+ for obj in self.wifi_int_pairs:
+ brconfigs = setup_ap_connection(obj.dut, obj.network, obj.ap)
+ iperf_server_address = wputils.wait_for_dhcp(
+ obj.ether_int.interface)
+ setattr(obj, 'server_address', iperf_server_address)
+ setattr(obj, 'brconfigs', brconfigs)
+ obj.attenuator.set_atten(MAX_ATTENUATION)
+ # Enable BQR on master and slave Android device
+ btutils.enable_bqr(self.dut)
+ btutils.enable_bqr(self.bt_device_controller)
+
+ def teardown_class(self):
+ super().teardown_class()
+ for obj in self.wifi_int_pairs:
+ obj.ap.bridge.teardown(obj.brconfigs)
+ self.log.info('Stop IPERF server at port {}'.format(
+ obj.iperf_server.port))
+ obj.iperf_server.stop()
+ self.log.info('Stop IPERF process on {}'.format(obj.dut.serial))
+ obj.dut.adb.shell('pkill -9 iperf3')
+ #only for glinux machine
+ # wputils.bring_down_interface(obj.ether_int.interface)
+ obj.attenuator.set_atten(MAX_ATTENUATION)
+ obj.ap.close()
+
+ def teardown_test(self):
+
+ super().teardown_test()
+
+ for obj in self.wifi_int_pairs:
+ obj.attenuator.set_atten(MAX_ATTENUATION)
+
+ def play_and_record_audio(self, duration, queue):
+ """Play and record audio for a set duration.
+
+ Args:
+ duration: duration in seconds for music playing
+ que: multiprocess que to store the return value of this function
+ Returns:
+ audio_captured: captured audio file path
+ """
+
+ self.log.info('Play and record audio for {} second'.format(duration))
+ self.media.play()
+ self.audio_device.start()
+ time.sleep(duration)
+ audio_captured = self.audio_device.stop()
+ self.media.stop()
+ self.log.info('Audio play and record stopped')
+ asserts.assert_true(audio_captured, 'Audio not recorded')
+ queue.put(audio_captured)
+
+ def locate_interference_pair_by_channel(self, interference_channels):
+ """Function to find which attenautor to set based on channel info
+ Args:
+ interference_channels: list of interference channels
+ Return:
+ interference_pair_indices: list of indices for interference pair
+ in self.wifi_int_pairs
+ """
+ interference_pair_indices = []
+ for chan in interference_channels:
+ for i in range(len(self.wifi_int_pairs)):
+ if self.wifi_int_pairs[i].channel == chan:
+ interference_pair_indices.append(i)
+ return interference_pair_indices
+
+ def get_interference_rssi(self):
+ """Function to read wifi interference RSSI level."""
+
+ bssids = []
+ self.interference_rssi = []
+ wutils.wifi_toggle_state(self.android_devices[0], True)
+ for item in self.wifi_int_pairs:
+ ssid = item.network['SSID']
+ bssid = item.ap.get_bssid_from_ssid(ssid, '2g')
+ bssids.append(bssid)
+ interference_rssi_dict = {
+ "ssid": ssid,
+ "bssid": bssid,
+ "chan": item.channel,
+ "rssi": 0
+ }
+ self.interference_rssi.append(interference_rssi_dict)
+ scaned_rssi = wpeutils.get_scan_rssi(self.android_devices[0],
+ bssids,
+ num_measurements=2)
+ for item in self.interference_rssi:
+ item['rssi'] = scaned_rssi[item['bssid']]['mean']
+ self.log.info('Interference RSSI at channel {} is {} dBm'.format(
+ item['chan'], item['rssi']))
+ wutils.wifi_toggle_state(self.android_devices[0], False)
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtMetricsBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtMetricsBaseTest.py
new file mode 100644
index 0000000..02ed03a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtMetricsBaseTest.py
@@ -0,0 +1,74 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import os
+
+from acts.libs.proto.proto_utils import parse_proto_to_ascii
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_metrics_utils import get_bluetooth_metrics
+from acts.utils import dump_string_to_file
+
+
+class BtMetricsBaseTest(BluetoothBaseTest):
+ """
+ Base class for tests that requires dumping and parsing Bluetooth Metrics
+ """
+
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ self.ad = self.android_devices[0]
+
+ def setup_class(self):
+ """
+ This method finds bluetooth protobuf definitions from config file,
+ compile the protobuf and create a log directory for metrics dumping
+ :return: True on success, False on failure
+ """
+ super(BtMetricsBaseTest, self).setup_class()
+ for ad in self.android_devices:
+ ad.metrics_path = os.path.join(ad.log_path, "BluetoothMetrics")
+ os.makedirs(ad.metrics_path, exist_ok=True)
+ return True
+
+ def setup_test(self):
+ """
+ This method clears the current metrics, should be called after child
+ class setup_test()
+ :return: True
+ """
+ super(BtMetricsBaseTest, self).setup_test()
+ # Clear all metrics
+ for ad in self.android_devices:
+ get_bluetooth_metrics(ad)
+ return True
+
+ def collect_bluetooth_manager_metrics_logs(self, ads):
+ """
+ Collect Bluetooth metrics logs, save an ascii log to disk and return
+ both binary and ascii logs to caller
+ :param ads: list of active Android devices
+ :return: List of binary metrics logs,
+ List of ascii metrics logs
+ """
+ bluetooth_logs = []
+ bluetooth_logs_ascii = []
+ for ad in ads:
+ serial = ad.serial
+ out_name = "{}_{}".format(serial, "bluetooth_metrics.txt")
+ bluetooth_log = get_bluetooth_metrics(ad)
+ bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
+ dump_string_to_file(bluetooth_log_ascii,
+ os.path.join(ad.metrics_path, out_name))
+ bluetooth_logs.append(bluetooth_log)
+ bluetooth_logs_ascii.append(bluetooth_log_ascii)
+ return bluetooth_logs, bluetooth_logs_ascii
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtSarBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtSarBaseTest.py
new file mode 100644
index 0000000..6b6e548
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtSarBaseTest.py
@@ -0,0 +1,739 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+import time
+import logging
+import pandas as pd
+
+from acts import asserts
+from acts.libs.proc import job
+from acts.base_test import BaseTestClass
+
+from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts_contrib.test_utils.bt.bt_power_test_utils import MediaControl
+from acts_contrib.test_utils.bt.ble_performance_test_utils import run_ble_throughput_and_read_rssi
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory
+
+import acts_contrib.test_utils.bt.bt_test_utils as bt_utils
+import acts_contrib.test_utils.wifi.wifi_performance_test_utils as wifi_utils
+
+PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music'
+
+FORCE_SAR_ADB_COMMAND = ('am broadcast -n'
+ 'com.google.android.apps.scone/.coex.TestReceiver -a '
+ 'com.google.android.apps.scone.coex.SIMULATE_STATE ')
+
+SLEEP_DURATION = 2
+
+DEFAULT_DURATION = 5
+DEFAULT_MAX_ERROR_THRESHOLD = 2
+DEFAULT_AGG_MAX_ERROR_THRESHOLD = 2
+FIXED_ATTENUATION = 36
+
+
+class BtSarBaseTest(BaseTestClass):
+ """ Base class for all BT SAR Test classes.
+
+ This class implements functions common to BT SAR test Classes.
+ """
+ BACKUP_BT_SAR_TABLE_NAME = 'backup_bt_sar_table.csv'
+
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+ self.power_file_paths = [
+ '/vendor/etc/bluetooth_power_limits.csv',
+ '/data/vendor/radio/bluetooth_power_limits.csv'
+ ]
+ self.sar_test_result = BlackboxMetricLogger.for_test_case(
+ metric_name='pass')
+ self.sar_file_name = os.path.basename(self.power_file_paths[0])
+ self.power_column = 'BluetoothPower'
+ self.REG_DOMAIN_DICT = {
+ ('us', 'ca', 'in'): 'US',
+ ('uk', 'fr', 'es', 'de', 'it', 'ie', 'sg', 'au', 'tw'): 'EU',
+ ('jp', ): 'JP'
+ }
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ This function initializes hardware and compiles parameters that are
+ common to all tests in this class and derived classes.
+ """
+ super().setup_class()
+
+ self.test_params = self.user_params.get('bt_sar_test_params', {})
+ if not self.test_params:
+ self.log.warning(
+ 'bt_sar_test_params was not found in the config file.')
+
+ self.user_params.update(self.test_params)
+ req_params = ['bt_devices', 'calibration_params', 'custom_files']
+
+ self.unpack_userparams(
+ req_params,
+ country_code='us',
+ duration=DEFAULT_DURATION,
+ sort_order=None,
+ max_error_threshold=DEFAULT_MAX_ERROR_THRESHOLD,
+ agg_error_threshold=DEFAULT_AGG_MAX_ERROR_THRESHOLD,
+ tpc_threshold=[2, 8],
+ sar_margin={
+ 'BDR': 0,
+ 'EDR': 0,
+ 'BLE': 0
+ })
+
+ self.attenuator = self.attenuators[0]
+ self.dut = self.android_devices[0]
+
+ for key in self.REG_DOMAIN_DICT.keys():
+ if self.country_code.lower() in key:
+ self.reg_domain = self.REG_DOMAIN_DICT[key]
+
+ self.sar_version_2 = False
+
+ if 'Error' not in self.dut.adb.shell('bluetooth_sar_test -r'):
+ #Flag for SAR version 2
+ self.sar_version_2 = True
+ self.power_column = 'BluetoothEDRPower'
+ self.power_file_paths[0] = os.path.join(
+ os.path.dirname(self.power_file_paths[0]),
+ 'bluetooth_power_limits_{}.csv'.format(self.reg_domain))
+ self.sar_file_name = os.path.basename(self.power_file_paths[0])
+
+ if self.sar_version_2:
+ custom_file_suffix = 'version2'
+ else:
+ custom_file_suffix = 'version1'
+
+ for file in self.custom_files:
+ if 'custom_sar_table_{}.csv'.format(custom_file_suffix) in file:
+ self.custom_sar_path = file
+ break
+ else:
+ raise RuntimeError('Custom Sar File is missing')
+
+ self.sar_file_path = self.power_file_paths[0]
+ self.atten_min = 0
+ self.atten_max = int(self.attenuator.get_max_atten())
+
+ # Get music file and push it to the phone and initialize Media controller
+ music_files = self.user_params.get('music_files', [])
+ if music_files:
+ music_src = music_files[0]
+ music_dest = PHONE_MUSIC_FILE_DIRECTORY
+ success = self.dut.push_system_file(music_src, music_dest)
+ if success:
+ self.music_file = os.path.join(PHONE_MUSIC_FILE_DIRECTORY,
+ os.path.basename(music_src))
+ # Initialize media_control class
+ self.media = MediaControl(self.dut, self.music_file)
+
+ #Initializing BT device controller
+ if self.bt_devices:
+ attr, idx = self.bt_devices.split(':')
+ self.bt_device_controller = getattr(self, attr)[int(idx)]
+ self.bt_device = bt_factory().generate(self.bt_device_controller)
+ else:
+ self.log.error('No BT devices config is provided!')
+
+ bt_utils.enable_bqr(self.android_devices)
+
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+
+ # Reading BT SAR table from the phone
+ self.bt_sar_df = self.read_sar_table(self.dut)
+
+ def setup_test(self):
+ super().setup_test()
+
+ #Reset SAR test result to 0 before every test
+ self.sar_test_result.metric_value = 0
+
+ # Starting BT on the master
+ self.dut.droid.bluetoothFactoryReset()
+ bt_utils.enable_bluetooth(self.dut.droid, self.dut.ed)
+
+ # Starting BT on the slave
+ self.bt_device.reset()
+ self.bt_device.power_on()
+
+ # Connect master and slave
+ bt_utils.connect_phone_to_headset(self.dut, self.bt_device, 60)
+
+ # Playing music
+ self.media.play()
+
+ # Find and set PL10 level for the DUT
+ self.pl10_atten = self.set_PL10_atten_level(self.dut)
+ self.attenuator.set_atten(self.pl10_atten)
+
+ def teardown_test(self):
+ #Stopping Music
+ if hasattr(self, 'media'):
+ self.media.stop()
+
+ # Stopping BT on slave
+ self.bt_device.reset()
+ self.bt_device.power_off()
+
+ #Stopping BT on master
+ bt_utils.disable_bluetooth(self.dut.droid)
+
+ #Resetting the atten to initial levels
+ self.attenuator.set_atten(self.atten_min)
+ self.log.info('Attenuation set to {} dB'.format(self.atten_min))
+
+ def teardown_class(self):
+
+ super().teardown_class()
+ self.dut.droid.bluetoothFactoryReset()
+
+ # Stopping BT on slave
+ self.bt_device.reset()
+ self.bt_device.power_off()
+
+ #Stopping BT on master
+ bt_utils.disable_bluetooth(self.dut.droid)
+
+ def save_sar_plot(self, df):
+ """ Saves SAR plot to the path given.
+
+ Args:
+ df: Processed SAR table sweep results
+ """
+ self.plot.add_line(
+ df.index,
+ df['expected_tx_power'],
+ legend='expected',
+ marker='circle')
+ self.plot.add_line(
+ df.index,
+ df['measured_tx_power'],
+ legend='measured',
+ marker='circle')
+ self.plot.add_line(
+ df.index, df['delta'], legend='delta', marker='circle')
+
+ results_file_path = os.path.join(self.log_path, '{}.html'.format(
+ self.current_test_name))
+ self.plot.generate_figure()
+ wifi_utils.BokehFigure.save_figures([self.plot], results_file_path)
+
+ def sweep_power_cap(self):
+ sar_df = self.bt_sar_df
+ sar_df['BDR_power_cap'] = -128
+ sar_df['EDR_power_cap'] = -128
+ sar_df['BLE_power_cap'] = -128
+
+ if self.sar_version_2:
+ power_column_dict = {
+ 'BDR': 'BluetoothBDRPower',
+ 'EDR': 'BluetoothEDRPower',
+ 'BLE': 'BluetoothLEPower'
+ }
+ else:
+ power_column_dict = {'EDR': self.power_column}
+
+ power_cap_error = False
+
+ for type, column_name in power_column_dict.items():
+
+ self.log.info("Performing sanity test on {}".format(type))
+ # Iterating through the BT SAR scenarios
+ for scenario in range(0, self.bt_sar_df.shape[0]):
+ # Reading BT SAR table row into dict
+ read_scenario = sar_df.loc[scenario].to_dict()
+ start_time = self.dut.adb.shell('date +%s.%m')
+ time.sleep(SLEEP_DURATION)
+
+ # Setting SAR state to the read BT SAR row
+ self.set_sar_state(self.dut, read_scenario, self.country_code)
+
+ # Reading device power cap from logcat after forcing SAR State
+ scenario_power_cap = self.get_current_power_cap(
+ self.dut, start_time, type=type)
+ sar_df.loc[scenario, '{}_power_cap'.format(
+ type)] = scenario_power_cap
+ self.log.info(
+ 'scenario: {}, '
+ 'sar_power: {}, power_cap:{}'.format(
+ scenario, sar_df.loc[scenario, column_name],
+ sar_df.loc[scenario, '{}_power_cap'.format(type)]))
+
+ if not sar_df['{}_power_cap'.format(type)].equals(sar_df[column_name]):
+ power_cap_error = True
+
+ results_file_path = os.path.join(self.log_path, '{}.csv'.format(
+ self.current_test_name))
+ sar_df.to_csv(results_file_path)
+
+ return power_cap_error
+
+ def sweep_table(self,
+ client_ad=None,
+ server_ad=None,
+ client_conn_id=None,
+ gatt_server=None,
+ gatt_callback=None,
+ isBLE=False):
+ """Iterates over the BT SAR table and forces signal states.
+
+ Iterates over BT SAR table and forces signal states,
+ measuring RSSI and power level for each state.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ client_conn_id: the client connection ID.
+ gatt_server: the gatt server
+ gatt_callback: Gatt callback objec
+ isBLE : boolean variable for BLE connection
+ Returns:
+ sar_df : SAR table sweep results in pandas dataframe
+ """
+
+ sar_df = self.bt_sar_df.copy()
+ sar_df['power_cap'] = -128
+ sar_df['slave_rssi'] = -128
+ sar_df['master_rssi'] = -128
+ sar_df['ble_rssi'] = -128
+ sar_df['pwlv'] = -1
+
+ # Sorts the table
+ if self.sort_order:
+ if self.sort_order.lower() == 'ascending':
+ sar_df = sar_df.sort_values(
+ by=[self.power_column], ascending=True)
+ else:
+ sar_df = sar_df.sort_values(
+ by=[self.power_column], ascending=False)
+ sar_df = sar_df.reset_index(drop=True)
+
+ # Sweeping BT SAR table
+ for scenario in range(sar_df.shape[0]):
+ # Reading BT SAR Scenario from the table
+ read_scenario = sar_df.loc[scenario].to_dict()
+
+ start_time = self.dut.adb.shell('date +%s.%m')
+ time.sleep(SLEEP_DURATION)
+
+ #Setting SAR State
+ self.set_sar_state(self.dut, read_scenario, self.country_code)
+
+ if isBLE:
+ sar_df.loc[scenario, 'power_cap'] = self.get_current_power_cap(
+ self.dut, start_time, type='BLE')
+
+ sar_df.loc[
+ scenario, 'ble_rssi'] = run_ble_throughput_and_read_rssi(
+ client_ad, server_ad, client_conn_id, gatt_server,
+ gatt_callback)
+
+ self.log.info('scenario:{}, power_cap:{}, ble_rssi:{}'.format(
+ scenario, sar_df.loc[scenario, 'power_cap'],
+ sar_df.loc[scenario, 'ble_rssi']))
+ else:
+ sar_df.loc[scenario, 'power_cap'] = self.get_current_power_cap(
+ self.dut, start_time)
+
+ processed_bqr_results = bt_utils.get_bt_metric(
+ self.android_devices, self.duration)
+ sar_df.loc[scenario, 'slave_rssi'] = processed_bqr_results[
+ 'rssi'][self.bt_device_controller.serial]
+ sar_df.loc[scenario, 'master_rssi'] = processed_bqr_results[
+ 'rssi'][self.dut.serial]
+ sar_df.loc[scenario, 'pwlv'] = processed_bqr_results['pwlv'][
+ self.dut.serial]
+ self.log.info(
+ 'scenario:{}, power_cap:{}, s_rssi:{}, m_rssi:{}, m_pwlv:{}'
+ .format(scenario, sar_df.loc[scenario, 'power_cap'],
+ sar_df.loc[scenario, 'slave_rssi'],
+ sar_df.loc[scenario, 'master_rssi'],
+ sar_df.loc[scenario, 'pwlv']))
+
+ self.log.info('BT SAR Table swept')
+
+ return sar_df
+
+ def process_table(self, sar_df):
+ """Processes the results of sweep_table and computes BT TX power.
+
+ Processes the results of sweep_table and computes BT TX power
+ after factoring in the path loss and FTM offsets.
+
+ Args:
+ sar_df: BT SAR table after the sweep
+
+ Returns:
+ sar_df: processed BT SAR table
+ """
+
+ sar_df['pathloss'] = self.calibration_params['pathloss']
+
+ if hasattr(self, 'pl10_atten'):
+ sar_df['atten'] = self.pl10_atten
+ else:
+ sar_df['atten'] = FIXED_ATTENUATION
+
+ # BT SAR Backoff for each scenario
+ if self.sar_version_2:
+ #Reads OTP values from the phone
+ self.otp = bt_utils.read_otp(self.dut)
+
+ #OTP backoff
+ edr_otp = min(0, float(self.otp['EDR']['10']))
+ bdr_otp = min(0, float(self.otp['BR']['10']))
+ ble_otp = min(0, float(self.otp['BLE']['10']))
+
+ # EDR TX Power for PL10
+ edr_tx_power_pl10 = self.calibration_params['target_power']['EDR']['10'] - edr_otp
+
+ # BDR TX Power for PL10
+ bdr_tx_power_pl10 = self.calibration_params['target_power']['BDR']['10'] - bdr_otp
+
+ # RSSI being measured is BDR
+ offset = bdr_tx_power_pl10 - edr_tx_power_pl10
+
+ # BDR-EDR offset
+ sar_df['offset'] = offset
+
+ # Max TX power permissible
+ sar_df['max_power'] = self.calibration_params['max_power']
+
+ # Adding a target power column
+ if 'ble_rssi' in sar_df.columns:
+ sar_df[
+ 'target_power'] = self.calibration_params['target_power']['BLE']['10'] - ble_otp
+ else:
+ sar_df['target_power'] = sar_df['pwlv'].astype(str).map(
+ self.calibration_params['target_power']['EDR']) - edr_otp
+
+ #Translates power_cap values to expected TX power level
+ sar_df['cap_tx_power'] = sar_df['power_cap'] / 4.0
+
+ sar_df['expected_tx_power'] = sar_df[[
+ 'cap_tx_power', 'target_power', 'max_power'
+ ]].min(axis=1)
+
+ if hasattr(self, 'pl10_atten'):
+ sar_df[
+ 'measured_tx_power'] = sar_df['slave_rssi'] + sar_df['pathloss'] + self.pl10_atten - offset
+ else:
+ sar_df[
+ 'measured_tx_power'] = sar_df['ble_rssi'] + sar_df['pathloss'] + FIXED_ATTENUATION
+
+ else:
+
+ # Adding a target power column
+ sar_df['target_power'] = sar_df['pwlv'].astype(str).map(
+ self.calibration_params['target_power']['EDR']['10'])
+
+ # Adding a ftm power column
+ sar_df['ftm_power'] = sar_df['pwlv'].astype(str).map(
+ self.calibration_params['ftm_power']['EDR'])
+ sar_df[
+ 'backoff'] = sar_df['target_power'] - sar_df['power_cap'] / 4.0
+
+ sar_df[
+ 'expected_tx_power'] = sar_df['ftm_power'] - sar_df['backoff']
+ sar_df[
+ 'measured_tx_power'] = sar_df['slave_rssi'] + sar_df['pathloss'] + self.pl10_atten
+
+ sar_df[
+ 'delta'] = sar_df['expected_tx_power'] - sar_df['measured_tx_power']
+
+ self.log.info('Sweep results processed')
+
+ results_file_path = os.path.join(self.log_path, self.current_test_name)
+ sar_df.to_csv('{}.csv'.format(results_file_path))
+ self.save_sar_plot(sar_df)
+
+ return sar_df
+
+ def process_results(self, sar_df, type='EDR'):
+ """Determines the test results of the sweep.
+
+ Parses the processed table with computed BT TX power values
+ to return pass or fail.
+
+ Args:
+ sar_df: processed BT SAR table
+ """
+ if self.sar_version_2:
+ breach_error_result = (
+ sar_df['expected_tx_power'] + self.sar_margin[type] >
+ sar_df['measured_tx_power']).all()
+ if not breach_error_result:
+ asserts.fail('Measured TX power exceeds expected')
+
+ else:
+ # checks for errors at particular points in the sweep
+ max_error_result = abs(
+ sar_df['delta']) > self.max_error_threshold[type]
+ if max_error_result:
+ asserts.fail('Maximum Error Threshold Exceeded')
+
+ # checks for error accumulation across the sweep
+ if sar_df['delta'].sum() > self.agg_error_threshold[type]:
+ asserts.fail(
+ 'Aggregate Error Threshold Exceeded. Error: {} Threshold: {}'.
+ format(sar_df['delta'].sum(), self.agg_error_threshold))
+
+ self.sar_test_result.metric_value = 1
+ asserts.explicit_pass('Measured and Expected Power Values in line')
+
+ def set_sar_state(self, ad, signal_dict, country_code='us'):
+ """Sets the SAR state corresponding to the BT SAR signal.
+
+ The SAR state is forced using an adb command that takes
+ device signals as input.
+
+ Args:
+ ad: android_device object.
+ signal_dict: dict of BT SAR signals read from the SAR file.
+ Returns:
+ enforced_state: dict of device signals.
+ """
+ signal_dict = {k: max(int(v), 0) for (k, v) in signal_dict.items()}
+ signal_dict["Wifi"] = signal_dict['WIFI5Ghz']
+ signal_dict['WIFI2Ghz'] = 0 if signal_dict['WIFI5Ghz'] else 1
+
+ device_state_dict = {
+ ('Earpiece', 'earpiece'): signal_dict['Head'],
+ ('Wifi', 'wifi'): signal_dict['WIFI5Ghz'],
+ ('Wifi 2.4G', 'wifi_24g'): signal_dict['WIFI2Ghz'],
+ ('Voice', 'voice'): 0,
+ ('Wifi AP', 'wifi_ap'): signal_dict['HotspotVoice'],
+ ('Bluetooth', 'bluetooth'): 1,
+ ('Bluetooth media', 'bt_media'): signal_dict['BTMedia'],
+ ('Radio', 'radio_power'): signal_dict['Cell'],
+ ('Motion', 'motion'): signal_dict['IMU'],
+ ('Bluetooth connected', 'bt_connected'): 1
+ }
+
+ if 'BTHotspot' in signal_dict.keys():
+ device_state_dict[('Bluetooth tethering',
+ 'bt_tethering')] = signal_dict['BTHotspot']
+
+ enforced_state = {}
+ sar_state_command = FORCE_SAR_ADB_COMMAND
+ for key in device_state_dict:
+ enforced_state[key[0]] = device_state_dict[key]
+ sar_state_command = '{} --ei {} {}'.format(
+ sar_state_command, key[1], device_state_dict[key])
+ if self.sar_version_2:
+ sar_state_command = '{} --es country_iso "{}"'.format(
+ sar_state_command, country_code.lower())
+
+ #Forcing the SAR state
+ adb_output = ad.adb.shell(sar_state_command)
+
+ # Checking if command was successfully enforced
+ if 'result=0' in adb_output:
+ self.log.info('Requested BT SAR state successfully enforced.')
+ return enforced_state
+ else:
+ self.log.error("Couldn't force BT SAR state.")
+
+ def parse_bt_logs(self, ad, begin_time, regex=''):
+ """Returns bt software stats by parsing logcat since begin_time.
+
+ The quantity to be fetched is dictated by the regex provided.
+
+ Args:
+ ad: android_device object.
+ begin_time: time stamp to start the logcat parsing.
+ regex: regex for fetching the required BT software stats.
+
+ Returns:
+ stat: the desired BT stat.
+ """
+ # Waiting for logcat to update
+ time.sleep(SLEEP_DURATION)
+ bt_adb_log = ad.adb.logcat('-b all -t %s' % begin_time)
+ for line in bt_adb_log.splitlines():
+ if re.findall(regex, line):
+ stat = re.findall(regex, line)[0]
+ return stat
+
+ def set_country_code(self, ad, cc):
+ """Sets the SAR regulatory domain as per given country code
+
+ The SAR regulatory domain is forced using an adb command that takes
+ country code as input.
+
+ Args:
+ ad: android_device object.
+ cc: country code
+ """
+
+ ad.adb.shell("{} --es country_iso {}".format(FORCE_SAR_ADB_COMMAND,
+ cc))
+ self.log.info("Country Code set to {}".format(cc))
+
+ def get_country_code(self, ad, begin_time):
+ """Returns the enforced regulatory domain since begin_time
+
+ Returns enforced regulatory domain since begin_time by parsing logcat.
+ Function should follow a function call to set a country code
+
+ Args:
+ ad : android_device obj
+ begin_time: time stamp to start
+
+ Returns:
+ read enforced regulatory domain
+ """
+
+ reg_domain_regex = "updateRegulatoryDomain:\s+(\S+)"
+ reg_domain = self.parse_bt_logs(ad, begin_time, reg_domain_regex)
+ return reg_domain
+
+ def get_current_power_cap(self, ad, begin_time, type='EDR'):
+ """ Returns the enforced software EDR power cap since begin_time.
+
+ Returns the enforced EDR power cap since begin_time by parsing logcat.
+ Function should follow a function call that forces a SAR state
+
+ Args:
+ ad: android_device obj.
+ begin_time: time stamp to start.
+
+ Returns:
+ read enforced power cap
+ """
+ power_cap_regex_dict = {
+ 'BDR': [
+ 'Bluetooth powers: BR:\s+(\d+), EDR:\s+\d+',
+ 'Bluetooth Tx Power Cap\s+(\d+)'
+ ],
+ 'EDR': [
+ 'Bluetooth powers: BR:\s+\d+, EDR:\s+(\d+)',
+ 'Bluetooth Tx Power Cap\s+(\d+)'
+ ],
+ 'BLE': [
+ 'Bluetooth powers: BR:\s+\d+, EDR:\s+\d+, BLE:\s+(\d+)',
+ 'Bluetooth Tx Power Cap\s+(\d+)'
+ ]
+ }
+
+ power_cap_regex_list = power_cap_regex_dict[type]
+
+ for power_cap_regex in power_cap_regex_list:
+ power_cap = self.parse_bt_logs(ad, begin_time, power_cap_regex)
+ if power_cap:
+ return int(power_cap)
+
+ raise ValueError('Failed to get TX power cap')
+
+ def get_current_device_state(self, ad, begin_time):
+ """ Returns the device state of the android dut since begin_time.
+
+ Returns the device state of the android dut by parsing logcat since
+ begin_time. Function should follow a function call that forces
+ a SAR state.
+
+ Args:
+ ad: android_device obj.
+ begin_time: time stamp to start.
+
+ Returns:
+ device_state: device state of the android device.
+ """
+
+ device_state_regex = 'updateDeviceState: DeviceState: ([\s*\S+\s]+)'
+ time.sleep(SLEEP_DURATION)
+ device_state = self.parse_bt_logs(ad, begin_time, device_state_regex)
+ if device_state:
+ return device_state
+
+ raise ValueError("Couldn't fetch device state")
+
+ def read_sar_table(self, ad, output_path=''):
+ """Extracts the BT SAR table from the phone.
+
+ Extracts the BT SAR table from the phone into the android device
+ log path directory.
+
+ Args:
+ ad: android_device object.
+ output_path: path to custom sar table
+ Returns:
+ df : BT SAR table (as pandas DataFrame).
+ """
+ if not output_path:
+ output_path = os.path.join(ad.device_log_path, self.sar_file_name)
+ ad.adb.pull('{} {}'.format(self.sar_file_path, output_path))
+
+ df = pd.read_csv(output_path)
+ self.log.info('BT SAR table read from the phone')
+ return df
+
+ def push_table(self, ad, src_path, dest_path=''):
+ """Pushes a BT SAR table to the phone.
+
+ Pushes a BT SAR table to the android device and reboots the device.
+ Also creates a backup file if backup flag is True.
+
+ Args:
+ ad: android_device object.
+ src_path: path to the BT SAR table.
+ """
+ #Copying the to-be-pushed file for logging
+ if os.path.dirname(src_path) != ad.device_log_path:
+ job.run('cp {} {}'.format(src_path, ad.device_log_path))
+
+ #Pushing the file provided in the config
+ if dest_path:
+ ad.push_system_file(src_path, dest_path)
+ else:
+ ad.push_system_file(src_path, self.sar_file_path)
+ self.log.info('BT SAR table pushed')
+ ad.reboot()
+
+ self.bt_sar_df = self.read_sar_table(self.dut, src_path)
+
+ def set_PL10_atten_level(self, ad):
+ """Finds the attenuation level at which the phone is at PL10
+
+ Finds PL10 attenuation level by sweeping the attenuation range.
+ If the power level is not achieved during sweep,
+ returns the max atten level
+
+ Args:
+ ad: android object class
+ Returns:
+ atten : attenuation level when the phone is at PL10
+ """
+ BT_SAR_ATTEN_STEP = 3
+
+ for atten in range(self.atten_min, self.atten_max, BT_SAR_ATTEN_STEP):
+ self.attenuator.set_atten(atten)
+ # Sleep required for BQR to reflect the change in parameters
+ time.sleep(SLEEP_DURATION)
+ metrics = bt_utils.get_bt_metric(ad)
+ if metrics['pwlv'][ad.serial] == 10:
+ self.log.info(
+ 'PL10 located at {}'.format(atten + BT_SAR_ATTEN_STEP))
+ return atten + BT_SAR_ATTEN_STEP
+
+ self.log.warn(
+ "PL10 couldn't be located in the given attenuation range")
diff --git a/acts_tests/acts_contrib/test_utils/bt/GattConnectedBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/GattConnectedBaseTest.py
new file mode 100644
index 0000000..d6fc433
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/GattConnectedBaseTest.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This is base class for tests that exercises different GATT procedures between two connected devices.
+Setup/Teardown methods take care of establishing connection, and doing GATT DB initialization/discovery.
+"""
+
+from queue import Empty
+
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_mtu_size
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_characteristics
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_descriptors
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+
+
+class GattConnectedBaseTest(BluetoothBaseTest):
+
+ TEST_SERVICE_UUID = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B"
+ READABLE_CHAR_UUID = "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8"
+ READABLE_DESC_UUID = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"
+ WRITABLE_CHAR_UUID = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"
+ WRITABLE_DESC_UUID = "76d5ed92-ca81-4edb-bb6b-9f019665fb32"
+ NOTIFIABLE_CHAR_UUID = "b2c83efa-34ca-11e6-ac61-9e71128cae77"
+
+ def setup_class(self):
+ super().setup_class()
+ self.cen_ad = self.android_devices[0]
+ self.per_ad = self.android_devices[1]
+
+ def setup_test(self):
+ super(GattConnectedBaseTest, self).setup_test()
+
+ self.gatt_server_callback, self.gatt_server = self._setup_multiple_services(
+ )
+ if not self.gatt_server_callback or not self.gatt_server:
+ raise AssertionError('Service setup failed')
+
+ self.bluetooth_gatt, self.gatt_callback, self.adv_callback = (
+ orchestrate_gatt_connection(self.cen_ad, self.per_ad))
+ self.per_ad.droid.bleStopBleAdvertising(self.adv_callback)
+
+ self.mtu = gatt_mtu_size['min']
+
+ if self.cen_ad.droid.gattClientDiscoverServices(self.bluetooth_gatt):
+ event = self._client_wait(gatt_event['gatt_serv_disc'])
+ self.discovered_services_index = event['data']['ServicesIndex']
+ services_count = self.cen_ad.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ self.test_service_index = None
+ for i in range(services_count):
+ disc_service_uuid = (
+ self.cen_ad.droid.gattClientGetDiscoveredServiceUuid(
+ self.discovered_services_index, i).upper())
+ if disc_service_uuid == self.TEST_SERVICE_UUID:
+ self.test_service_index = i
+ break
+
+ if not self.test_service_index:
+ print("Service not found")
+ return False
+
+ connected_device_list = self.per_ad.droid.gattServerGetConnectedDevices(
+ self.gatt_server)
+ if len(connected_device_list) == 0:
+ self.log.info("No devices connected from peripheral.")
+ return False
+
+ return True
+
+ def teardown_test(self):
+ self.per_ad.droid.gattServerClearServices(self.gatt_server)
+ self.per_ad.droid.gattServerClose(self.gatt_server)
+
+ del self.gatt_server_callback
+ del self.gatt_server
+
+ self._orchestrate_gatt_disconnection(self.bluetooth_gatt,
+ self.gatt_callback)
+
+ return super(GattConnectedBaseTest, self).teardown_test()
+
+ def _server_wait(self, gatt_event):
+ return self._timed_pop(gatt_event, self.per_ad,
+ self.gatt_server_callback)
+
+ def _client_wait(self, gatt_event):
+ return self._timed_pop(gatt_event, self.cen_ad, self.gatt_callback)
+
+ def _timed_pop(self, gatt_event, droid, gatt_callback):
+ expected_event = gatt_event["evt"].format(gatt_callback)
+ try:
+ return droid.ed.pop_event(expected_event, bt_default_timeout)
+ except Empty as emp:
+ raise AssertionError(gatt_event["err"].format(expected_event))
+
+ def _setup_characteristics_and_descriptors(self, droid):
+ characteristic_input = [
+ {
+ 'uuid': self.WRITABLE_CHAR_UUID,
+ 'property': gatt_characteristic['property_write'] |
+ gatt_characteristic['property_write_no_response'],
+ 'permission': gatt_characteristic['permission_write']
+ },
+ {
+ 'uuid': self.READABLE_CHAR_UUID,
+ 'property': gatt_characteristic['property_read'],
+ 'permission': gatt_characteristic['permission_read']
+ },
+ {
+ 'uuid': self.NOTIFIABLE_CHAR_UUID,
+ 'property': gatt_characteristic['property_notify'] |
+ gatt_characteristic['property_indicate'],
+ 'permission': gatt_characteristic['permission_read']
+ },
+ ]
+ descriptor_input = [{
+ 'uuid': self.WRITABLE_DESC_UUID,
+ 'property': gatt_descriptor['permission_read'] |
+ gatt_characteristic['permission_write'],
+ }, {
+ 'uuid': self.READABLE_DESC_UUID,
+ 'property': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }, {
+ 'uuid': gatt_char_desc_uuids['client_char_cfg'],
+ 'property': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }]
+ characteristic_list = setup_gatt_characteristics(droid,
+ characteristic_input)
+ self.notifiable_char_index = characteristic_list[2]
+ descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+ return characteristic_list, descriptor_list
+
+ def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
+ self.log.info("Disconnecting from peripheral device.")
+ try:
+ disconnect_gatt_connection(self.cen_ad, bluetooth_gatt,
+ gatt_callback)
+ except GattTestUtilsError as err:
+ log.error(err)
+ return False
+ self.cen_ad.droid.gattClientClose(bluetooth_gatt)
+ return True
+
+ def _find_service_added_event(self, gatt_server_callback, uuid):
+ expected_event = gatt_cb_strings['serv_added'].format(
+ gatt_server_callback)
+ try:
+ event = self.per_ad.ed.pop_event(expected_event,
+ bt_default_timeout)
+ except Empty:
+ self.log.error(gatt_cb_err['serv_added_err'].format(
+ expected_event))
+ return False
+ if event['data']['serviceUuid'].lower() != uuid.lower():
+ self.log.error("Uuid mismatch. Found: {}, Expected {}.".format(
+ event['data']['serviceUuid'], uuid))
+ return False
+ return True
+
+ def _setup_multiple_services(self):
+ gatt_server_callback = (
+ self.per_ad.droid.gattServerCreateGattServerCallback())
+ gatt_server = self.per_ad.droid.gattServerOpenGattServer(
+ gatt_server_callback)
+ characteristic_list, descriptor_list = (
+ self._setup_characteristics_and_descriptors(self.per_ad.droid))
+ self.per_ad.droid.gattServerCharacteristicAddDescriptor(
+ characteristic_list[0], descriptor_list[0])
+ self.per_ad.droid.gattServerCharacteristicAddDescriptor(
+ characteristic_list[1], descriptor_list[1])
+ self.per_ad.droid.gattServerCharacteristicAddDescriptor(
+ characteristic_list[2], descriptor_list[2])
+ gatt_service3 = self.per_ad.droid.gattServerCreateService(
+ self.TEST_SERVICE_UUID, gatt_service_types['primary'])
+ for characteristic in characteristic_list:
+ self.per_ad.droid.gattServerAddCharacteristicToService(
+ gatt_service3, characteristic)
+ self.per_ad.droid.gattServerAddService(gatt_server, gatt_service3)
+ result = self._find_service_added_event(gatt_server_callback,
+ self.TEST_SERVICE_UUID)
+ if not result:
+ return False, False
+ return gatt_server_callback, gatt_server
+
+ def assertEqual(self, first, second, msg=None):
+ if not first == second:
+ if not msg:
+ raise AssertionError('%r != %r' % (first, second))
+ else:
+ raise AssertionError(msg + ' %r != %r' % (first, second))
diff --git a/acts_tests/acts_contrib/test_utils/bt/GattEnum.py b/acts_tests/acts_contrib/test_utils/bt/GattEnum.py
new file mode 100644
index 0000000..b65c5f2
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/GattEnum.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from enum import Enum
+from enum import IntEnum
+
+
+class GattCbErr(Enum):
+ CHAR_WRITE_REQ_ERR = "Characteristic Write Request event not found. Expected {}"
+ CHAR_WRITE_ERR = "Characteristic Write event not found. Expected {}"
+ DESC_WRITE_REQ_ERR = "Descriptor Write Request event not found. Expected {}"
+ DESC_WRITE_ERR = "Descriptor Write event not found. Expected {}"
+ CHAR_READ_ERR = "Characteristic Read event not found. Expected {}"
+ CHAR_READ_REQ_ERR = "Characteristic Read Request not found. Expected {}"
+ DESC_READ_ERR = "Descriptor Read event not found. Expected {}"
+ DESC_READ_REQ_ERR = "Descriptor Read Request event not found. Expected {}"
+ RD_REMOTE_RSSI_ERR = "Read Remote RSSI event not found. Expected {}"
+ GATT_SERV_DISC_ERR = "GATT Services Discovered event not found. Expected {}"
+ SERV_ADDED_ERR = "Service Added event not found. Expected {}"
+ MTU_CHANGED_ERR = "MTU Changed event not found. Expected {}"
+ MTU_SERV_CHANGED_ERR = "MTU Server Changed event not found. Expected {}"
+ GATT_CONN_CHANGE_ERR = "GATT Connection Changed event not found. Expected {}"
+ CHAR_CHANGE_ERR = "GATT Characteristic Changed event not fond. Expected {}"
+ PHY_READ_ERR = "Phy Read event not fond. Expected {}"
+ PHY_UPDATE_ERR = "Phy Update event not fond. Expected {}"
+ EXEC_WRITE_ERR = "GATT Execute Write event not found. Expected {}"
+
+
+class GattCbStrings(Enum):
+ CHAR_WRITE_REQ = "GattServer{}onCharacteristicWriteRequest"
+ EXEC_WRITE = "GattServer{}onExecuteWrite"
+ CHAR_WRITE = "GattConnect{}onCharacteristicWrite"
+ DESC_WRITE_REQ = "GattServer{}onDescriptorWriteRequest"
+ DESC_WRITE = "GattConnect{}onDescriptorWrite"
+ CHAR_READ = "GattConnect{}onCharacteristicRead"
+ CHAR_READ_REQ = "GattServer{}onCharacteristicReadRequest"
+ DESC_READ = "GattConnect{}onDescriptorRead"
+ DESC_READ_REQ = "GattServer{}onDescriptorReadRequest"
+ RD_REMOTE_RSSI = "GattConnect{}onReadRemoteRssi"
+ GATT_SERV_DISC = "GattConnect{}onServicesDiscovered"
+ SERV_ADDED = "GattServer{}onServiceAdded"
+ MTU_CHANGED = "GattConnect{}onMtuChanged"
+ MTU_SERV_CHANGED = "GattServer{}onMtuChanged"
+ GATT_CONN_CHANGE = "GattConnect{}onConnectionStateChange"
+ CHAR_CHANGE = "GattConnect{}onCharacteristicChanged"
+ PHY_READ = "GattConnect{}onPhyRead"
+ PHY_UPDATE = "GattConnect{}onPhyUpdate"
+ SERV_PHY_READ = "GattServer{}onPhyRead"
+ SERV_PHY_UPDATE = "GattServer{}onPhyUpdate"
+
+
+class GattEvent(Enum):
+ CHAR_WRITE_REQ = {
+ "evt": GattCbStrings.CHAR_WRITE_REQ.value,
+ "err": GattCbErr.CHAR_WRITE_REQ_ERR.value
+ }
+ EXEC_WRITE = {
+ "evt": GattCbStrings.EXEC_WRITE.value,
+ "err": GattCbErr.EXEC_WRITE_ERR.value
+ }
+ CHAR_WRITE = {
+ "evt": GattCbStrings.CHAR_WRITE.value,
+ "err": GattCbErr.CHAR_WRITE_ERR.value
+ }
+ DESC_WRITE_REQ = {
+ "evt": GattCbStrings.DESC_WRITE_REQ.value,
+ "err": GattCbErr.DESC_WRITE_REQ_ERR.value
+ }
+ DESC_WRITE = {
+ "evt": GattCbStrings.DESC_WRITE.value,
+ "err": GattCbErr.DESC_WRITE_ERR.value
+ }
+ CHAR_READ = {
+ "evt": GattCbStrings.CHAR_READ.value,
+ "err": GattCbErr.CHAR_READ_ERR.value
+ }
+ CHAR_READ_REQ = {
+ "evt": GattCbStrings.CHAR_READ_REQ.value,
+ "err": GattCbErr.CHAR_READ_REQ_ERR.value
+ }
+ DESC_READ = {
+ "evt": GattCbStrings.DESC_READ.value,
+ "err": GattCbErr.DESC_READ_ERR.value
+ }
+ DESC_READ_REQ = {
+ "evt": GattCbStrings.DESC_READ_REQ.value,
+ "err": GattCbErr.DESC_READ_REQ_ERR.value
+ }
+ RD_REMOTE_RSSI = {
+ "evt": GattCbStrings.RD_REMOTE_RSSI.value,
+ "err": GattCbErr.RD_REMOTE_RSSI_ERR.value
+ }
+ GATT_SERV_DISC = {
+ "evt": GattCbStrings.GATT_SERV_DISC.value,
+ "err": GattCbErr.GATT_SERV_DISC_ERR.value
+ }
+ SERV_ADDED = {
+ "evt": GattCbStrings.SERV_ADDED.value,
+ "err": GattCbErr.SERV_ADDED_ERR.value
+ }
+ MTU_CHANGED = {
+ "evt": GattCbStrings.MTU_CHANGED.value,
+ "err": GattCbErr.MTU_CHANGED_ERR.value
+ }
+ GATT_CONN_CHANGE = {
+ "evt": GattCbStrings.GATT_CONN_CHANGE.value,
+ "err": GattCbErr.GATT_CONN_CHANGE_ERR.value
+ }
+ CHAR_CHANGE = {
+ "evt": GattCbStrings.CHAR_CHANGE.value,
+ "err": GattCbErr.CHAR_CHANGE_ERR.value
+ }
+ PHY_READ = {
+ "evt": GattCbStrings.PHY_READ.value,
+ "err": GattCbErr.PHY_READ_ERR.value
+ }
+ PHY_UPDATE = {
+ "evt": GattCbStrings.PHY_UPDATE.value,
+ "err": GattCbErr.PHY_UPDATE_ERR.value
+ }
+ SERV_PHY_READ = {
+ "evt": GattCbStrings.SERV_PHY_READ.value,
+ "err": GattCbErr.PHY_READ_ERR.value
+ }
+ SERV_PHY_UPDATE = {
+ "evt": GattCbStrings.SERV_PHY_UPDATE.value,
+ "err": GattCbErr.PHY_UPDATE_ERR.value
+ }
+
+
+class GattConnectionState(IntEnum):
+ STATE_DISCONNECTED = 0
+ STATE_CONNECTING = 1
+ STATE_CONNECTED = 2
+ STATE_DISCONNECTING = 3
+
+
+class GattCharacteristic(Enum):
+ PROPERTY_BROADCAST = 0x01
+ PROPERTY_READ = 0x02
+ PROPERTY_WRITE_NO_RESPONSE = 0x04
+ PROPERTY_WRITE = 0x08
+ PROPERTY_NOTIFY = 0x10
+ PROPERTY_INDICATE = 0x20
+ PROPERTY_SIGNED_WRITE = 0x40
+ PROPERTY_EXTENDED_PROPS = 0x80
+ PERMISSION_READ = 0x01
+ PERMISSION_READ_ENCRYPTED = 0x02
+ PERMISSION_READ_ENCRYPTED_MITM = 0x04
+ PERMISSION_WRITE = 0x10
+ PERMISSION_WRITE_ENCRYPTED = 0x20
+ PERMISSION_WRITE_ENCRYPTED_MITM = 0x40
+ PERMISSION_WRITE_SIGNED = 0x80
+ PERMISSION_WRITE_SIGNED_MITM = 0x100
+ WRITE_TYPE_DEFAULT = 0x02
+ WRITE_TYPE_NO_RESPONSE = 0x01
+ WRITE_TYPE_SIGNED = 0x04
+ FORMAT_UINT8 = 0x11
+ FORMAT_UINT16 = 0x12
+ FORMAT_UINT32 = 0x14
+ FORMAT_SINT8 = 0x21
+ FORMAT_SINT16 = 0x22
+ FORMAT_SINT32 = 0x24
+ FORMAT_SFLOAT = 0x32
+ FORMAT_FLOAT = 0x34
+
+
+class GattDescriptor(Enum):
+ ENABLE_NOTIFICATION_VALUE = [0x01, 0x00]
+ ENABLE_INDICATION_VALUE = [0x02, 0x00]
+ DISABLE_NOTIFICATION_VALUE = [0x00, 0x00]
+ PERMISSION_READ = 0x01
+ PERMISSION_READ_ENCRYPTED = 0x02
+ PERMISSION_READ_ENCRYPTED_MITM = 0x04
+ PERMISSION_WRITE = 0x10
+ PERMISSION_WRITE_ENCRYPTED = 0x20
+ PERMISSION_WRITE_ENCRYPTED_MITM = 0x40
+ PERMISSION_WRITE_SIGNED = 0x80
+ PERMISSION_WRITE_SIGNED_MITM = 0x100
+
+
+class GattCharDesc(Enum):
+ GATT_CHARAC_EXT_PROPER_UUID = '00002900-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_USER_DESC_UUID = '00002901-0000-1000-8000-00805f9b34fb'
+ GATT_CLIENT_CHARAC_CFG_UUID = '00002902-0000-1000-8000-00805f9b34fb'
+ GATT_SERVER_CHARAC_CFG_UUID = '00002903-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_FMT_UUID = '00002904-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_AGREG_FMT_UUID = '00002905-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_VALID_RANGE_UUID = '00002906-0000-1000-8000-00805f9b34fb'
+ GATT_EXTERNAL_REPORT_REFERENCE = '00002907-0000-1000-8000-00805f9b34fb'
+ GATT_REPORT_REFERENCE = '00002908-0000-1000-8000-00805f9b34fb'
+
+
+class GattCharTypes(Enum):
+ GATT_CHARAC_DEVICE_NAME = '00002a00-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_APPEARANCE = '00002a01-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_PERIPHERAL_PRIV_FLAG = '00002a02-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_RECONNECTION_ADDRESS = '00002a03-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_PERIPHERAL_PREF_CONN = '00002a04-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_SERVICE_CHANGED = '00002a05-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_SYSTEM_ID = '00002a23-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_MODEL_NUMBER_STRING = '00002a24-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_SERIAL_NUMBER_STRING = '00002a25-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_FIRMWARE_REVISION_STRING = '00002a26-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_HARDWARE_REVISION_STRING = '00002a27-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_SOFTWARE_REVISION_STRING = '00002a28-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_MANUFACTURER_NAME_STRING = '00002a29-0000-1000-8000-00805f9b34fb'
+ GATT_CHARAC_PNP_ID = '00002a50-0000-1000-8000-00805f9b34fb'
+
+
+class GattCharacteristicAttrLength(Enum):
+ MTU_ATTR_1 = 1
+ MTU_ATTR_2 = 3
+ MTU_ATTR_3 = 15
+
+
+class CharacteristicValueFormat(Enum):
+ STRING = 0x1
+ BYTE = 0x2
+ FORMAT_SINT8 = 0x21
+ FORMAT_UINT8 = 0x11
+ FORMAT_SINT16 = 0x22
+ FORMAT_UINT16 = 0x12
+ FORMAT_SINT32 = 0x24
+ FORMAT_UINT32 = 0x14
+
+
+class GattService(IntEnum):
+ SERVICE_TYPE_PRIMARY = 0
+ SERVICE_TYPE_SECONDARY = 1
+
+
+class GattConnectionPriority(IntEnum):
+ CONNECTION_PRIORITY_BALANCED = 0
+ CONNECTION_PRIORITY_HIGH = 1
+ CONNECTION_PRIORITY_LOW_POWER = 2
+
+
+class MtuSize(IntEnum):
+ MIN = 23
+ MAX = 217
+
+
+class GattCharacteristicAttrLength(IntEnum):
+ MTU_ATTR_1 = 1
+ MTU_ATTR_2 = 3
+ MTU_ATTR_3 = 15
+
+
+class BluetoothGatt(Enum):
+ GATT_SUCCESS = 0
+ GATT_FAILURE = 0x101
+
+
+class GattTransport(IntEnum):
+ TRANSPORT_AUTO = 0x00
+ TRANSPORT_BREDR = 0x01
+ TRANSPORT_LE = 0x02
+
+
+class GattPhy(IntEnum):
+ PHY_LE_1M = 1
+ PHY_LE_2M = 2
+ PHY_LE_CODED = 3
+
+
+class GattPhyMask(IntEnum):
+ PHY_LE_1M_MASK = 1
+ PHY_LE_2M_MASK = 2
+ PHY_LE_CODED_MASK = 4
+
+
+# TODO Decide whether to continue with Enums or move to dictionaries
+GattServerResponses = {
+ "GATT_SUCCESS": 0x0,
+ "GATT_FAILURE": 0x1,
+ "GATT_READ_NOT_PERMITTED": 0x2,
+ "GATT_WRITE_NOT_PERMITTED": 0x3,
+ "GATT_INVALID_PDU": 0x4,
+ "GATT_INSUFFICIENT_AUTHENTICATION": 0x5,
+ "GATT_REQUEST_NOT_SUPPORTED": 0x6,
+ "GATT_INVALID_OFFSET": 0x7,
+ "GATT_INSUFFICIENT_AUTHORIZATION": 0x8,
+ "GATT_INVALID_ATTRIBUTE_LENGTH": 0xD,
+ "GATT_INSUFFICIENT_ENCRYPTION": 0xF,
+ "GATT_CONNECTION_CONGESTED": 0x8F,
+ "GATT_13_ERR": 0x13,
+ "GATT_12_ERR": 0x12,
+ "GATT_0C_ERR": 0x0C,
+ "GATT_16": 0x16
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/__init__.py b/acts_tests/acts_contrib/test_utils/bt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/bt/ble_lib.py b/acts_tests/acts_contrib/test_utils/bt/ble_lib.py
new file mode 100644
index 0000000..4fac563
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/ble_lib.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Ble libraries
+"""
+
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import small_timeout
+from acts_contrib.test_utils.bt.bt_constants import adv_fail
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_on_own_address_read
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_started
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+
+import time
+import os
+
+
+class BleLib():
+ def __init__(self, log, dut):
+ self.advertisement_list = []
+ self.dut = dut
+ self.log = log
+ self.default_timeout = 5
+ self.set_advertisement_list = []
+ self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb"
+
+ def _verify_ble_adv_started(self, advertise_callback):
+ """Helper for verifying if an advertisment started or not"""
+ regex = "({}|{})".format(
+ adv_succ.format(advertise_callback),
+ adv_fail.format(advertise_callback))
+ try:
+ event = self.dut.ed.pop_events(regex, 5, small_timeout)
+ except Empty:
+ self.dut.log.error("Failed to get success or failed event.")
+ return
+ if event[0]["name"] == adv_succ.format(advertise_callback):
+ self.dut.log.info("Advertisement started successfully.")
+ return True
+ else:
+ self.dut.log.info("Advertisement failed to start.")
+ return False
+
+ def start_generic_connectable_advertisement(self, line):
+ """Start a connectable LE advertisement"""
+ scan_response = None
+ if line:
+ scan_response = bool(line)
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ advertise_callback, advertise_data, advertise_settings = (
+ generate_ble_advertise_objects(self.dut.droid))
+ if scan_response:
+ self.dut.droid.bleStartBleAdvertisingWithScanResponse(
+ advertise_callback, advertise_data, advertise_settings,
+ advertise_data)
+ else:
+ self.dut.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+
+ def start_connectable_advertisement_set(self, line):
+ """Start Connectable Advertisement Set"""
+ adv_callback = self.dut.droid.bleAdvSetGenCallback()
+ adv_data = {
+ "includeDeviceName": True,
+ }
+ self.dut.droid.bleAdvSetStartAdvertisingSet(
+ {
+ "connectable": True,
+ "legacyMode": False,
+ "primaryPhy": "PHY_LE_1M",
+ "secondaryPhy": "PHY_LE_1M",
+ "interval": 320
+ }, adv_data, None, None, None, 0, 0, adv_callback)
+ evt = self.dut.ed.pop_event(
+ advertising_set_started.format(adv_callback), self.default_timeout)
+ set_id = evt['data']['setId']
+ self.log.error("did not receive the set started event!")
+ evt = self.dut.ed.pop_event(
+ advertising_set_on_own_address_read.format(set_id),
+ self.default_timeout)
+ address = evt['data']['address']
+ self.log.info("Advertiser address is: {}".format(str(address)))
+ self.set_advertisement_list.append(adv_callback)
+
+ def stop_all_advertisement_set(self, line):
+ """Stop all Advertisement Sets"""
+ for adv in self.set_advertisement_list:
+ try:
+ self.dut.droid.bleAdvSetStopAdvertisingSet(adv)
+ except Exception as err:
+ self.log.error("Failed to stop advertisement: {}".format(err))
+
+ def adv_add_service_uuid_list(self, line):
+ """Add service UUID to the LE advertisement inputs:
+ [uuid1 uuid2 ... uuidN]"""
+ uuids = line.split()
+ uuid_list = []
+ for uuid in uuids:
+ if len(uuid) == 4:
+ uuid = self.generic_uuid.format(line)
+ uuid_list.append(uuid)
+ self.dut.droid.bleSetAdvertiseDataSetServiceUuids(uuid_list)
+
+ def adv_data_include_local_name(self, is_included):
+ """Include local name in the advertisement. inputs: [true|false]"""
+ self.dut.droid.bleSetAdvertiseDataIncludeDeviceName(bool(is_included))
+
+ def adv_data_include_tx_power_level(self, is_included):
+ """Include tx power level in the advertisement. inputs: [true|false]"""
+ self.dut.droid.bleSetAdvertiseDataIncludeTxPowerLevel(
+ bool(is_included))
+
+ def adv_data_add_manufacturer_data(self, line):
+ """Include manufacturer id and data to the advertisment:
+ [id data1 data2 ... dataN]"""
+ info = line.split()
+ manu_id = int(info[0])
+ manu_data = []
+ for data in info[1:]:
+ manu_data.append(int(data))
+ self.dut.droid.bleAddAdvertiseDataManufacturerId(manu_id, manu_data)
+
+ def start_generic_nonconnectable_advertisement(self, line):
+ """Start a nonconnectable LE advertisement"""
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(False)
+ advertise_callback, advertise_data, advertise_settings = (
+ generate_ble_advertise_objects(self.dut.droid))
+ self.dut.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+
+ def stop_all_advertisements(self, line):
+ """Stop all LE advertisements"""
+ for callback_id in self.advertisement_list:
+ self.log.info("Stopping Advertisement {}".format(callback_id))
+ self.dut.droid.bleStopBleAdvertising(callback_id)
+ time.sleep(1)
+ self.advertisement_list = []
+
+ def ble_stop_advertisement(self, callback_id):
+ """Stop an LE advertisement"""
+ if not callback_id:
+ self.log.info("Need a callback ID")
+ return
+ callback_id = int(callback_id)
+ if callback_id not in self.advertisement_list:
+ self.log.info("Callback not in list of advertisements.")
+ return
+ self.dut.droid.bleStopBleAdvertising(callback_id)
+ self.advertisement_list.remove(callback_id)
+
+ def start_max_advertisements(self, line):
+ scan_response = None
+ if line:
+ scan_response = bool(line)
+ while (True):
+ try:
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ advertise_callback, advertise_data, advertise_settings = (
+ generate_ble_advertise_objects(self.dut.droid))
+ if scan_response:
+ self.dut.droid.bleStartBleAdvertisingWithScanResponse(
+ advertise_callback, advertise_data, advertise_settings,
+ advertise_data)
+ else:
+ self.dut.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+ else:
+ self.log.info("Advertisements active: {}".format(
+ len(self.advertisement_list)))
+ return False
+ except Exception as err:
+ self.log.info("Advertisements active: {}".format(
+ len(self.advertisement_list)))
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/bt/ble_performance_test_utils.py b/acts_tests/acts_contrib/test_utils/bt/ble_performance_test_utils.py
new file mode 100644
index 0000000..394e962
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/ble_performance_test_utils.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+import statistics
+from queue import Empty
+from concurrent.futures import ThreadPoolExecutor
+
+from acts_contrib.test_utils.bt.bt_gatt_utils import close_gatt_client
+from acts_contrib.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+
+default_event_timeout = 10
+rssi_read_duration = 25
+
+
+def establish_ble_connection(client_ad, server_ad):
+ """Function to establish BLE connection between two BLE devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ Returns:
+ bluetooth_gatt: GATT object
+ gatt_callback: Gatt callback object
+ adv_callback: advertisement callback object
+ gatt_server: the gatt server
+ """
+ gatt_server_cb = server_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = server_ad.droid.gattServerOpenGattServer(gatt_server_cb)
+ try:
+ bluetooth_gatt, gatt_callback, adv_callback = (
+ orchestrate_gatt_connection(client_ad, server_ad))
+ except GattTestUtilsError as err:
+ logging.error(err)
+ return False
+ return bluetooth_gatt, gatt_callback, adv_callback, gatt_server
+
+
+def read_ble_rssi(client_ad, gatt_server, gatt_callback):
+ """Function to Read BLE RSSI of the remote BLE device.
+ Args:
+ client_ad: the Android device performing the connection.
+ gatt_server: the gatt server
+ gatt_callback:the gatt connection call back object
+ Returns:
+ ble_rssi: RSSI value of the remote BLE device
+ """
+ AVG_RSSI = []
+ end_time = time.time() + rssi_read_duration
+ logging.info("Reading BLE RSSI for {} sec".format(rssi_read_duration))
+ while time.time() < end_time:
+ expected_event = gatt_cb_strings['rd_remote_rssi'].format(
+ gatt_callback)
+ read_rssi = client_ad.droid.gattClientReadRSSI(gatt_server)
+ if read_rssi:
+ try:
+ event = client_ad.ed.pop_event(expected_event,
+ default_event_timeout)
+ except Empty:
+ logging.error(
+ gatt_cb_err['rd_remote_rssi_err'].format(expected_event))
+ return False
+ rssi_value = event['data']['Rssi']
+ AVG_RSSI.append(rssi_value)
+ logging.debug("First & Last reading of RSSI :{:03d} & {:03d}".format(
+ AVG_RSSI[0], AVG_RSSI[-1]))
+ ble_rssi = statistics.mean(AVG_RSSI)
+ ble_rssi = round(ble_rssi, 2)
+
+ return ble_rssi
+
+
+def ble_coc_connection(client_ad, server_ad):
+ """Sets up the CoC connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+
+ Returns:
+ True if connection was successful or false if unsuccessful,
+ gatt_callback: GATT callback object
+ client connection ID: Client connection ID
+ and server connection ID : server connection ID
+ """
+ #secured_conn: True if using secured connection
+ #le_connection_interval: LE Connection interval. 0 means use default.
+ #buffer_size : is the number of bytes per L2CAP data buffer
+ #le_tx_data_length: LE Data Length used by BT Controller to transmit.
+ is_secured = False
+ le_connection_interval = 30
+ buffer_size = 240
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ gatt_server_cb = server_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = server_ad.droid.gattServerOpenGattServer(gatt_server_cb)
+
+ logging.info(
+ "orchestrate_ble_coc_connection. is_secured={}, Connection Interval={}msec, "
+ "buffer_size={}bytes".format(is_secured, le_connection_interval,
+ buffer_size))
+ try:
+ status, client_conn_id, server_conn_id, bluetooth_gatt, gatt_callback = orchestrate_coc_connection(
+ client_ad,
+ server_ad,
+ True,
+ is_secured,
+ le_connection_interval,
+ le_tx_data_length,
+ gatt_disconnection=False)
+ except Exception as err:
+ logging.info("Failed to esatablish COC connection".format(err))
+ return 0
+ return True, gatt_callback, gatt_server, bluetooth_gatt, client_conn_id
+
+
+def run_ble_throughput(server_ad, client_conn_id, client_ad,
+ num_iterations=30):
+ """Function to measure Throughput from one client to one-or-many servers
+
+ Args:
+ server_ad: the Android device accepting the connection.
+ client_conn_id: the client connection ID.
+ client_ad: the Android device performing the connection.
+ num_iterations: The num_iterations is that number of repetitions of each
+ set of buffers r/w.
+ Returns:
+ data_rate: Throughput in terms of bytes per second, 0 if test failed.
+ """
+ # number_buffers is the total number of data buffers to transmit per
+ # set of buffers r/w.
+ # buffer_size is the number of bytes per L2CAP data buffer.
+ number_buffers = 100
+ buffer_size = 240
+ list_server_ad = [server_ad]
+ list_client_conn_id = [client_conn_id]
+ data_rate = do_multi_connection_throughput(client_ad, list_server_ad,
+ list_client_conn_id,
+ num_iterations, number_buffers,
+ buffer_size)
+ if data_rate <= 0:
+ return False
+ data_rate = data_rate * 8
+ logging.info(
+ "run_ble_coc_connection_throughput: throughput=%d bites per sec",
+ data_rate)
+ return data_rate
+
+
+def run_ble_throughput_and_read_rssi(client_ad, server_ad, client_conn_id,
+ gatt_server, gatt_callback):
+ """Function to measure ble rssi while sendinng data from client to server
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ client_conn_id: the client connection ID.
+ gatt_server: the gatt server
+ gatt_callback: Gatt callback object
+ Returns:
+ ble_rssi: RSSI value of the remote BLE device.
+ """
+ executor = ThreadPoolExecutor(2)
+ ble_throughput = executor.submit(run_ble_throughput, client_ad,
+ client_conn_id, server_ad)
+ ble_rssi = executor.submit(read_ble_rssi, server_ad, gatt_server,
+ gatt_callback)
+ logging.info("BLE RSSI is:{} dBm with data rate={} bites per sec ".format(
+ ble_rssi.result(), ble_throughput.result()))
+ return ble_rssi.result()
+
+
+def ble_gatt_disconnection(client_ad, bluetooth_gatt, gatt_callback):
+ """Function to disconnect GATT connection between client and server.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ bluetooth_gatt: GATT object
+ gatt_callback:the gatt connection call back object
+ Returns:
+ ble_rssi: RSSI value of the remote BLE device
+ """
+ logging.info("Disconnecting from peripheral device.")
+ try:
+ disconnect_gatt_connection(client_ad, bluetooth_gatt, gatt_callback)
+ close_gatt_client(client_ad, bluetooth_gatt)
+ except GattTestUtilsError as err:
+ logging.error(err)
+ return False
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py b/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py
new file mode 100644
index 0000000..14a42b0
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py
@@ -0,0 +1,830 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import time
+import os
+
+from acts.keys import Config
+from acts.utils import rand_ascii_str
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import logcat_strings
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_BLUETOOTH
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_voice_utils import get_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.utils import exe_cmd
+from acts.utils import get_current_epoch_time
+
+KEYCODE_VOLUME_UP = "input keyevent 24"
+KEYCODE_VOLUME_DOWN = "input keyevent 25"
+KEYCODE_EVENT_PLAY_PAUSE = "input keyevent 85"
+KEYCODE_MEDIA_STOP = "input keyevent 86"
+KEYCODE_EVENT_NEXT = "input keyevent 87"
+KEYCODE_EVENT_PREVIOUS = "input keyevent 88"
+KEYCODE_MEDIA_REWIND = "input keyevent 89"
+KEYCODE_MEDIA_FAST_FORWARD = "input keyevent 90"
+KEYCODE_MUTE = "input keyevent 91"
+
+default_timeout = 10
+
+
+class E2eBtCarkitLib():
+
+ android_devices = []
+ short_timeout = 3
+ active_call_id = None
+ hold_call_id = None
+ log = None
+ mac_address = None
+
+ def __init__(self, log, target_mac_address=None):
+ self.log = log
+ self.target_mac_address = target_mac_address
+
+ def connect_hsp_helper(self, ad):
+ end_time = time.time() + default_timeout + 10
+ connected_hsp_devices = len(ad.droid.bluetoothHspGetConnectedDevices())
+ while connected_hsp_devices != 1 and time.time() < end_time:
+ try:
+ ad.droid.bluetoothHspConnect(self.target_mac_address)
+ time.sleep(3)
+ if len(ad.droid.bluetoothHspGetConnectedDevices() == 1):
+ break
+ except Exception:
+ self.log.debug("Failed to connect hsp trying again...")
+ try:
+ ad.droid.bluetoothConnectBonded(self.target_mac_address)
+ except Exception:
+ self.log.info("Failed to connect to bonded device...")
+ connected_hsp_devices = len(
+ ad.droid.bluetoothHspGetConnectedDevices())
+ if connected_hsp_devices != 1:
+ self.log.error("Failed to reconnect to HSP service...")
+ return False
+ self.log.info("Connected to HSP service...")
+ return True
+
+ def setup_multi_call(self, caller0, caller1, callee):
+ outgoing_num = get_phone_number(self.log, callee)
+ if not initiate_call(self.log, caller0, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, callee):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ if not initiate_call(self.log, caller1, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, callee):
+ self.log.error("Failed to answer call.")
+ return False
+ return True
+
+ def process_tests(self, tests):
+ for test in tests:
+ try:
+ test()
+ except Exception as err:
+ self.log.error(err)
+
+ def run_suite_hfp_tests(self):
+ tests = [
+ self.outgoing_call_private_number,
+ self.outgoing_call_unknown_contact,
+ self.incomming_call_private_number,
+ self.incomming_call_unknown_contact,
+ self.outgoing_call_multiple_iterations,
+ self.outgoing_call_hsp_disabled_then_enabled_during_call,
+ self.call_audio_routes,
+ self.sms_during_incomming_call,
+ self.multi_incomming_call,
+ self.multi_call_audio_routing,
+ self.multi_call_swap_multiple_times,
+ self.outgoing_call_a2dp_play_before_and_after,
+ ]
+ _process_tests(tests)
+
+ def run_suite_hfp_conf_tests(self):
+ tests = [
+ self.multi_call_join_conference_call,
+ self.multi_call_join_conference_call_hangup_conf_call,
+ self.outgoing_multi_call_join_conference_call,
+ self.multi_call_join_conference_call_audio_routes,
+ ]
+ _process_tests(tests)
+
+ def run_suite_map_tests(self):
+ tests = [
+ self.sms_receive_different_sizes,
+ self.sms_receive_multiple,
+ self.sms_send_outgoing_texts,
+ ]
+ _process_tests(tests)
+
+ def run_suite_avrcp_tests(self):
+ tests = [
+ self.avrcp_play_pause,
+ self.avrcp_next_previous_song,
+ self.avrcp_next_previous,
+ self.avrcp_next_repetative,
+ ]
+ _process_tests(tests)
+
+ def disconnect_reconnect_multiple_iterations(self, pri_dut):
+ iteration_count = 5
+ self.log.info(
+ "Test disconnect-reconnect scenario from phone {} times.".format(
+ iteration_count))
+ self.log.info(
+ "This test will prompt for user interaction after each reconnect.")
+ input("Press enter to execute this testcase...")
+ #Assumes only one devices connected
+ grace_timeout = 4 #disconnect and reconnect timeout
+ for n in range(iteration_count):
+ self.log.info("Test iteration {}.".format(n + 1))
+ self.log.info("Disconnecting device {}...".format(
+ self.target_mac_address))
+ pri_dut.droid.bluetoothDisconnectConnected(self.target_mac_address)
+ # May have to do a longer sleep for carkits.... need to test
+ time.sleep(grace_timeout)
+ self.log.info("Connecting device {}...".format(
+ self.target_mac_address))
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ if not self.connect_hsp_helper(pri_dut):
+ return False
+ start_time = time.time()
+ connected_devices = pri_dut.droid.bluetoothGetConnectedDevices()
+ self.log.info(
+ "Waiting up to 10 seconds for device to reconnect...")
+ while time.time() < start_time + 10 and len(connected_devices) != 1:
+ connected_devices = pri_dut.droid.bluetoothGetConnectedDevices(
+ )
+ time.sleep(1)
+ if len(connected_devices) != 1:
+ self.log.error(
+ "Failed to reconnect at iteration {}... continuing".format(
+ n))
+ return False
+ input("Continue to next iteration?")
+ return True
+
+ def disconnect_a2dp_only_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect a2dp only scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothA2dpDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect A2DP service...")
+ return False
+ time.sleep(self.short_timeout)
+ result = input("Confirm A2DP disconnected? (Y/n) ")
+ if result == "n":
+ self.log.error(
+ "Tester confirmed that A2DP did not disconnect. Failing test.")
+ return False
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from A2DP service")
+ return False
+ pri_dut.droid.bluetoothA2dpConnect(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to A2DP service...")
+ return False
+ return True
+
+ def disconnect_hsp_only_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect hsp only scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ result = input("Confirm HFP disconnected? (Y/n) ")
+ pri_dut.droid.bluetoothHspConnect(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 1:
+ self.log.error("Failed to connect from HSP service")
+ return False
+ return True
+
+ def disconnect_both_hsp_and_a2dp_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect hsp and a2dp scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothA2dpDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect A2DP service...")
+ return False
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from A2DP service")
+ return False
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from HSP service")
+ return False
+ result = input("Confirm HFP and A2DP disconnected? (Y/n) ")
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to A2DP service...")
+ return False
+ if not self.connect_hsp_helper(pri_dut):
+ return False
+ return True
+
+ def outgoing_call_private_number(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test outgoing call scenario from phone to private number")
+ input("Press enter to execute this testcase...")
+ outgoing_num = "*67" + get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_a2dp_play_before_and_after(self, pri_dut, sec_dut):
+ self.log.info(
+ "Test outgoing call scenario while playing music. Music should resume after call."
+ )
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in a play state..."
+ )
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter when music continues to play.")
+ self.log.info("Pausing Music...")
+ pri_dut.adb.shell(KEYCODE_EVENT_PLAY_PAUSE)
+ return True
+
+ def outgoing_call_unknown_contact(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test outgoing call scenario from phone to unknow contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def incomming_call_private_number(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from private number")
+ input("Press enter to execute this testcase...")
+ outgoing_num = "*67" + get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, ter_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+
+ return True
+
+ def incomming_call_unknown_contact(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from unknown contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, ter_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_multiple_iterations(self, pri_dut, sec_dut):
+ iteration_count = 3
+ self.log.info(
+ "Test outgoing call scenario from phone {} times from known contact".
+ format(iteration_count))
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ for _ in range(iteration_count):
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_hsp_disabled_then_enabled_during_call(
+ self, pri_dut, sec_dut):
+ self.log.info(
+ "Test outgoing call hsp disabled then enable during call.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from HSP service")
+ return False
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ time.sleep(default_timeout)
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ test_result = True
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to HSP service...")
+ return
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return test_result
+
+ def call_audio_routes(self, pri_dut, sec_dut):
+ self.log.info("Test various audio routes scenario from phone.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ call_id = pri_dut.droid.telecomCallGetCallIds()[0]
+ pri_dut.droid.telecomCallPlayDtmfTone(call_id, "9")
+ input("Press enter to switch to speaker...")
+ self.log.info("Switching to speaker.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_SPEAKER)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_SPEAKER:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_SPEAKER))
+ return False
+ input("Press enter to switch to earpiece...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup call...")
+ self.log.info("Hanging up call...")
+ pri_dut.droid.telecomCallStopDtmfTone(call_id)
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def sms_receive_different_sizes(self, pri_dut, sec_dut):
+ self.log.info("Test recieve sms.")
+ input("Press enter to execute this testcase...")
+ msg = [rand_ascii_str(50), rand_ascii_str(1), rand_ascii_str(500)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_receive_multiple(self, pri_dut, sec_dut):
+ text_count = 10
+ self.log.info(
+ "Test sending {} sms messages to phone.".format(text_count))
+ input("Press enter to execute this testcase...")
+ for _ in range(text_count):
+ msg = [rand_ascii_str(50)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info(
+ "Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_send_outgoing_texts(self, pri_dut, sec_dut):
+ self.log.info("Test send sms of different sizes.")
+ input("Press enter to execute this testcase...")
+ msg = [rand_ascii_str(50), rand_ascii_str(1), rand_ascii_str(500)]
+ if not sms_send_receive_verify(self.log, pri_dut, sec_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_during_incomming_call(self, pri_dut, sec_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from unknown contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, sec_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ msg = [rand_ascii_str(10)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_incomming_call(self, pri_dut, sec_dut, ter_dut):
+ self.log.info("Test 2 incomming calls scenario to phone.")
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_audio_routing(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone, then test audio routing."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to switch to earpiece...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_swap_multiple_times(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone, then test audio routing."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to swap active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ if not swap_calls(self.log, [pri_dut, sec_dut, ter_dut], calls[0],
+ calls[1], 5):
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls.")
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call_hangup_conf_call(
+ self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls, then terminate the call from the primary dut."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup conf call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_multi_call_join_conference_call(self, pri_dut, sec_dut,
+ ter_dut):
+ self.log.info(
+ "Test 2 outgoing calls scenario from phone then join the calls.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ outgoing_num = get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call_audio_routes(self, pri_dut, sec_dut,
+ ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls, then test different audio routes."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to switch to phone speaker...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup conf call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def avrcp_play_pause(self, pri_dut):
+ play_pause_count = 5
+ self.log.info(
+ "Test AVRCP play/pause {} times.".format(play_pause_count))
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ for i in range(play_pause_count):
+ input("Execute iteration {}?".format(i + 1))
+ pri_dut.adb.shell(KEYCODE_EVENT_PLAY_PAUSE)
+ self.log.info("Test should end in a paused state.")
+ return True
+
+ def avrcp_next_previous_song(self, pri_dut):
+ self.log.info("Test AVRCP go to the next song then the previous song.")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ self.log.info("Hitting Next input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ input("Press enter to go to the previous song")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ self.log.info("Test should end on original song.")
+ return True
+
+ def avrcp_next_previous(self, pri_dut):
+ self.log.info(
+ "Test AVRCP go to the next song then the press previous after a few seconds."
+ )
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ self.log.info("Hitting Next input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ time.sleep(5)
+ self.log.info("Hitting Previous input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ self.log.info("Test should end on \"next\" song.")
+ return True
+
+ def avrcp_next_repetative(self, pri_dut):
+ iterations = 10
+ self.log.info("Test AVRCP go to the next {} times".format(iterations))
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ for i in range(iterations):
+ self.log.info(
+ "Hitting Next input event, iteration {}...".format(i + 1))
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ # Allow time for the carkit to update.
+ time.sleep(1)
+ return True
+
+ def _cycle_aboslute_volume_control_helper(self, volume_step,
+ android_volume_steps, pri_dut):
+ begin_time = get_current_epoch_time()
+ pri_dut.droid.setMediaVolume(volume_step)
+ percentage_to_set = int((volume_step / android_volume_steps) * 100)
+ self.log.info("Setting phone volume to {}%".format(percentage_to_set))
+ volume_info_logcat = pri_dut.search_logcat(
+ logcat_strings['media_playback_vol_changed'], begin_time)
+ if len(volume_info_logcat) > 1:
+ self.log.info("Instant response detected.")
+ carkit_response = volume_info_logcat[-1]['log_message'].split(',')
+ for item in carkit_response:
+ if " volume=" in item:
+ carkit_vol_response = int((
+ int(item.split("=")[-1]) / android_volume_steps) * 100)
+ self.log.info(
+ "Carkit set volume to {}%".format(carkit_vol_response))
+ result = input(
+ "Did volume change reflect properly on carkit and phone? (Y/n) "
+ ).lower()
+
+ def cycle_absolute_volume_control(self, pri_dut):
+ result = input(
+ "Does carkit support Absolute Volume Control? (Y/n) ").lower()
+ if result == "n":
+ return True
+ android_volume_steps = 25
+ for i in range(android_volume_steps):
+ self._cycle_aboslute_volume_control_helper(i, android_volume_steps,
+ pri_dut)
+ for i in reversed(range(android_volume_steps)):
+ self._cycle_aboslute_volume_control_helper(i, android_volume_steps,
+ pri_dut)
+ return True
+
+ def cycle_battery_level(self, pri_dut):
+ for i in range(11):
+ level = i * 10
+ pri_dut.shell.set_battery_level(level)
+ question = "Phone battery level {}. Has the carkit indicator " \
+ "changed? (Y/n) "
+ result = input(question.format(level)).lower()
+
+ def test_voice_recognition_from_phone(self, pri_dut):
+ result = input(
+ "Does carkit support voice recognition (BVRA)? (Y/n) ").lower()
+ if result == "n":
+ return True
+ input("Press enter to start voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStartVoiceRecognition(
+ self.target_mac_address)
+ input("Press enter to stop voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStopVoiceRecognition(
+ self.target_mac_address)
+
+ def test_audio_and_voice_recognition_from_phone(self, pri_dut):
+ result = input(
+ "Does carkit support voice recognition (BVRA)? (Y/n) ").lower()
+ if result == "n":
+ return True
+ # Start playing music here
+ input("Press enter to start voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStartVoiceRecognition(
+ self.target_mac_address)
+ input("Press enter to stop voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStopVoiceRecognition(
+ self.target_mac_address)
+ time.sleep(2)
+ result = input("Did carkit continue music playback after? (Y/n) ")
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_coc_test_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_coc_test_utils.py
new file mode 100644
index 0000000..14d3e2c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_coc_test_utils.py
@@ -0,0 +1,299 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+from acts import utils
+
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+from acts_contrib.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts_contrib.test_utils.bt.bt_constants import default_le_connection_interval_ms
+from acts_contrib.test_utils.bt.bt_constants import default_le_data_length
+from acts_contrib.test_utils.bt.bt_constants import gatt_phy
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts_contrib.test_utils.bt.bt_constants import le_connection_event_time_step_ms
+from acts_contrib.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
+from acts_contrib.test_utils.bt.bt_constants import le_default_supervision_timeout
+from acts_contrib.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+
+log = logging
+
+
+class BtCoCTestUtilsError(Exception):
+ pass
+
+
+def do_multi_connection_throughput(client_ad, list_server_ad,
+ list_client_conn_id, num_iterations,
+ number_buffers, buffer_size):
+ """Throughput measurements from one client to one-or-many servers.
+
+ Args:
+ client_ad: the Android device to perform the write.
+ list_server_ad: the list of Android server devices connected to this client.
+ list_client_conn_id: list of client connection IDs
+ num_iterations: the number of test repetitions.
+ number_buffers: the total number of data buffers to transmit per test.
+ buffer_size: the number of bytes per L2CAP data buffer.
+
+ Returns:
+ Throughput in terms of bytes per second, 0 if test failed.
+ """
+
+ total_num_bytes = 0
+ start_write_time = time.perf_counter()
+ client_ad.log.info(
+ "do_multi_connection_throughput: Before write. Start Time={:f}, "
+ "num_iterations={}, number_buffers={}, buffer_size={}, "
+ "number_buffers*buffer_size={}, num_servers={}".format(
+ start_write_time, num_iterations, number_buffers, buffer_size,
+ number_buffers * buffer_size, len(list_server_ad)))
+
+ if (len(list_server_ad) != len(list_client_conn_id)):
+ client_ad.log.error("do_multi_connection_throughput: invalid "
+ "parameters. Num of list_server_ad({}) != "
+ "list_client_conn({})".format(
+ len(list_server_ad), len(list_client_conn_id)))
+ return 0
+
+ try:
+ for _, client_conn_id in enumerate(list_client_conn_id):
+ client_ad.log.info("do_multi_connection_throughput: "
+ "client_conn_id={}".format(client_conn_id))
+ # Plumb the tx data queue with the first set of data buffers.
+ client_ad.droid.bluetoothConnectionThroughputSend(
+ number_buffers, buffer_size, client_conn_id)
+ except Exception as err:
+ client_ad.log.error("Failed to write data: {}".format(err))
+ return 0
+
+ # Each Loop iteration will write and read one set of buffers.
+ for _ in range(0, (num_iterations - 1)):
+ try:
+ for _, client_conn_id in enumerate(list_client_conn_id):
+ client_ad.droid.bluetoothConnectionThroughputSend(
+ number_buffers, buffer_size, client_conn_id)
+ except Exception as err:
+ client_ad.log.error("Failed to write data: {}".format(err))
+ return 0
+
+ for _, server_ad in enumerate(list_server_ad):
+ try:
+ server_ad.droid.bluetoothConnectionThroughputRead(
+ number_buffers, buffer_size)
+ total_num_bytes += number_buffers * buffer_size
+ except Exception as err:
+ server_ad.log.error("Failed to read data: {}".format(err))
+ return 0
+
+ for _, server_ad in enumerate(list_server_ad):
+ try:
+ server_ad.droid.bluetoothConnectionThroughputRead(
+ number_buffers, buffer_size)
+ total_num_bytes += number_buffers * buffer_size
+ except Exception as err:
+ server_ad.log.error("Failed to read data: {}".format(err))
+ return 0
+
+ end_read_time = time.perf_counter()
+
+ test_time = (end_read_time - start_write_time)
+ if (test_time == 0):
+ client_ad.log.error("Buffer transmits cannot take zero time")
+ return 0
+ data_rate = (1.000 * total_num_bytes) / test_time
+ log.info(
+ "Calculated using total write and read times: total_num_bytes={}, "
+ "test_time={}, data rate={:08.0f} bytes/sec, {:08.0f} bits/sec".format(
+ total_num_bytes, test_time, data_rate, (data_rate * 8)))
+ return data_rate
+
+
+def orchestrate_coc_connection(
+ client_ad,
+ server_ad,
+ is_ble,
+ secured_conn=False,
+ le_connection_interval=0,
+ le_tx_data_length=default_le_data_length,
+ accept_timeout_ms=default_bluetooth_socket_timeout_ms,
+ le_min_ce_len=0,
+ le_max_ce_len=0,
+ gatt_disconnection=True):
+ """Sets up the CoC connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ is_ble: using LE transport.
+ secured_conn: using secured connection
+ le_connection_interval: LE Connection interval. 0 means use default.
+ le_tx_data_length: LE Data Length used by BT Controller to transmit.
+ accept_timeout_ms: timeout while waiting for incoming connection.
+ gatt_disconnection: LE GATT disconnection, default is True, False will return
+ bluetooth_gatt and gatt_callback
+ Returns:
+ True if connection was successful or false if unsuccessful,
+ client connection ID,
+ and server connection ID
+ """
+ server_ad.droid.bluetoothStartPairingHelper()
+ client_ad.droid.bluetoothStartPairingHelper()
+
+ adv_callback = None
+ mac_address = None
+ if is_ble:
+ try:
+ # This will start advertising and scanning. Will fail if it could
+ # not find the advertisements from server_ad
+ client_ad.log.info(
+ "Orchestrate_coc_connection: Start BLE advertisement and"
+ "scanning. Secured Connection={}".format(secured_conn))
+ mac_address, adv_callback, scan_callback = (
+ get_mac_address_of_generic_advertisement(client_ad, server_ad))
+ except BtTestUtilsError as err:
+ raise BtCoCTestUtilsError(
+ "Orchestrate_coc_connection: Error in getting mac address: {}".
+ format(err))
+ else:
+ mac_address = server_ad.droid.bluetoothGetLocalAddress()
+ adv_callback = None
+
+ # Adjust the Connection Interval (if necessary)
+ bluetooth_gatt_1 = -1
+ gatt_callback_1 = -1
+ gatt_connected = False
+ if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0):
+ client_ad.log.info(
+ "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}"
+ .format(le_connection_interval, le_min_ce_len, le_max_ce_len))
+ try:
+ bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection(
+ client_ad,
+ mac_address,
+ False,
+ transport=gatt_transport['le'],
+ opportunistic=False)
+ client_ad.droid.bleStopBleScan(scan_callback)
+ except GattTestUtilsError as err:
+ client_ad.log.error(err)
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+ return False, None, None
+ client_ad.log.info("setup_gatt_connection returns success")
+ if (le_connection_interval != 0):
+ minInterval = le_connection_interval / le_connection_interval_time_step_ms
+ maxInterval = le_connection_interval / le_connection_interval_time_step_ms
+ else:
+ minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ if (le_min_ce_len != 0):
+ le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms
+ if (le_max_ce_len != 0):
+ le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms
+
+ return_status = client_ad.droid.gattClientRequestLeConnectionParameters(
+ bluetooth_gatt_1, minInterval, maxInterval, 0,
+ le_default_supervision_timeout, le_min_ce_len, le_max_ce_len)
+ if not return_status:
+ client_ad.log.error(
+ "gattClientRequestLeConnectionParameters returns failure")
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+ return False, None, None
+ client_ad.log.info(
+ "gattClientRequestLeConnectionParameters returns success. Interval={}"
+ .format(minInterval))
+ gatt_connected = True
+ # For now, we will only test with 1 Mbit Phy.
+ # TODO: Add explicit tests with 2 MBit Phy.
+ client_ad.droid.gattClientSetPreferredPhy(
+ bluetooth_gatt_1, gatt_phy['1m'], gatt_phy['1m'], 0)
+
+ server_ad.droid.bluetoothSocketConnBeginAcceptThreadPsm(
+ accept_timeout_ms, is_ble, secured_conn)
+
+ psm_value = server_ad.droid.bluetoothSocketConnGetPsm()
+ client_ad.log.info("Assigned PSM value={}".format(psm_value))
+
+ client_ad.droid.bluetoothSocketConnBeginConnectThreadPsm(
+ mac_address, is_ble, psm_value, secured_conn)
+
+ if (le_tx_data_length != default_le_data_length) and is_ble:
+ client_ad.log.info("orchestrate_coc_connection: call "
+ "bluetoothSocketRequestMaximumTxDataLength")
+ client_ad.droid.bluetoothSocketRequestMaximumTxDataLength()
+
+ end_time = time.time() + bt_default_timeout
+ test_result = False
+ while time.time() < end_time:
+ if len(server_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ server_ad.log.info("CoC Server Connection Active")
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ client_ad.log.info("CoC Client Connection Active")
+ test_result = True
+ break
+ time.sleep(1)
+
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+
+ if not test_result:
+ client_ad.log.error("Failed to establish an CoC connection")
+ return False, None, None
+
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ server_ad.log.info(
+ "CoC client_ad Connection Active, num=%d",
+ len(client_ad.droid.bluetoothSocketConnActiveConnections()))
+ else:
+ server_ad.log.info("Error CoC client_ad Connection Inactive")
+ client_ad.log.info("Error CoC client_ad Connection Inactive")
+
+ # Wait for the client to be ready
+ client_conn_id = None
+ while (client_conn_id == None):
+ client_conn_id = client_ad.droid.bluetoothGetLastConnId()
+ if (client_conn_id != None):
+ break
+ time.sleep(1)
+
+ # Wait for the server to be ready
+ server_conn_id = None
+ while (server_conn_id == None):
+ server_conn_id = server_ad.droid.bluetoothGetLastConnId()
+ if (server_conn_id != None):
+ break
+ time.sleep(1)
+
+ client_ad.log.info(
+ "orchestrate_coc_connection: client conn id={}, server conn id={}".
+ format(client_conn_id, server_conn_id))
+
+ if gatt_disconnection:
+
+ if gatt_connected:
+ disconnect_gatt_connection(client_ad, bluetooth_gatt_1,
+ gatt_callback_1)
+ client_ad.droid.gattClientClose(bluetooth_gatt_1)
+
+ return True, client_conn_id, server_conn_id
+
+ else:
+ return True, client_conn_id, server_conn_id, bluetooth_gatt_1, gatt_callback_1
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_constants.py b/acts_tests/acts_contrib/test_utils/bt/bt_constants.py
new file mode 100644
index 0000000..f7bc93f
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_constants.py
@@ -0,0 +1,791 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+### Generic Constants Begin ###
+
+bt_default_timeout = 15
+default_rfcomm_timeout_ms = 10000
+default_bluetooth_socket_timeout_ms = 10000
+pan_connect_timeout = 5
+bt_discovery_timeout = 3
+small_timeout = 0.0001
+
+# Time delay (in seconds) at the end of each LE CoC Test to give sufficient time
+# for the ACL LE link to be disconnected. The ACL link stays connected after
+# L2CAP disconnects. An example of the timeout is L2CAP_LINK_INACTIVITY_TOUT.
+# This delay must be greater than the maximum of these timeouts.
+# TODO: Investigate the use of broadcast intent
+# BluetoothDevice.ACTION_ACL_DISCONNECTED to replace this delay method.
+l2cap_max_inactivity_delay_after_disconnect = 5
+
+# LE specifications related constants
+le_connection_interval_time_step_ms = 1.25
+le_default_supervision_timeout = 2000
+default_le_data_length = 23
+default_le_connection_interval_ms = 30
+le_connection_event_time_step_ms = 0.625
+
+# Headers of LE L2CAP Connection-oriented Channels. See section 3.4, Vol
+# 3, Part A, Version 5.0.
+l2cap_header_size = 4
+l2cap_coc_sdu_length_field_size = 2
+l2cap_coc_header_size = l2cap_header_size + l2cap_coc_sdu_length_field_size
+
+java_integer = {"min": -2147483648, "max": 2147483647}
+
+btsnoop_log_path_on_device = "/data/misc/bluetooth/logs/btsnoop_hci.log"
+btsnoop_last_log_path_on_device = \
+ "/data/misc/bluetooth/logs/btsnoop_hci.log.last"
+pairing_variant_passkey_confirmation = 2
+
+# Callback strings
+scan_result = "BleScan{}onScanResults"
+scan_failed = "BleScan{}onScanFailed"
+batch_scan_result = "BleScan{}onBatchScanResult"
+adv_fail = "BleAdvertise{}onFailure"
+adv_succ = "BleAdvertise{}onSuccess"
+bluetooth_off = "BluetoothStateChangedOff"
+bluetooth_on = "BluetoothStateChangedOn"
+mtu_changed = "GattConnect{}onMtuChanged"
+advertising_set_started = "AdvertisingSet{}onAdvertisingSetStarted"
+advertising_set_stopped = "AdvertisingSet{}onAdvertisingSetStopped"
+advertising_set_on_own_address_read = "AdvertisingSet{}onOwnAddressRead"
+advertising_set_enabled = "AdvertisingSet{}onAdvertisingEnabled"
+advertising_set_data_set = "AdvertisingSet{}onAdvertisingDataSet"
+advertising_set_scan_response_set = "AdvertisingSet{}onScanResponseDataSet"
+advertising_set_parameters_update = \
+ "AdvertisingSet{}onAdvertisingParametersUpdated"
+advertising_set_periodic_parameters_updated = \
+ "AdvertisingSet{}onPeriodicAdvertisingParametersUpdated"
+advertising_set_periodic_data_set = \
+ "AdvertisingSet{}onPeriodicAdvertisingDataSet"
+advertising_set_periodic_enable = "AdvertisingSet{}onPeriodicAdvertisingEnable"
+bluetooth_profile_connection_state_changed = \
+ "BluetoothProfileConnectionStateChanged"
+bluetooth_le_on = "BleStateChangedOn"
+bluetooth_le_off = "BleStateChangedOff"
+bluetooth_a2dp_codec_config_changed = "BluetoothA2dpCodecConfigChanged"
+# End Callback Strings
+
+batch_scan_not_supported_list = [
+ "Nexus 4",
+ "Nexus 5",
+ "Nexus 7",
+]
+
+### Generic Constants End ###
+
+### Bluetooth Constants Begin ###
+
+# rfcomm test uuids
+rfcomm_secure_uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66"
+rfcomm_insecure_uuid = "8ce255c0-200a-11e0-ac64-0800200c9a66"
+
+# bluetooth socket connection test uuid
+bluetooth_socket_conn_test_uuid = "12345678-1234-5678-9abc-123456789abc"
+
+# Bluetooth Adapter Scan Mode Types
+bt_scan_mode_types = {
+ "state_off": -1,
+ "none": 0,
+ "connectable": 1,
+ "connectable_discoverable": 3
+}
+
+# Bluetooth Adapter State Constants
+bt_adapter_states = {
+ "off": 10,
+ "turning_on": 11,
+ "on": 12,
+ "turning_off": 13,
+ "ble_turning_on": 14,
+ "ble_on": 15,
+ "ble_turning_off": 16
+}
+
+# Should be kept in sync with BluetoothProfile.java
+bt_profile_constants = {
+ "headset": 1,
+ "a2dp": 2,
+ "health": 3,
+ "input_device": 4,
+ "pan": 5,
+ "pbap_server": 6,
+ "gatt": 7,
+ "gatt_server": 8,
+ "map": 9,
+ "sap": 10,
+ "a2dp_sink": 11,
+ "avrcp_controller": 12,
+ "headset_client": 16,
+ "pbap_client": 17,
+ "map_mce": 18
+}
+
+# Bluetooth RFCOMM UUIDs as defined by the SIG
+bt_rfcomm_uuids = {
+ "default_uuid": "457807c0-4897-11df-9879-0800200c9a66",
+ "base_uuid": "00000000-0000-1000-8000-00805F9B34FB",
+ "sdp": "00000001-0000-1000-8000-00805F9B34FB",
+ "udp": "00000002-0000-1000-8000-00805F9B34FB",
+ "rfcomm": "00000003-0000-1000-8000-00805F9B34FB",
+ "tcp": "00000004-0000-1000-8000-00805F9B34FB",
+ "tcs_bin": "00000005-0000-1000-8000-00805F9B34FB",
+ "tcs_at": "00000006-0000-1000-8000-00805F9B34FB",
+ "att": "00000007-0000-1000-8000-00805F9B34FB",
+ "obex": "00000008-0000-1000-8000-00805F9B34FB",
+ "ip": "00000009-0000-1000-8000-00805F9B34FB",
+ "ftp": "0000000A-0000-1000-8000-00805F9B34FB",
+ "http": "0000000C-0000-1000-8000-00805F9B34FB",
+ "wsp": "0000000E-0000-1000-8000-00805F9B34FB",
+ "bnep": "0000000F-0000-1000-8000-00805F9B34FB",
+ "upnp": "00000010-0000-1000-8000-00805F9B34FB",
+ "hidp": "00000011-0000-1000-8000-00805F9B34FB",
+ "hardcopy_control_channel": "00000012-0000-1000-8000-00805F9B34FB",
+ "hardcopy_data_channel": "00000014-0000-1000-8000-00805F9B34FB",
+ "hardcopy_notification": "00000016-0000-1000-8000-00805F9B34FB",
+ "avctp": "00000017-0000-1000-8000-00805F9B34FB",
+ "avdtp": "00000019-0000-1000-8000-00805F9B34FB",
+ "cmtp": "0000001B-0000-1000-8000-00805F9B34FB",
+ "mcap_control_channel": "0000001E-0000-1000-8000-00805F9B34FB",
+ "mcap_data_channel": "0000001F-0000-1000-8000-00805F9B34FB",
+ "l2cap": "00000100-0000-1000-8000-00805F9B34FB"
+}
+
+# Should be kept in sync with BluetoothProfile#STATE_* constants.
+bt_profile_states = {
+ "disconnected": 0,
+ "connecting": 1,
+ "connected": 2,
+ "disconnecting": 3
+}
+
+# Access Levels from BluetoothDevice.
+bt_access_levels = {"access_allowed": 1, "access_denied": 2}
+
+# Priority levels as defined in BluetoothProfile.java.
+bt_priority_levels = {
+ "auto_connect": 1000,
+ "on": 100,
+ "off": 0,
+ "undefined": -1
+}
+
+# A2DP codec configuration constants as defined in
+# frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java
+codec_types = {
+ 'SBC': 0,
+ 'AAC': 1,
+ 'APTX': 2,
+ 'APTX-HD': 3,
+ 'LDAC': 4,
+ 'MAX': 5,
+ 'INVALID': 1000000
+}
+
+codec_priorities = {'DISABLED': -1, 'DEFAULT': 0, 'HIGHEST': 1000000}
+
+sample_rates = {
+ 'NONE': 0,
+ '44100': 0x1 << 0,
+ '48000': 0x1 << 1,
+ '88200': 0x1 << 2,
+ '96000': 0x1 << 3,
+ '176400': 0x1 << 4,
+ '192000': 0x1 << 5
+}
+
+bits_per_samples = {'NONE': 0, '16': 0x1 << 0, '24': 0x1 << 1, '32': 0x1 << 2}
+
+channel_modes = {'NONE': 0, 'MONO': 0x1 << 0, 'STEREO': 0x1 << 1}
+
+# Bluetooth HID constants.
+hid_connection_timeout = 5
+
+# Bluetooth HID EventFacade constants.
+hid_on_set_report_event = "onSetReport"
+hid_on_get_report_event = "onGetReport"
+hid_on_set_protocol_event = "onSetProtocol"
+hid_on_intr_data_event = "onInterruptData"
+hid_on_virtual_cable_unplug_event = "onVirtualCableUnplug"
+hid_id_keyboard = 1
+hid_id_mouse = 2
+hid_default_event_timeout = 15
+hid_default_set_report_payload = "Haha"
+
+### Bluetooth Constants End ###
+
+### Bluetooth Low Energy Constants Begin ###
+
+# Bluetooth Low Energy scan callback types
+ble_scan_settings_callback_types = {
+ "all_matches": 1,
+ "first_match": 2,
+ "match_lost": 4,
+ "found_and_lost": 6
+}
+
+# Bluetooth Low Energy scan settings match mode
+ble_scan_settings_match_modes = {"aggresive": 1, "sticky": 2}
+
+# Bluetooth Low Energy scan settings match nums
+ble_scan_settings_match_nums = {"one": 1, "few": 2, "max": 3}
+
+# Bluetooth Low Energy scan settings result types
+ble_scan_settings_result_types = {"full": 0, "abbreviated": 1}
+
+# Bluetooth Low Energy scan settings mode
+ble_scan_settings_modes = {
+ "opportunistic": -1,
+ "low_power": 0,
+ "balanced": 1,
+ "low_latency": 2
+}
+
+# Bluetooth Low Energy scan settings report delay millis
+ble_scan_settings_report_delay_milli_seconds = {
+ "min": 0,
+ "max": 9223372036854775807
+}
+
+# Bluetooth Low Energy scan settings phy
+ble_scan_settings_phys = {"1m": 1, "coded": 3, "all_supported": 255}
+
+# Bluetooth Low Energy advertise settings types
+ble_advertise_settings_types = {"non_connectable": 0, "connectable": 1}
+
+# Bluetooth Low Energy advertise settings modes
+ble_advertise_settings_modes = {
+ "low_power": 0,
+ "balanced": 1,
+ "low_latency": 2
+}
+
+# Bluetooth Low Energy advertise settings tx power
+ble_advertise_settings_tx_powers = {
+ "ultra_low": 0,
+ "low": 1,
+ "medium": 2,
+ "high": 3
+}
+
+# Bluetooth Low Energy service uuids for specific devices
+ble_uuids = {
+ "p_service": "0000feef-0000-1000-8000-00805f9b34fb",
+ "hr_service": "0000180d-0000-1000-8000-00805f9b34fb"
+}
+
+# Bluetooth Low Energy advertising error codes
+ble_advertise_error_code = {
+ "data_too_large": 1,
+ "too_many_advertisers": 2,
+ "advertisement_already_started": 3,
+ "bluetooth_internal_failure": 4,
+ "feature_not_supported": 5
+}
+
+### Bluetooth Low Energy Constants End ###
+
+### Bluetooth GATT Constants Begin ###
+
+# Gatt Callback error messages
+gatt_cb_err = {
+ "char_write_req_err":
+ "Characteristic Write Request event not found. Expected {}",
+ "char_write_err": "Characteristic Write event not found. Expected {}",
+ "desc_write_req_err":
+ "Descriptor Write Request event not found. Expected {}",
+ "desc_write_err": "Descriptor Write event not found. Expected {}",
+ "char_read_err": "Characteristic Read event not found. Expected {}",
+ "char_read_req_err": "Characteristic Read Request not found. Expected {}",
+ "desc_read_err": "Descriptor Read event not found. Expected {}",
+ "desc_read_req_err":
+ "Descriptor Read Request event not found. Expected {}",
+ "rd_remote_rssi_err": "Read Remote RSSI event not found. Expected {}",
+ "gatt_serv_disc_err":
+ "GATT Services Discovered event not found. Expected {}",
+ "serv_added_err": "Service Added event not found. Expected {}",
+ "mtu_changed_err": "MTU Changed event not found. Expected {}",
+ "mtu_serv_changed_err": "MTU Server Changed event not found. Expected {}",
+ "gatt_conn_changed_err":
+ "GATT Connection Changed event not found. Expected {}",
+ "char_change_err":
+ "GATT Characteristic Changed event not fond. Expected {}",
+ "phy_read_err": "Phy Read event not fond. Expected {}",
+ "phy_update_err": "Phy Update event not fond. Expected {}",
+ "exec_write_err": "GATT Execute Write event not found. Expected {}"
+}
+
+# GATT callback strings as defined in GattClientFacade.java and
+# GattServerFacade.java implemented callbacks.
+gatt_cb_strings = {
+ "char_write_req": "GattServer{}onCharacteristicWriteRequest",
+ "exec_write": "GattServer{}onExecuteWrite",
+ "char_write": "GattConnect{}onCharacteristicWrite",
+ "desc_write_req": "GattServer{}onDescriptorWriteRequest",
+ "desc_write": "GattConnect{}onDescriptorWrite",
+ "char_read": "GattConnect{}onCharacteristicRead",
+ "char_read_req": "GattServer{}onCharacteristicReadRequest",
+ "desc_read": "GattConnect{}onDescriptorRead",
+ "desc_read_req": "GattServer{}onDescriptorReadRequest",
+ "rd_remote_rssi": "GattConnect{}onReadRemoteRssi",
+ "gatt_serv_disc": "GattConnect{}onServicesDiscovered",
+ "serv_added": "GattServer{}onServiceAdded",
+ "mtu_changed": "GattConnect{}onMtuChanged",
+ "mtu_serv_changed": "GattServer{}onMtuChanged",
+ "gatt_conn_change": "GattConnect{}onConnectionStateChange",
+ "char_change": "GattConnect{}onCharacteristicChanged",
+ "phy_read": "GattConnect{}onPhyRead",
+ "phy_update": "GattConnect{}onPhyUpdate",
+ "serv_phy_read": "GattServer{}onPhyRead",
+ "serv_phy_update": "GattServer{}onPhyUpdate",
+}
+
+# GATT event dictionary of expected callbacks and errors.
+gatt_event = {
+ "char_write_req": {
+ "evt": gatt_cb_strings["char_write_req"],
+ "err": gatt_cb_err["char_write_req_err"]
+ },
+ "exec_write": {
+ "evt": gatt_cb_strings["exec_write"],
+ "err": gatt_cb_err["exec_write_err"]
+ },
+ "char_write": {
+ "evt": gatt_cb_strings["char_write"],
+ "err": gatt_cb_err["char_write_err"]
+ },
+ "desc_write_req": {
+ "evt": gatt_cb_strings["desc_write_req"],
+ "err": gatt_cb_err["desc_write_req_err"]
+ },
+ "desc_write": {
+ "evt": gatt_cb_strings["desc_write"],
+ "err": gatt_cb_err["desc_write_err"]
+ },
+ "char_read": {
+ "evt": gatt_cb_strings["char_read"],
+ "err": gatt_cb_err["char_read_err"]
+ },
+ "char_read_req": {
+ "evt": gatt_cb_strings["char_read_req"],
+ "err": gatt_cb_err["char_read_req_err"]
+ },
+ "desc_read": {
+ "evt": gatt_cb_strings["desc_read"],
+ "err": gatt_cb_err["desc_read_err"]
+ },
+ "desc_read_req": {
+ "evt": gatt_cb_strings["desc_read_req"],
+ "err": gatt_cb_err["desc_read_req_err"]
+ },
+ "rd_remote_rssi": {
+ "evt": gatt_cb_strings["rd_remote_rssi"],
+ "err": gatt_cb_err["rd_remote_rssi_err"]
+ },
+ "gatt_serv_disc": {
+ "evt": gatt_cb_strings["gatt_serv_disc"],
+ "err": gatt_cb_err["gatt_serv_disc_err"]
+ },
+ "serv_added": {
+ "evt": gatt_cb_strings["serv_added"],
+ "err": gatt_cb_err["serv_added_err"]
+ },
+ "mtu_changed": {
+ "evt": gatt_cb_strings["mtu_changed"],
+ "err": gatt_cb_err["mtu_changed_err"]
+ },
+ "gatt_conn_change": {
+ "evt": gatt_cb_strings["gatt_conn_change"],
+ "err": gatt_cb_err["gatt_conn_changed_err"]
+ },
+ "char_change": {
+ "evt": gatt_cb_strings["char_change"],
+ "err": gatt_cb_err["char_change_err"]
+ },
+ "phy_read": {
+ "evt": gatt_cb_strings["phy_read"],
+ "err": gatt_cb_err["phy_read_err"]
+ },
+ "phy_update": {
+ "evt": gatt_cb_strings["phy_update"],
+ "err": gatt_cb_err["phy_update_err"]
+ },
+ "serv_phy_read": {
+ "evt": gatt_cb_strings["serv_phy_read"],
+ "err": gatt_cb_err["phy_read_err"]
+ },
+ "serv_phy_update": {
+ "evt": gatt_cb_strings["serv_phy_update"],
+ "err": gatt_cb_err["phy_update_err"]
+ }
+}
+
+# Matches constants of connection states defined in BluetoothGatt.java
+gatt_connection_state = {
+ "disconnected": 0,
+ "connecting": 1,
+ "connected": 2,
+ "disconnecting": 3,
+ "closed": 4
+}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattCharacteristic.java
+gatt_characteristic = {
+ "property_broadcast": 0x01,
+ "property_read": 0x02,
+ "property_write_no_response": 0x04,
+ "property_write": 0x08,
+ "property_notify": 0x10,
+ "property_indicate": 0x20,
+ "property_signed_write": 0x40,
+ "property_extended_props": 0x80,
+ "permission_read": 0x01,
+ "permission_read_encrypted": 0x02,
+ "permission_read_encrypted_mitm": 0x04,
+ "permission_write": 0x10,
+ "permission_write_encrypted": 0x20,
+ "permission_write_encrypted_mitm": 0x40,
+ "permission_write_signed": 0x80,
+ "permission_write_signed_mitm": 0x100,
+ "write_type_default": 0x02,
+ "write_type_no_response": 0x01,
+ "write_type_signed": 0x04,
+}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattDescriptor.java
+gatt_descriptor = {
+ "enable_notification_value": [0x01, 0x00],
+ "enable_indication_value": [0x02, 0x00],
+ "disable_notification_value": [0x00, 0x00],
+ "permission_read": 0x01,
+ "permission_read_encrypted": 0x02,
+ "permission_read_encrypted_mitm": 0x04,
+ "permission_write": 0x10,
+ "permission_write_encrypted": 0x20,
+ "permission_write_encrypted_mitm": 0x40,
+ "permission_write_signed": 0x80,
+ "permission_write_signed_mitm": 0x100
+}
+
+# https://www.bluetooth.com/specifications/gatt/descriptors
+gatt_char_desc_uuids = {
+ "char_ext_props": '00002900-0000-1000-8000-00805f9b34fb',
+ "char_user_desc": '00002901-0000-1000-8000-00805f9b34fb',
+ "client_char_cfg": '00002902-0000-1000-8000-00805f9b34fb',
+ "server_char_cfg": '00002903-0000-1000-8000-00805f9b34fb',
+ "char_fmt_uuid": '00002904-0000-1000-8000-00805f9b34fb',
+ "char_agreg_fmt": '00002905-0000-1000-8000-00805f9b34fb',
+ "char_valid_range": '00002906-0000-1000-8000-00805f9b34fb',
+ "external_report_reference": '00002907-0000-1000-8000-00805f9b34fb',
+ "report_reference": '00002908-0000-1000-8000-00805f9b34fb'
+}
+
+# https://www.bluetooth.com/specifications/gatt/characteristics
+gatt_char_types = {
+ "device_name": '00002a00-0000-1000-8000-00805f9b34fb',
+ "appearance": '00002a01-0000-1000-8000-00805f9b34fb',
+ "peripheral_priv_flag": '00002a02-0000-1000-8000-00805f9b34fb',
+ "reconnection_address": '00002a03-0000-1000-8000-00805f9b34fb',
+ "peripheral_pref_conn": '00002a04-0000-1000-8000-00805f9b34fb',
+ "service_changed": '00002a05-0000-1000-8000-00805f9b34fb',
+ "system_id": '00002a23-0000-1000-8000-00805f9b34fb',
+ "model_number_string": '00002a24-0000-1000-8000-00805f9b34fb',
+ "serial_number_string": '00002a25-0000-1000-8000-00805f9b34fb',
+ "firmware_revision_string": '00002a26-0000-1000-8000-00805f9b34fb',
+ "hardware_revision_string": '00002a27-0000-1000-8000-00805f9b34fb',
+ "software_revision_string": '00002a28-0000-1000-8000-00805f9b34fb',
+ "manufacturer_name_string": '00002a29-0000-1000-8000-00805f9b34fb',
+ "pnp_id": '00002a50-0000-1000-8000-00805f9b34fb',
+}
+
+# Matches constants of Bluetooth GATT Characteristic values as defined
+# in BluetoothGattCharacteristic.java
+gatt_characteristic_value_format = {
+ "string": 0x1,
+ "byte": 0x2,
+ "sint8": 0x21,
+ "uint8": 0x11,
+ "sint16": 0x22,
+ "unit16": 0x12,
+ "sint32": 0x24,
+ "uint32": 0x14
+}
+
+# Matches constants of Bluetooth Gatt Service types as defined in
+# BluetoothGattService.java
+gatt_service_types = {"primary": 0, "secondary": 1}
+
+# Matches constants of Bluetooth Gatt Connection Priority values as defined in
+# BluetoothGatt.java
+gatt_connection_priority = {"balanced": 0, "high": 1, "low_power": 2}
+
+# Min and max MTU values
+gatt_mtu_size = {"min": 23, "max": 217}
+
+# Gatt Characteristic attribute lengths
+gatt_characteristic_attr_length = {"attr_1": 1, "attr_2": 3, "attr_3": 15}
+
+# Matches constants of Bluetooth Gatt operations status as defined in
+# BluetoothGatt.java
+gatt_status = {"success": 0, "failure": 0x101}
+
+# Matches constants of Bluetooth transport values as defined in
+# BluetoothDevice.java
+gatt_transport = {"auto": 0x00, "bredr": 0x01, "le": 0x02}
+
+# Matches constants of Bluetooth physical channeling values as defined in
+# BluetoothDevice.java
+gatt_phy = {"1m": 1, "2m": 2, "le_coded": 3}
+
+# Matches constants of Bluetooth physical channeling bitmask values as defined
+# in BluetoothDevice.java
+gatt_phy_mask = {"1m_mask": 1, "2m_mask": 2, "coded_mask": 4}
+
+# Values as defiend in the Bluetooth GATT specification
+gatt_server_responses = {
+ "GATT_SUCCESS": 0x0,
+ "GATT_FAILURE": 0x1,
+ "GATT_READ_NOT_PERMITTED": 0x2,
+ "GATT_WRITE_NOT_PERMITTED": 0x3,
+ "GATT_INVALID_PDU": 0x4,
+ "GATT_INSUFFICIENT_AUTHENTICATION": 0x5,
+ "GATT_REQUEST_NOT_SUPPORTED": 0x6,
+ "GATT_INVALID_OFFSET": 0x7,
+ "GATT_INSUFFICIENT_AUTHORIZATION": 0x8,
+ "GATT_INVALID_ATTRIBUTE_LENGTH": 0xd,
+ "GATT_INSUFFICIENT_ENCRYPTION": 0xf,
+ "GATT_CONNECTION_CONGESTED": 0x8f,
+ "GATT_13_ERR": 0x13,
+ "GATT_12_ERR": 0x12,
+ "GATT_0C_ERR": 0x0C,
+ "GATT_16": 0x16
+}
+
+### Bluetooth GATT Constants End ###
+
+### Chameleon Constants Begin ###
+
+# Chameleon audio bits per sample.
+audio_bits_per_sample_16 = 16
+audio_bits_per_sample_24 = 24
+audio_bits_per_sample_32 = 32
+
+# Chameleon audio sample rates.
+audio_sample_rate_44100 = 44100
+audio_sample_rate_48000 = 48000
+audio_sample_rate_88200 = 88200
+audio_sample_rate_96000 = 96000
+
+# Chameleon audio channel modes.
+audio_channel_mode_mono = 1
+audio_channel_mode_stereo = 2
+audio_channel_mode_8 = 8
+
+# Chameleon time delays.
+delay_after_binding_seconds = 0.5
+delay_before_record_seconds = 0.5
+silence_wait_seconds = 5
+
+# Chameleon bus endpoints.
+fpga_linein_bus_endpoint = 'Chameleon FPGA line-in'
+headphone_bus_endpoint = 'Cros device headphone'
+
+### Chameleon Constants End ###
+
+# Begin logcat strings dict"""
+logcat_strings = {
+ "media_playback_vol_changed": "onRouteVolumeChanged",
+}
+
+# End logcat strings dict"""
+
+### Begin Service Discovery UUIDS ###
+# Values match the Bluetooth SIG defined values: """
+""" https://www.bluetooth.com/specifications/assigned-numbers/service-discovery """
+sig_uuid_constants = {
+ "BASE_UUID": "0000{}-0000-1000-8000-00805F9B34FB",
+ "SDP": "0001",
+ "UDP": "0002",
+ "RFCOMM": "0003",
+ "TCP": "0004",
+ "TCS-BIN": "0005",
+ "TCS-AT": "0006",
+ "ATT": "0007",
+ "OBEX": "0008",
+ "IP": "0009",
+ "FTP": "000A",
+ "HTTP": "000C",
+ "WSP": "000E",
+ "BNEP": "000F",
+ "UPNP": "0010",
+ "HIDP": "0011",
+ "HardcopyControlChannel": "0012",
+ "HardcopyDataChannel": "0014",
+ "HardcopyNotification": "0016",
+ "AVCTP": "0017",
+ "AVDTP": "0019",
+ "CMTP": "001B",
+ "MCAPControlChannel": "001E",
+ "MCAPDataChannel": "001F",
+ "L2CAP": "0100",
+ "ServiceDiscoveryServerServiceClassID": "1000",
+ "BrowseGroupDescriptorServiceClassID": "1001",
+ "SerialPort": "1101",
+ "LANAccessUsingPPP": "1102",
+ "DialupNetworking": "1103",
+ "IrMCSync": "1104",
+ "OBEXObjectPush": "1105",
+ "OBEXFileTransfer": "1106",
+ "IrMCSyncCommand": "1107",
+ "Headset": "1108",
+ "CordlessTelephony": "1109",
+ "AudioSource": "110A",
+ "AudioSink": "110B",
+ "A/V_RemoteControlTarget": "110C",
+ "AdvancedAudioDistribution": "110D",
+ "A/V_RemoteControl": "110E",
+ "A/V_RemoteControlController": "110F",
+ "Intercom": "1110",
+ "Fax": "1111",
+ "Headset - Audio Gateway (AG)": "1112",
+ "WAP": "1113",
+ "WAP_CLIENT": "1114",
+ "PANU": "1115",
+ "NAP": "1116",
+ "GN": "1117",
+ "DirectPrinting": "1118",
+ "ReferencePrinting": "1119",
+ "ImagingResponder": "111B",
+ "ImagingAutomaticArchive": "111C",
+ "ImagingReferencedObjects": "111D",
+ "Handsfree": "111E",
+ "HandsfreeAudioGateway": "111F",
+ "DirectPrintingReferenceObjectsService": "1120",
+ "ReflectedUI": "1121",
+ "BasicPrinting": "1122",
+ "PrintingStatus": "1123",
+ "HumanInterfaceDeviceService": "1124",
+ "HardcopyCableReplacement": "1125",
+ "HCR_Print": "1126",
+ "HCR_Scan": "1127",
+ "Common_ISDN_Access": "1128",
+ "SIM_Access": "112D",
+ "Phonebook Access - PCE": "112E",
+ "Phonebook Access - PSE": "112F",
+ "Phonebook Access": "1130",
+ "Headset - HS": "1131",
+ "Message Access Server": "1132",
+ "Message Notification Server": "1133",
+ "Message Access Profile": "1134",
+ "GNSS": "1135",
+ "GNSS_Server": "1136",
+ "PnPInformation": "1200",
+ "GenericNetworking": "1201",
+ "GenericFileTransfer": "1202",
+ "GenericAudio": "1203",
+ "GenericTelephony": "1204",
+ "UPNP_Service": "1205",
+ "UPNP_IP_Service": "1206",
+ "ESDP_UPNP_IP_PAN": "1300",
+ "ESDP_UPNP_IP_LAP": "1301",
+ "ESDP_UPNP_L2CAP": "1302",
+ "VideoSource": "1303",
+ "VideoSink": "1304",
+ "VideoDistribution": "1305",
+ "HDP": "1400"
+}
+
+### End Service Discovery UUIDS ###
+
+### Begin Appearance Constants ###
+# https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.gap.appearance.xml
+sig_appearance_constants = {
+ "UNKNOWN": 0,
+ "PHONE": 64,
+ "COMPUTER": 128,
+ "WATCH": 192,
+ "WATCH_SPORTS": 193,
+ "CLOCK": 256,
+ "DISPLAY": 320,
+ "REMOTE_CONTROL": 384,
+ "EYE_GLASSES": 448,
+ "TAG": 512,
+ "KEYRING": 576,
+ "MEDIA_PLAYER": 640,
+ "BARCODE_SCANNER": 704,
+ "THERMOMETER": 768,
+ "THERMOMETER_EAR": 769,
+ "HEART_RATE_SENSOR": 832,
+ "HEART_RATE_SENSOR_BELT": 833,
+ "BLOOD_PRESSURE": 896,
+ "BLOOD_PRESSURE_ARM": 897,
+ "BLOOD_PRESSURE_WRIST": 898,
+ "HID": 960,
+ "HID_KEYBOARD": 961,
+ "HID_MOUSE": 962,
+ "HID_JOYSTICK": 963,
+ "HID_GAMEPAD": 964,
+ "HID_DIGITIZER_TABLET": 965,
+ "HID_CARD_READER": 966,
+ "HID_DIGITAL_PEN": 967,
+ "HID_BARCODE_SCANNER": 968,
+ "GLUCOSE_METER": 1024,
+ "RUNNING_WALKING_SENSOR": 1088,
+ "RUNNING_WALKING_SENSOR_IN_SHOE": 1089,
+ "RUNNING_WALKING_SENSOR_ON_SHOE": 1090,
+ "RUNNING_WALKING_SENSOR_ON_HIP": 1091,
+ "CYCLING": 1152,
+ "CYCLING_COMPUTER": 1153,
+ "CYCLING_SPEED_SENSOR": 1154,
+ "CYCLING_CADENCE_SENSOR": 1155,
+ "CYCLING_POWER_SENSOR": 1156,
+ "CYCLING_SPEED_AND_CADENCE_SENSOR": 1157,
+ "PULSE_OXIMETER": 3136,
+ "PULSE_OXIMETER_FINGERTIP": 3137,
+ "PULSE_OXIMETER_WRIST": 3138,
+ "WEIGHT_SCALE": 3200,
+ "PERSONAL_MOBILITY": 3264,
+ "PERSONAL_MOBILITY_WHEELCHAIR": 3265,
+ "PERSONAL_MOBILITY_SCOOTER": 3266,
+ "GLUCOSE_MONITOR": 3328,
+ "SPORTS_ACTIVITY": 5184,
+ "SPORTS_ACTIVITY_LOCATION_DISPLAY": 5185,
+ "SPORTS_ACTIVITY_LOCATION_AND_NAV_DISPLAY": 5186,
+ "SPORTS_ACTIVITY_LOCATION_POD": 5187,
+ "SPORTS_ACTIVITY_LOCATION_AND_NAV_POD": 5188,
+}
+
+### End Appearance Constants ###
+
+# Attribute Record values from the Bluetooth Specification
+# Version 5, Vol 3, Part B
+bt_attribute_values = {
+ 'ATTR_SERVICE_RECORD_HANDLE': 0x0000,
+ 'ATTR_SERVICE_CLASS_ID_LIST': 0x0001,
+ 'ATTR_SERVICE_RECORD_STATE': 0x0002,
+ 'ATTR_SERVICE_ID': 0x0003,
+ 'ATTR_PROTOCOL_DESCRIPTOR_LIST': 0x0004,
+ 'ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST': 0x000D,
+ 'ATTR_BROWSE_GROUP_LIST': 0x0005,
+ 'ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST': 0x0006,
+ 'ATTR_SERVICE_INFO_TIME_TO_LIVE': 0x0007,
+ 'ATTR_SERVICE_AVAILABILITY': 0x0008,
+ 'ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST': 0x0009,
+ 'ATTR_A2DP_SUPPORTED_FEATURES': 0x0311,
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_contacts_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_contacts_utils.py
new file mode 100644
index 0000000..41de7c2
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_contacts_utils.py
@@ -0,0 +1,428 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Compare_contacts accepts 2 vcf files, extracts full name, email, and
+telephone numbers from each and reports how many unique cards it finds across
+the two files.
+"""
+
+from mmap import ACCESS_READ
+from mmap import mmap
+import logging
+import re
+import random
+import string
+import time
+from acts.utils import exe_cmd
+import queue
+
+# CallLog types
+INCOMMING_CALL_TYPE = "1"
+OUTGOING_CALL_TYPE = "2"
+MISSED_CALL_TYPE = "3"
+
+# Callback strings.
+CONTACTS_CHANGED_CALLBACK = "ContactsChanged"
+CALL_LOG_CHANGED = "CallLogChanged"
+CONTACTS_ERASED_CALLBACK = "ContactsErased"
+
+# URI for contacts database on Nexus.
+CONTACTS_URI = "content://com.android.contacts/data/phones"
+
+# Path for temporary file storage on device.
+STORAGE_PATH = "/storage/emulated/0/Download/"
+
+PBAP_SYNC_TIME = 30
+
+log = logging
+
+
+def parse_contacts(file_name):
+ """Read vcf file and generate a list of contacts.
+
+ Contacts full name, prefered email, and all phone numbers are extracted.
+ """
+
+ vcard_regex = re.compile(b"^BEGIN:VCARD((\n*?.*?)*?)END:VCARD",
+ re.MULTILINE)
+ fullname_regex = re.compile(b"^FN:(.*)", re.MULTILINE)
+ email_regex = re.compile(b"^EMAIL;PREF:(.*)", re.MULTILINE)
+ tel_regex = re.compile(b"^TEL;(.*):(.*)", re.MULTILINE)
+
+ with open(file_name, "r") as contacts_file:
+ contacts = []
+ contacts_map = mmap(
+ contacts_file.fileno(), length=0, access=ACCESS_READ)
+ new_contact = None
+
+ # Find all VCARDs in the input file, then extract the first full name,
+ # first email address, and all phone numbers from it. If there is at
+ # least a full name add it to the contact list.
+ for current_vcard in vcard_regex.findall(contacts_map):
+ new_contact = VCard()
+
+ fullname = fullname_regex.search(current_vcard[0])
+ if fullname is not None:
+ new_contact.name = fullname.group(1)
+
+ email = email_regex.search(current_vcard[0])
+ if email is not None:
+ new_contact.email = email.group(1)
+
+ for phone_number in tel_regex.findall(current_vcard[0]):
+ new_contact.add_phone_number(
+ PhoneNumber(phone_number[0], phone_number[1]))
+
+ contacts.append(new_contact)
+
+ return contacts
+
+
+def phone_number_count(destination_path, file_name):
+ """Counts number of phone numbers in a VCF.
+ """
+ tel_regex = re.compile(b"^TEL;(.*):(.*)", re.MULTILINE)
+ with open("{}{}".format(destination_path, file_name),
+ "r") as contacts_file:
+ contacts_map = mmap(
+ contacts_file.fileno(), length=0, access=ACCESS_READ)
+ numbers = tel_regex.findall(contacts_map)
+ return len(numbers)
+
+
+def count_contacts_with_differences(destination_path,
+ pce_contacts_vcf_file_name,
+ pse_contacts_vcf_file_name):
+ """Compare two contact files and report the number of differences.
+
+ Difference count is returned, and the differences are logged, this is order
+ independent.
+ """
+
+ pce_contacts = parse_contacts("{}{}".format(destination_path,
+ pce_contacts_vcf_file_name))
+ pse_contacts = parse_contacts("{}{}".format(destination_path,
+ pse_contacts_vcf_file_name))
+
+ differences = set(pce_contacts).symmetric_difference(set(pse_contacts))
+ if not differences:
+ log.info("All {} contacts in the phonebooks match".format(
+ str(len(pce_contacts))))
+ else:
+ log.info("{} contacts match, but ".format(
+ str(len(set(pce_contacts).intersection(set(pse_contacts))))))
+ log.info("the following {} entries don't match:".format(
+ str(len(differences))))
+ for current_vcard in differences:
+ log.info(current_vcard)
+ return len(differences)
+
+
+class PhoneNumber(object):
+ """Simple class for maintaining a phone number entry and type with only the
+ digits.
+ """
+
+ def __init__(self, phone_type, phone_number):
+ self.phone_type = phone_type
+ # remove non digits from phone_number
+ self.phone_number = re.sub(r"\D", "", str(phone_number))
+
+ def __eq__(self, other):
+ return (self.phone_type == other.phone_type and
+ self.phone_number == other.phone_number)
+
+ def __hash__(self):
+ return hash(self.phone_type) ^ hash(self.phone_number)
+
+
+class VCard(object):
+ """Contains name, email, and phone numbers.
+ """
+
+ def __init__(self):
+ self.name = None
+ self.first_name = None
+ self.last_name = None
+ self.email = None
+ self.phone_numbers = []
+ self.photo = None
+
+ def __lt__(self, other):
+ return self.name < other.name
+
+ def __hash__(self):
+ result = hash(self.name) ^ hash(self.email) ^ hash(self.photo == None)
+ for number in self.phone_numbers:
+ result ^= hash(number)
+ return result
+
+ def __eq__(self, other):
+ return hash(self) == hash(other)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __str__(self):
+ vcard_strings = ["BEGIN:VCARD\n", "VERSION:2.1\n"]
+
+ if self.first_name or self.last_name:
+ vcard_strings.append("N:{};{};;;\nFN:{} {}\n".format(
+ self.last_name, self.first_name, self.first_name,
+ self.last_name))
+ elif self.name:
+ vcard_strings.append("FN:{}\n".format(self.name))
+
+ if self.phone_numbers:
+ for phone in self.phone_numbers:
+ vcard_strings.append("TEL;{}:{}\n".format(
+ str(phone.phone_type), phone.phone_number))
+
+ if self.email:
+ vcard_strings.append("EMAIL;PREF:{}\n".format(self.email))
+
+ vcard_strings.append("END:VCARD\n")
+ return "".join(vcard_strings)
+
+ def add_phone_number(self, phone_number):
+ if phone_number not in self.phone_numbers:
+ self.phone_numbers.append(phone_number)
+
+
+def generate_random_phone_number():
+ """Generate a random phone number/type
+ """
+ return PhoneNumber("CELL",
+ "+{0:010d}".format(random.randint(0, 9999999999)))
+
+
+def generate_random_string(length=8,
+ charset="{}{}{}".format(string.digits,
+ string.ascii_letters,
+ string.punctuation)):
+ """Generate a random string of specified length from the characterset
+ """
+ # Remove ; since that would make 2 words.
+ charset = charset.replace(";", "")
+ name = []
+ for i in range(length):
+ name.append(random.choice(charset))
+ return "".join(name)
+
+
+def generate_contact_list(destination_path,
+ file_name,
+ contact_count,
+ phone_number_count=1):
+ """Generate a simple VCF file for count contacts with basic content.
+
+ An example with count = 1 and local_number = 2]
+
+ BEGIN:VCARD
+ VERSION:2.1
+ N:Person;1;;;
+ FN:1 Person
+ TEL;CELL:+1-555-555-1234
+ TEL;CELL:+1-555-555-4321
+ EMAIL;PREF:person1@gmail.com
+ END:VCARD
+ """
+ vcards = []
+ for i in range(contact_count):
+ current_contact = VCard()
+ current_contact.first_name = generate_random_string(
+ random.randint(1, 19))
+ current_contact.last_name = generate_random_string(
+ random.randint(1, 19))
+ current_contact.email = "{}{}@{}.{}".format(
+ current_contact.last_name, current_contact.first_name,
+ generate_random_string(random.randint(1, 19)),
+ generate_random_string(random.randint(1, 4)))
+ for number in range(phone_number_count):
+ current_contact.add_phone_number(generate_random_phone_number())
+ vcards.append(current_contact)
+ create_new_contacts_vcf_from_vcards(destination_path, file_name, vcards)
+
+
+def create_new_contacts_vcf_from_vcards(destination_path, vcf_file_name,
+ vcards):
+ """Create a new file with filename
+ """
+ contact_file = open("{}{}".format(destination_path, vcf_file_name), "w+")
+ for card in vcards:
+ contact_file.write(str(card))
+ contact_file.close()
+
+
+def get_contact_count(device):
+ """Returns the number of name:phone number pairs.
+ """
+ contact_list = device.droid.contactsQueryContent(
+ CONTACTS_URI, ["display_name", "data1"], "", [], "display_name")
+ return len(contact_list)
+
+
+def import_device_contacts_from_vcf(device, destination_path, vcf_file, timeout=10):
+ """Uploads and import vcf file to device.
+ """
+ number_count = phone_number_count(destination_path, vcf_file)
+ device.log.info("Trying to add {} phone numbers.".format(number_count))
+ local_phonebook_path = "{}{}".format(destination_path, vcf_file)
+ phone_phonebook_path = "{}{}".format(STORAGE_PATH, vcf_file)
+ device.adb.push("{} {}".format(local_phonebook_path, phone_phonebook_path))
+ device.droid.importVcf("file://{}{}".format(STORAGE_PATH, vcf_file))
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ #TODO: use unattended way to bypass contact import module instead of keyevent
+ if "ImportVCardActivity" in device.get_my_current_focus_window():
+ # keyevent to allow contacts import from vcf file
+ for key in ["DPAD_RIGHT", "DPAD_RIGHT", "ENTER"]:
+ device.adb.shell("input keyevent KEYCODE_{}".format(key))
+ break
+ time.sleep(1)
+ if wait_for_phone_number_update_complete(device, number_count):
+ return number_count
+ else:
+ return 0
+
+
+def export_device_contacts_to_vcf(device, destination_path, vcf_file):
+ """Export and download vcf file from device.
+ """
+ path_on_phone = "{}{}".format(STORAGE_PATH, vcf_file)
+ device.droid.exportVcf("{}".format(path_on_phone))
+ # Download and then remove file from device
+ device.adb.pull("{} {}".format(path_on_phone, destination_path))
+ return True
+
+
+def delete_vcf_files(device):
+ """Deletes all files with .vcf extension
+ """
+ files = device.adb.shell("ls {}".format(STORAGE_PATH))
+ for file_name in files.split():
+ if ".vcf" in file_name:
+ device.adb.shell("rm -f {}{}".format(STORAGE_PATH, file_name))
+
+
+def erase_contacts(device):
+ """Erase all contacts out of devices contact database.
+ """
+ device.log.info("Erasing contacts.")
+ if get_contact_count(device) > 0:
+ device.droid.contactsEraseAll()
+ try:
+ device.ed.pop_event(CONTACTS_ERASED_CALLBACK, PBAP_SYNC_TIME)
+ except queue.Empty:
+ log.error("Phone book not empty.")
+ return False
+ return True
+
+
+def wait_for_phone_number_update_complete(device, expected_count):
+ """Check phone_number count on device and wait for updates until it has the
+ expected number of phone numbers in its contact database.
+ """
+ update_completed = True
+ try:
+ while (expected_count != get_contact_count(device) and
+ device.ed.pop_event(CONTACTS_CHANGED_CALLBACK, PBAP_SYNC_TIME)):
+ pass
+ except queue.Empty:
+ log.error("Contacts failed to update.")
+ update_completed = False
+ device.log.info("Found {} out of the expected {} contacts.".format(
+ get_contact_count(device), expected_count))
+ return update_completed
+
+
+def wait_for_call_log_update_complete(device, expected_count):
+ """Check call log count on device and wait for updates until it has the
+ expected number of calls in its call log database.
+ """
+ update_completed = True
+ try:
+ while (expected_count != device.droid.callLogGetCount() and
+ device.ed.pop_event(CALL_LOG_CHANGED, PBAP_SYNC_TIME)):
+ pass
+ except queue.Empty:
+ log.error("Call Log failed to update.")
+ update_completed = False
+ device.log.info("Found {} out of the expected {} call logs.".format(
+ device.droid.callLogGetCount(), expected_count))
+ return
+
+
+def add_call_log(device, call_log_type, phone_number, call_time):
+ """Add call number and time to specified log.
+ """
+ new_call_log = {}
+ new_call_log["type"] = str(call_log_type)
+ new_call_log["number"] = phone_number
+ new_call_log["time"] = str(call_time)
+ device.droid.callLogsPut(new_call_log)
+
+
+def get_and_compare_call_logs(pse, pce, call_log_type):
+ """Gather and compare call logs from PSE and PCE for the specified type.
+ """
+ pse_call_log = pse.droid.callLogsGet(call_log_type)
+ pce_call_log = pce.droid.callLogsGet(call_log_type)
+ return compare_call_logs(pse_call_log, pce_call_log)
+
+
+def normalize_phonenumber(phone_number):
+ """Remove all non-digits from phone_number
+ """
+ return re.sub(r"\D", "", phone_number)
+
+
+def compare_call_logs(pse_call_log, pce_call_log):
+ """Gather and compare call logs from PSE and PCE for the specified type.
+ """
+ call_logs_match = True
+ if len(pse_call_log) == len(pce_call_log):
+ for i in range(len(pse_call_log)):
+ # Compare the phone number
+ if normalize_phonenumber(pse_call_log[i][
+ "number"]) != normalize_phonenumber(pce_call_log[i][
+ "number"]):
+ log.warning("Call Log numbers differ")
+ call_logs_match = False
+
+ # Compare which log it was taken from (Incomming, Outgoing, Missed
+ if pse_call_log[i]["type"] != pce_call_log[i]["type"]:
+ log.warning("Call Log types differ")
+ call_logs_match = False
+
+ # Compare time to truncated second.
+ if int(pse_call_log[i]["date"]) // 1000 != int(pce_call_log[i][
+ "date"]) // 1000:
+ log.warning("Call log times don't match, check timezone.")
+ call_logs_match = False
+
+ else:
+ log.warning("Call Log lengths differ {}:{}".format(
+ len(pse_call_log), len(pce_call_log)))
+ call_logs_match = False
+
+ if not call_logs_match:
+ log.info("PSE Call Log:")
+ log.info(pse_call_log)
+ log.info("PCE Call Log:")
+ log.info(pce_call_log)
+
+ return call_logs_match
+
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_factory.py b/acts_tests/acts_contrib/test_utils/bt/bt_factory.py
new file mode 100644
index 0000000..cbc4f09
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_factory.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import importlib
+
+
+def create(configs):
+ """Used to create instance of bt implementation.
+
+ A list of of configuration is extracted from configs.
+ The modules names are extracted and passed to import_module
+ to get the specific implementation, which gets appended to a
+ device list.
+ Args:
+ configs: A configurations dictionary that contains
+ a list of configs for each device in configs['user_params']['BtDevice'].
+
+ Returns:
+ A list of bt implementations.
+ """
+ bt_devices = []
+ for config in configs:
+ bt_name = config['bt_module']
+ bt = importlib.import_module('acts_contrib.test_utils.bt.bt_implementations.%s'
+ % bt_name)
+ bt_devices.append(bt.BluethoothDevice(config))
+ return bt_devices
+
+
+def destroy(bt_device_list):
+ for bt in bt_device_list:
+ bt.close()
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_gatt_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_gatt_utils.py
new file mode 100644
index 0000000..e902ec2
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_gatt_utils.py
@@ -0,0 +1,418 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import logging
+
+from acts_contrib.test_utils.bt.bt_test_utils import BtTestUtilsError
+from acts_contrib.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_connection_state
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_phy_mask
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+import pprint
+from queue import Empty
+
+default_timeout = 10
+log = logging
+
+
+class GattTestUtilsError(Exception):
+ pass
+
+
+def setup_gatt_connection(cen_ad,
+ mac_address,
+ autoconnect,
+ transport=gatt_transport['auto'],
+ opportunistic=False):
+ gatt_callback = cen_ad.droid.gattCreateGattCallback()
+ log.info("Gatt Connect to mac address {}.".format(mac_address))
+ bluetooth_gatt = cen_ad.droid.gattClientConnectGatt(
+ gatt_callback, mac_address, autoconnect, transport, opportunistic,
+ gatt_phy_mask['1m_mask'])
+ expected_event = gatt_cb_strings['gatt_conn_change'].format(gatt_callback)
+ try:
+ event = cen_ad.ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ close_gatt_client(cen_ad, bluetooth_gatt)
+ raise GattTestUtilsError(
+ "Could not establish a connection to "
+ "peripheral. Expected event: {}".format(expected_event))
+ if event['data']['State'] != gatt_connection_state['connected']:
+ close_gatt_client(cen_ad, bluetooth_gatt)
+ try:
+ cen_ad.droid.gattClientClose(bluetooth_gatt)
+ except Exception:
+ self.log.debug("Failed to close gatt client.")
+ raise GattTestUtilsError("Could not establish a connection to "
+ "peripheral. Event Details: {}".format(
+ pprint.pformat(event)))
+ return bluetooth_gatt, gatt_callback
+
+
+def close_gatt_client(cen_ad, bluetooth_gatt):
+ try:
+ cen_ad.droid.gattClientClose(bluetooth_gatt)
+ except Exception:
+ log.debug("Failed to close gatt client.")
+
+
+def disconnect_gatt_connection(cen_ad, bluetooth_gatt, gatt_callback):
+ cen_ad.droid.gattClientDisconnect(bluetooth_gatt)
+ wait_for_gatt_disconnect_event(cen_ad, gatt_callback)
+ return
+
+
+def wait_for_gatt_disconnect_event(cen_ad, gatt_callback):
+ expected_event = gatt_cb_strings['gatt_conn_change'].format(gatt_callback)
+ try:
+ event = cen_ad.ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ raise GattTestUtilsError(
+ gatt_cb_err['gatt_conn_change_err'].format(expected_event))
+ found_state = event['data']['State']
+ expected_state = gatt_connection_state['disconnected']
+ if found_state != expected_state:
+ raise GattTestUtilsError(
+ "GATT connection state change expected {}, found {}".format(
+ expected_event, found_state))
+ return
+
+
+def orchestrate_gatt_connection(cen_ad,
+ per_ad,
+ transport=gatt_transport['le'],
+ mac_address=None,
+ autoconnect=False,
+ opportunistic=False):
+ adv_callback = None
+ if mac_address is None:
+ if transport == gatt_transport['le']:
+ try:
+ mac_address, adv_callback, scan_callback = (
+ get_mac_address_of_generic_advertisement(cen_ad, per_ad))
+ except BtTestUtilsError as err:
+ raise GattTestUtilsError(
+ "Error in getting mac address: {}".format(err))
+ else:
+ mac_address = per_ad.droid.bluetoothGetLocalAddress()
+ adv_callback = None
+ bluetooth_gatt, gatt_callback = setup_gatt_connection(
+ cen_ad, mac_address, autoconnect, transport, opportunistic)
+ return bluetooth_gatt, gatt_callback, adv_callback
+
+
+def run_continuous_write_descriptor(cen_droid,
+ cen_ed,
+ per_droid,
+ per_ed,
+ gatt_server,
+ gatt_server_callback,
+ bluetooth_gatt,
+ services_count,
+ discovered_services_index,
+ number_of_iterations=100000):
+ log.info("Starting continuous write")
+ bt_device_id = 0
+ status = 1
+ offset = 1
+ test_value = [1, 2, 3, 4, 5, 6, 7]
+ test_value_return = [1, 2, 3]
+ for _ in range(number_of_iterations):
+ try:
+ for i in range(services_count):
+ characteristic_uuids = (
+ cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+ discovered_services_index, i))
+ log.info(characteristic_uuids)
+ for characteristic in characteristic_uuids:
+ descriptor_uuids = (
+ cen_droid.gattClientGetDiscoveredDescriptorUuids(
+ discovered_services_index, i, characteristic))
+ log.info(descriptor_uuids)
+ for descriptor in descriptor_uuids:
+ cen_droid.gattClientDescriptorSetValue(
+ bluetooth_gatt, discovered_services_index, i,
+ characteristic, descriptor, test_value)
+ cen_droid.gattClientWriteDescriptor(
+ bluetooth_gatt, discovered_services_index, i,
+ characteristic, descriptor)
+ expected_event = gatt_cb_strings[
+ 'desc_write_req'].format(gatt_server_callback)
+ try:
+ event = per_ed.pop_event(expected_event,
+ default_timeout)
+ except Empty:
+ log.error(gatt_cb_err['desc_write_req_err'].format(
+ expected_event))
+ return False
+ request_id = event['data']['requestId']
+ found_value = event['data']['value']
+ if found_value != test_value:
+ log.error(
+ "Values didn't match. Found: {}, Expected: "
+ "{}".format(found_value, test_value))
+ per_droid.gattServerSendResponse(
+ gatt_server, bt_device_id, request_id, status,
+ offset, test_value_return)
+ expected_event = gatt_cb_strings['desc_write'].format(
+ bluetooth_gatt)
+ try:
+ cen_ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ log.error(gatt_cb_strings['desc_write_err'].format(
+ expected_event))
+ raise Exception("Thread ended prematurely.")
+ except Exception as err:
+ log.error("Continuing but found exception: {}".format(err))
+
+
+def setup_characteristics_and_descriptors(droid):
+ characteristic_input = [
+ {
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_characteristic['property_write']
+ | gatt_characteristic['property_write_no_response'],
+ 'permission':
+ gatt_characteristic['permission_write']
+ },
+ {
+ 'uuid':
+ "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
+ },
+ {
+ 'uuid':
+ "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
+ },
+ ]
+ descriptor_input = [{
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_descriptor['permission_write'],
+ }, {
+ 'uuid':
+ "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_characteristic['permission_write'],
+ }]
+ characteristic_list = setup_gatt_characteristics(droid,
+ characteristic_input)
+ descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+ return characteristic_list, descriptor_list
+
+
+def setup_multiple_services(per_ad):
+ per_droid, per_ed = per_ad.droid, per_ad.ed
+ gatt_server_callback = per_droid.gattServerCreateGattServerCallback()
+ gatt_server = per_droid.gattServerOpenGattServer(gatt_server_callback)
+ characteristic_list, descriptor_list = (
+ setup_characteristics_and_descriptors(per_droid))
+ per_droid.gattServerCharacteristicAddDescriptor(characteristic_list[1],
+ descriptor_list[0])
+ per_droid.gattServerCharacteristicAddDescriptor(characteristic_list[2],
+ descriptor_list[1])
+ gattService = per_droid.gattServerCreateService(
+ "00000000-0000-1000-8000-00805f9b34fb", gatt_service_types['primary'])
+ gattService2 = per_droid.gattServerCreateService(
+ "FFFFFFFF-0000-1000-8000-00805f9b34fb", gatt_service_types['primary'])
+ gattService3 = per_droid.gattServerCreateService(
+ "3846D7A0-69C8-11E4-BA00-0002A5D5C51B", gatt_service_types['primary'])
+ for characteristic in characteristic_list:
+ per_droid.gattServerAddCharacteristicToService(gattService,
+ characteristic)
+ per_droid.gattServerAddService(gatt_server, gattService)
+ expected_event = gatt_cb_strings['serv_added'].format(gatt_server_callback)
+ try:
+ per_ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ per_ad.droid.gattServerClose(gatt_server)
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
+ for characteristic in characteristic_list:
+ per_droid.gattServerAddCharacteristicToService(gattService2,
+ characteristic)
+ per_droid.gattServerAddService(gatt_server, gattService2)
+ try:
+ per_ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ per_ad.droid.gattServerClose(gatt_server)
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
+ for characteristic in characteristic_list:
+ per_droid.gattServerAddCharacteristicToService(gattService3,
+ characteristic)
+ per_droid.gattServerAddService(gatt_server, gattService3)
+ try:
+ per_ed.pop_event(expected_event, default_timeout)
+ except Empty:
+ per_ad.droid.gattServerClose(gatt_server)
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
+ return gatt_server_callback, gatt_server
+
+
+def setup_characteristics_and_descriptors(droid):
+ characteristic_input = [
+ {
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_characteristic['property_write']
+ | gatt_characteristic['property_write_no_response'],
+ 'permission':
+ gatt_characteristic['property_write']
+ },
+ {
+ 'uuid':
+ "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
+ },
+ {
+ 'uuid':
+ "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
+ },
+ ]
+ descriptor_input = [{
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_descriptor['permission_write'],
+ }, {
+ 'uuid':
+ "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_characteristic['permission_write'],
+ }]
+ characteristic_list = setup_gatt_characteristics(droid,
+ characteristic_input)
+ descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+ return characteristic_list, descriptor_list
+
+
+def setup_gatt_characteristics(droid, input):
+ characteristic_list = []
+ for item in input:
+ index = droid.gattServerCreateBluetoothGattCharacteristic(
+ item['uuid'], item['property'], item['permission'])
+ characteristic_list.append(index)
+ return characteristic_list
+
+
+def setup_gatt_descriptors(droid, input):
+ descriptor_list = []
+ for item in input:
+ index = droid.gattServerCreateBluetoothGattDescriptor(
+ item['uuid'],
+ item['property'],
+ )
+ descriptor_list.append(index)
+ log.info("setup descriptor list: {}".format(descriptor_list))
+ return descriptor_list
+
+
+def setup_gatt_mtu(cen_ad, bluetooth_gatt, gatt_callback, mtu):
+ """utility function to set mtu for GATT connection.
+
+ Steps:
+ 1. Request mtu change.
+ 2. Check if the mtu is changed to the new value
+
+ Args:
+ cen_ad: test device for client to scan.
+ bluetooth_gatt: GATT object
+ mtu: new mtu value to be set
+
+ Returns:
+ If success, return True.
+ if fail, return False
+ """
+ cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, mtu)
+ expected_event = gatt_cb_strings['mtu_changed'].format(gatt_callback)
+ try:
+ mtu_event = cen_ad.ed.pop_event(expected_event, default_timeout)
+ mtu_size_found = mtu_event['data']['MTU']
+ if mtu_size_found != mtu:
+ log.error("MTU size found: {}, expected: {}".format(
+ mtu_size_found, mtu))
+ return False
+ except Empty:
+ log.error(gatt_cb_err['mtu_changed_err'].format(expected_event))
+ return False
+ return True
+
+
+def log_gatt_server_uuids(cen_ad,
+ discovered_services_index,
+ bluetooth_gatt=None):
+ services_count = cen_ad.droid.gattClientGetDiscoveredServicesCount(
+ discovered_services_index)
+ for i in range(services_count):
+ service = cen_ad.droid.gattClientGetDiscoveredServiceUuid(
+ discovered_services_index, i)
+ log.info("Discovered service uuid {}".format(service))
+ characteristic_uuids = (
+ cen_ad.droid.gattClientGetDiscoveredCharacteristicUuids(
+ discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ cen_ad.droid.gattClientGetDiscoveredDescriptorUuidsByIndex(
+ discovered_services_index, i, j))
+ if bluetooth_gatt:
+ char_inst_id = cen_ad.droid.gattClientGetCharacteristicInstanceId(
+ bluetooth_gatt, discovered_services_index, i, j)
+ log.info("Discovered characteristic handle uuid: {} {}".format(
+ hex(char_inst_id), characteristic_uuids[j]))
+ for k in range(len(descriptor_uuids)):
+ desc_inst_id = cen_ad.droid.gattClientGetDescriptorInstanceId(
+ bluetooth_gatt, discovered_services_index, i, j, k)
+ log.info("Discovered descriptor handle uuid: {} {}".format(
+ hex(desc_inst_id), descriptor_uuids[k]))
+ else:
+ log.info("Discovered characteristic uuid: {}".format(
+ characteristic_uuids[j]))
+ for k in range(len(descriptor_uuids)):
+ log.info("Discovered descriptor uuid {}".format(
+ descriptor_uuids[k]))
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_implementations/bt_stub.py b/acts_tests/acts_contrib/test_utils/bt/bt_implementations/bt_stub.py
new file mode 100644
index 0000000..50c9393
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_implementations/bt_stub.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""A stub implementation of a DUT interface.
+
+This a stub interface which allows automated test to run
+without automating the hardware. This here for two reasons, first
+as an example of how to write a dut implementation, and second as
+an implementation that can be used to test case without writing
+out the full implementation.
+"""
+
+import logging
+
+class BluethoothDevice:
+ """The api interface used in the test for the stub.
+
+ This is interface which defines all the functions that can be
+ called by the bt test suite.
+ """
+
+ def __init__(self, config):
+ print('Init Stub with ', config)
+ logging.info('Init Stub with '+str(config))
+
+ def answer_phone(self):
+ input('Answer the phone and then press enter\n')
+
+ def hang_up(self):
+ input('Hang up the phone and then press enter\n')
+
+ def toggle_pause(self):
+ input('Press pause on device then press enter\n')
+
+ def volume(self, direction):
+ """Adjust the volume specified by the value of direction.
+
+ Args:
+ direction: A string that is either UP or DOWN
+ that indicates which way to adjust the volume.
+ """
+
+ return input('move volume '+direction+' and then press enter\n')
+
+ def connect(self, android):
+ input('Connect device and press enter\n')
+
+ def is_bt_connected(self):
+ con = input('Is device connected? y/n').lower()
+ while con not in ['y', 'n']:
+ con = input('Is device connected? y/n').lower()
+ return con == 'y'
+
+ def close(self):
+ """This where the hardware is released.
+ """
+ print('Close Stub')
+ logging.info('Close Stub')
+
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_metrics_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_metrics_utils.py
new file mode 100644
index 0000000..f30fe84
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_metrics_utils.py
@@ -0,0 +1,64 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import base64
+from acts_contrib.test_utils.bt.protos import bluetooth_pb2
+
+
+def get_bluetooth_metrics(ad):
+ """
+ Get metric proto from the Bluetooth stack
+
+ Parameter:
+ ad - Android device
+
+ Return:
+ a protobuf object representing Bluetooth metric
+
+ """
+ bluetooth_log = bluetooth_pb2.BluetoothLog()
+ proto_native_str_64 = \
+ ad.adb.shell("/system/bin/dumpsys bluetooth_manager --proto-bin")
+ proto_native_str = base64.b64decode(proto_native_str_64)
+ bluetooth_log.MergeFromString(proto_native_str)
+ return bluetooth_log
+
+def get_bluetooth_profile_connection_stats_map(bluetooth_log):
+ return project_pairs_list_to_map(bluetooth_log.profile_connection_stats,
+ lambda stats : stats.profile_id,
+ lambda stats : stats.num_times_connected,
+ lambda a, b : a + b)
+
+def get_bluetooth_headset_profile_connection_stats_map(bluetooth_log):
+ return project_pairs_list_to_map(bluetooth_log.headset_profile_connection_stats,
+ lambda stats : stats.profile_id,
+ lambda stats : stats.num_times_connected,
+ lambda a, b : a + b)
+
+def project_pairs_list_to_map(pairs_list, get_key, get_value, merge_value):
+ """
+ Project a list of pairs (A, B) into a map of [A] --> B
+ :param pairs_list: list of pairs (A, B)
+ :param get_key: function used to get key from pair (A, B)
+ :param get_value: function used to get value from pair (A, B)
+ :param merge_value: function used to merge values of B
+ :return: a map of [A] --> B
+ """
+ result = {}
+ for item in pairs_list:
+ my_key = get_key(item)
+ if my_key in result:
+ result[my_key] = merge_value(result[my_key], get_value(item))
+ else:
+ result[my_key] = get_value(item)
+ return result
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_power_test_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_power_test_utils.py
new file mode 100644
index 0000000..899621f
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_power_test_utils.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+import acts_contrib.test_utils.bt.BleEnum as bleenum
+import acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder as icb
+
+BLE_LOCATION_SCAN_ENABLE = 'settings put global ble_scan_always_enabled 1'
+BLE_LOCATION_SCAN_DISABLE = 'settings put global ble_scan_always_enabled 0'
+SCREEN_WAIT_TIME = 1
+
+
+class MediaControl(object):
+ """Media control using adb shell for power testing.
+
+ Object to control media play status using adb.
+ """
+
+ def __init__(self, android_device, music_file):
+ """Initialize the media_control class.
+
+ Args:
+ android_dut: android_device object
+ music_file: location of the music file
+ """
+ self.android_device = android_device
+ self.music_file = music_file
+
+ def player_on_foreground(self):
+ """Turn on screen and make sure media play is on foreground
+
+ All media control keycode only works when screen is on and media player
+ is on the foreground. Turn off screen first and turn it on to make sure
+ all operation is based on the same screen status. Otherwise, 'MENU' key
+ would block command to be sent.
+ """
+ self.android_device.droid.goToSleepNow()
+ time.sleep(SCREEN_WAIT_TIME)
+ self.android_device.droid.wakeUpNow()
+ time.sleep(SCREEN_WAIT_TIME)
+ self.android_device.send_keycode('MENU')
+ time.sleep(SCREEN_WAIT_TIME)
+
+ def play(self):
+ """Start playing music.
+
+ """
+ self.player_on_foreground()
+ PLAY = 'am start -a android.intent.action.VIEW -d file://{} -t audio/wav'.format(
+ self.music_file)
+ self.android_device.adb.shell(PLAY)
+
+ def pause(self):
+ """Pause music.
+
+ """
+ self.player_on_foreground()
+ self.android_device.send_keycode('MEDIA_PAUSE')
+
+ def resume(self):
+ """Pause music.
+
+ """
+ self.player_on_foreground()
+ self.android_device.send_keycode('MEDIA_PLAY')
+
+ def stop(self):
+ """Stop music and close media play.
+
+ """
+ self.player_on_foreground()
+ self.android_device.send_keycode('MEDIA_STOP')
+
+
+def start_apk_ble_adv(dut, adv_mode, adv_power_level, adv_duration):
+ """Trigger BLE advertisement from power-test.apk.
+
+ Args:
+ dut: Android device under test, type AndroidDevice obj
+ adv_mode: The BLE advertisement mode.
+ {0: 'LowPower', 1: 'Balanced', 2: 'LowLatency'}
+ adv_power_leve: The BLE advertisement TX power level.
+ {0: 'UltraLowTXPower', 1: 'LowTXPower', 2: 'MediumTXPower,
+ 3: HighTXPower}
+ adv_duration: duration of advertisement in seconds, type int
+ """
+
+ adv_duration = str(adv_duration) + 's'
+ builder = icb.InstrumentationTestCommandBuilder.default()
+ builder.add_test_class(
+ "com.google.android.device.power.tests.ble.BleAdvertise")
+ builder.set_manifest_package("com.google.android.device.power")
+ builder.set_runner("androidx.test.runner.AndroidJUnitRunner")
+ builder.add_key_value_param("cool-off-duration", "0s")
+ builder.add_key_value_param("idle-duration", "0s")
+ builder.add_key_value_param(
+ "com.android.test.power.receiver.ADVERTISE_MODE", adv_mode)
+ builder.add_key_value_param("com.android.test.power.receiver.POWER_LEVEL",
+ adv_power_level)
+ builder.add_key_value_param(
+ "com.android.test.power.receiver.ADVERTISING_DURATION", adv_duration)
+
+ adv_command = builder.build() + ' &'
+ logging.info('Start BLE {} at {} for {} seconds'.format(
+ bleenum.AdvertiseSettingsAdvertiseMode(adv_mode).name,
+ bleenum.AdvertiseSettingsAdvertiseTxPower(adv_power_level).name,
+ adv_duration))
+ dut.adb.shell_nb(adv_command)
+
+
+def start_apk_ble_scan(dut, scan_mode, scan_duration):
+ """Build the command to trigger BLE scan from power-test.apk.
+
+ Args:
+ dut: Android device under test, type AndroidDevice obj
+ scan_mode: The BLE scan mode.
+ {0: 'LowPower', 1: 'Balanced', 2: 'LowLatency', -1: 'Opportunistic'}
+ scan_duration: duration of scan in seconds, type int
+ Returns:
+ adv_command: the command for BLE scan
+ """
+ scan_duration = str(scan_duration) + 's'
+ builder = icb.InstrumentationTestCommandBuilder.default()
+ builder.set_proto_path()
+ builder.add_flag('--no-isolated-storage')
+ builder.add_test_class("com.google.android.device.power.tests.ble.BleScan")
+ builder.set_manifest_package("com.google.android.device.power")
+ builder.set_runner("androidx.test.runner.AndroidJUnitRunner")
+ builder.add_key_value_param("cool-off-duration", "0s")
+ builder.add_key_value_param("idle-duration", "0s")
+ builder.add_key_value_param("com.android.test.power.receiver.SCAN_MODE",
+ scan_mode)
+ builder.add_key_value_param("com.android.test.power.receiver.MATCH_MODE",
+ 2)
+ builder.add_key_value_param(
+ "com.android.test.power.receiver.SCAN_DURATION", scan_duration)
+ builder.add_key_value_param(
+ "com.android.test.power.receiver.CALLBACK_TYPE", 1)
+ builder.add_key_value_param("com.android.test.power.receiver.FILTER",
+ 'true')
+
+ scan_command = builder.build() + ' &'
+ logging.info('Start BLE {} scans for {} seconds'.format(
+ bleenum.ScanSettingsScanMode(scan_mode).name, scan_duration))
+ dut.adb.shell_nb(scan_command)
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_test_utils.py b/acts_tests/acts_contrib/test_utils/bt/bt_test_utils.py
new file mode 100644
index 0000000..653e4df
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_test_utils.py
@@ -0,0 +1,1863 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import logging
+import os
+import random
+import re
+import string
+import threading
+import time
+from queue import Empty
+from subprocess import call
+
+from acts import asserts
+from acts_contrib.test_utils.bt.bt_constants import adv_fail
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import batch_scan_not_supported_list
+from acts_contrib.test_utils.bt.bt_constants import batch_scan_result
+from acts_contrib.test_utils.bt.bt_constants import bits_per_samples
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_a2dp_codec_config_changed
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_off
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_on
+from acts_contrib.test_utils.bt.bt_constants import \
+ bluetooth_profile_connection_state_changed
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_socket_conn_test_uuid
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_constants
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_states
+from acts_contrib.test_utils.bt.bt_constants import bt_rfcomm_uuids
+from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts_contrib.test_utils.bt.bt_constants import btsnoop_last_log_path_on_device
+from acts_contrib.test_utils.bt.bt_constants import btsnoop_log_path_on_device
+from acts_contrib.test_utils.bt.bt_constants import channel_modes
+from acts_contrib.test_utils.bt.bt_constants import codec_types
+from acts_contrib.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts_contrib.test_utils.bt.bt_constants import default_rfcomm_timeout_ms
+from acts_contrib.test_utils.bt.bt_constants import hid_id_keyboard
+from acts_contrib.test_utils.bt.bt_constants import pairing_variant_passkey_confirmation
+from acts_contrib.test_utils.bt.bt_constants import pan_connect_timeout
+from acts_contrib.test_utils.bt.bt_constants import sample_rates
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_constants import sig_uuid_constants
+from acts_contrib.test_utils.bt.bt_constants import small_timeout
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.utils import exe_cmd
+
+from acts import utils
+
+log = logging
+
+advertisements_to_devices = {}
+
+
+class BtTestUtilsError(Exception):
+ pass
+
+
+def _add_android_device_to_dictionary(android_device, profile_list,
+ selector_dict):
+ """Adds the AndroidDevice and supported features to the selector dictionary
+
+ Args:
+ android_device: The Android device.
+ profile_list: The list of profiles the Android device supports.
+ """
+ for profile in profile_list:
+ if profile in selector_dict and android_device not in selector_dict[
+ profile]:
+ selector_dict[profile].append(android_device)
+ else:
+ selector_dict[profile] = [android_device]
+
+
+def bluetooth_enabled_check(ad, timeout_sec=5):
+ """Checks if the Bluetooth state is enabled, if not it will attempt to
+ enable it.
+
+ Args:
+ ad: The Android device list to enable Bluetooth on.
+ timeout_sec: number of seconds to wait for toggle to take effect.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ if not ad.droid.bluetoothCheckState():
+ ad.droid.bluetoothToggleState(True)
+ expected_bluetooth_on_event_name = bluetooth_on
+ try:
+ ad.ed.pop_event(expected_bluetooth_on_event_name,
+ bt_default_timeout)
+ except Empty:
+ ad.log.info(
+ "Failed to toggle Bluetooth on(no broadcast received).")
+ # Try one more time to poke at the actual state.
+ if ad.droid.bluetoothCheckState():
+ ad.log.info(".. actual state is ON")
+ return True
+ ad.log.error(".. actual state is OFF")
+ return False
+ end_time = time.time() + timeout_sec
+ while not ad.droid.bluetoothCheckState() and time.time() < end_time:
+ time.sleep(1)
+ return ad.droid.bluetoothCheckState()
+
+
+def check_device_supported_profiles(droid):
+ """Checks for Android device supported profiles.
+
+ Args:
+ droid: The droid object to query.
+
+ Returns:
+ A dictionary of supported profiles.
+ """
+ profile_dict = {}
+ profile_dict['hid'] = droid.bluetoothHidIsReady()
+ profile_dict['hsp'] = droid.bluetoothHspIsReady()
+ profile_dict['a2dp'] = droid.bluetoothA2dpIsReady()
+ profile_dict['avrcp'] = droid.bluetoothAvrcpIsReady()
+ profile_dict['a2dp_sink'] = droid.bluetoothA2dpSinkIsReady()
+ profile_dict['hfp_client'] = droid.bluetoothHfpClientIsReady()
+ profile_dict['pbap_client'] = droid.bluetoothPbapClientIsReady()
+ return profile_dict
+
+
+def cleanup_scanners_and_advertisers(scn_android_device, scn_callback_list,
+ adv_android_device, adv_callback_list):
+ """Try to gracefully stop all scanning and advertising instances.
+
+ Args:
+ scn_android_device: The Android device that is actively scanning.
+ scn_callback_list: The scan callback id list that needs to be stopped.
+ adv_android_device: The Android device that is actively advertising.
+ adv_callback_list: The advertise callback id list that needs to be
+ stopped.
+ """
+ scan_droid, scan_ed = scn_android_device.droid, scn_android_device.ed
+ adv_droid = adv_android_device.droid
+ try:
+ for scan_callback in scn_callback_list:
+ scan_droid.bleStopBleScan(scan_callback)
+ except Exception as err:
+ scn_android_device.log.debug(
+ "Failed to stop LE scan... reseting Bluetooth. Error {}".format(
+ err))
+ reset_bluetooth([scn_android_device])
+ try:
+ for adv_callback in adv_callback_list:
+ adv_droid.bleStopBleAdvertising(adv_callback)
+ except Exception as err:
+ adv_android_device.log.debug(
+ "Failed to stop LE advertisement... reseting Bluetooth. Error {}".
+ format(err))
+ reset_bluetooth([adv_android_device])
+
+
+def clear_bonded_devices(ad):
+ """Clear bonded devices from the input Android device.
+
+ Args:
+ ad: the Android device performing the connection.
+ Returns:
+ True if clearing bonded devices was successful, false if unsuccessful.
+ """
+ bonded_device_list = ad.droid.bluetoothGetBondedDevices()
+ while bonded_device_list:
+ device_address = bonded_device_list[0]['address']
+ if not ad.droid.bluetoothUnbond(device_address):
+ log.error("Failed to unbond {} from {}".format(
+ device_address, ad.serial))
+ return False
+ log.info("Successfully unbonded {} from {}".format(
+ device_address, ad.serial))
+ #TODO: wait for BOND_STATE_CHANGED intent instead of waiting
+ time.sleep(1)
+
+ # If device was first connected using LE transport, after bonding it is
+ # accessible through it's LE address, and through it classic address.
+ # Unbonding it will unbond two devices representing different
+ # "addresses". Attempt to unbond such already unbonded devices will
+ # result in bluetoothUnbond returning false.
+ bonded_device_list = ad.droid.bluetoothGetBondedDevices()
+ return True
+
+
+def connect_phone_to_headset(android,
+ headset,
+ timeout=bt_default_timeout,
+ connection_check_period=10):
+ """Connects android phone to bluetooth headset.
+ Headset object must have methods power_on and enter_pairing_mode,
+ and attribute mac_address.
+
+ Args:
+ android: AndroidDevice object with SL4A installed.
+ headset: Object with attribute mac_address and methods power_on and
+ enter_pairing_mode.
+ timeout: Seconds to wait for devices to connect.
+ connection_check_period: how often to check for connection once the
+ SL4A connect RPC has been sent.
+ Returns:
+ connected (bool): True if devices are paired and connected by end of
+ method. False otherwise.
+ """
+ headset_mac_address = headset.mac_address
+ connected = is_a2dp_src_device_connected(android, headset_mac_address)
+ log.info('Devices connected before pair attempt: %s' % connected)
+ if not connected:
+ # Turn on headset and initiate pairing mode.
+ headset.enter_pairing_mode()
+ android.droid.bluetoothStartPairingHelper()
+ start_time = time.time()
+ # If already connected, skip pair and connect attempt.
+ while not connected and (time.time() - start_time < timeout):
+ bonded_info = android.droid.bluetoothGetBondedDevices()
+ connected_info = android.droid.bluetoothGetConnectedDevices()
+ if headset.mac_address not in [
+ info["address"] for info in bonded_info
+ ]:
+ # Use SL4A to pair and connect with headset.
+ headset.enter_pairing_mode()
+ android.droid.bluetoothDiscoverAndBond(headset_mac_address)
+ elif headset.mac_address not in [
+ info["address"] for info in connected_info
+ ]:
+ #Device is bonded but not connected
+ android.droid.bluetoothConnectBonded(headset_mac_address)
+ else:
+ #Headset is connected, but A2DP profile is not
+ android.droid.bluetoothA2dpConnect(headset_mac_address)
+ log.info('Waiting for connection...')
+ time.sleep(connection_check_period)
+ # Check for connection.
+ connected = is_a2dp_src_device_connected(android, headset_mac_address)
+ log.info('Devices connected after pair attempt: %s' % connected)
+ return connected
+
+def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2):
+ """Connects pri droid to secondary droid.
+
+ Args:
+ pri_ad: AndroidDroid initiating connection
+ sec_ad: AndroidDroid accepting connection
+ profiles_set: Set of profiles to be connected
+ attempts: Number of attempts to try until failure.
+
+ Returns:
+ Pass if True
+ Fail if False
+ """
+ device_addr = sec_ad.droid.bluetoothGetLocalAddress()
+ # Allows extra time for the SDP records to be updated.
+ time.sleep(2)
+ curr_attempts = 0
+ while curr_attempts < attempts:
+ log.info("connect_pri_to_sec curr attempt {} total {}".format(
+ curr_attempts, attempts))
+ if _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
+ return True
+ curr_attempts += 1
+ log.error("connect_pri_to_sec failed to connect after {} attempts".format(
+ attempts))
+ return False
+
+
+def _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
+ """Connects pri droid to secondary droid.
+
+ Args:
+ pri_ad: AndroidDroid initiating connection.
+ sec_ad: AndroidDroid accepting connection.
+ profiles_set: Set of profiles to be connected.
+
+ Returns:
+ True of connection is successful, false if unsuccessful.
+ """
+ # Check if we support all profiles.
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_set:
+ if profile not in supported_profiles:
+ pri_ad.log.info("Profile {} is not supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ # First check that devices are bonded.
+ paired = False
+ for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
+ if paired_device['address'] == \
+ sec_ad.droid.bluetoothGetLocalAddress():
+ paired = True
+ break
+
+ if not paired:
+ pri_ad.log.error("Not paired to {}".format(sec_ad.serial))
+ return False
+
+ # Now try to connect them, the following call will try to initiate all
+ # connections.
+ pri_ad.droid.bluetoothConnectBonded(
+ sec_ad.droid.bluetoothGetLocalAddress())
+
+ end_time = time.time() + 10
+ profile_connected = set()
+ sec_addr = sec_ad.droid.bluetoothGetLocalAddress()
+ pri_ad.log.info("Profiles to be connected {}".format(profiles_set))
+ # First use APIs to check profile connection state
+ while (time.time() < end_time
+ and not profile_connected.issuperset(profiles_set)):
+ if (bt_profile_constants['headset_client'] not in profile_connected
+ and bt_profile_constants['headset_client'] in profiles_set):
+ if is_hfp_client_device_connected(pri_ad, sec_addr):
+ profile_connected.add(bt_profile_constants['headset_client'])
+ if (bt_profile_constants['a2dp'] not in profile_connected
+ and bt_profile_constants['a2dp'] in profiles_set):
+ if is_a2dp_src_device_connected(pri_ad, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp'])
+ if (bt_profile_constants['a2dp_sink'] not in profile_connected
+ and bt_profile_constants['a2dp_sink'] in profiles_set):
+ if is_a2dp_snk_device_connected(pri_ad, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp_sink'])
+ if (bt_profile_constants['map_mce'] not in profile_connected
+ and bt_profile_constants['map_mce'] in profiles_set):
+ if is_map_mce_device_connected(pri_ad, sec_addr):
+ profile_connected.add(bt_profile_constants['map_mce'])
+ if (bt_profile_constants['map'] not in profile_connected
+ and bt_profile_constants['map'] in profiles_set):
+ if is_map_mse_device_connected(pri_ad, sec_addr):
+ profile_connected.add(bt_profile_constants['map'])
+ time.sleep(0.1)
+ # If APIs fail, try to find the connection broadcast receiver.
+ while not profile_connected.issuperset(profiles_set):
+ try:
+ profile_event = pri_ad.ed.pop_event(
+ bluetooth_profile_connection_state_changed,
+ bt_default_timeout + 10)
+ pri_ad.log.info("Got event {}".format(profile_event))
+ except Exception:
+ pri_ad.log.error("Did not get {} profiles left {}".format(
+ bluetooth_profile_connection_state_changed, profile_connected))
+ return False
+
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+ if state == bt_profile_states['connected'] and \
+ device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+ profile_connected.add(profile)
+ pri_ad.log.info(
+ "Profiles connected until now {}".format(profile_connected))
+ # Failure happens inside the while loop. If we came here then we already
+ # connected.
+ return True
+
+
+def determine_max_advertisements(android_device):
+ """Determines programatically how many advertisements the Android device
+ supports.
+
+ Args:
+ android_device: The Android device to determine max advertisements of.
+
+ Returns:
+ The maximum advertisement count.
+ """
+ android_device.log.info(
+ "Determining number of maximum concurrent advertisements...")
+ advertisement_count = 0
+ bt_enabled = False
+ expected_bluetooth_on_event_name = bluetooth_on
+ if not android_device.droid.bluetoothCheckState():
+ android_device.droid.bluetoothToggleState(True)
+ try:
+ android_device.ed.pop_event(expected_bluetooth_on_event_name,
+ bt_default_timeout)
+ except Exception:
+ android_device.log.info(
+ "Failed to toggle Bluetooth on(no broadcast received).")
+ # Try one more time to poke at the actual state.
+ if android_device.droid.bluetoothCheckState() is True:
+ android_device.log.info(".. actual state is ON")
+ else:
+ android_device.log.error(
+ "Failed to turn Bluetooth on. Setting default advertisements to 1"
+ )
+ advertisement_count = -1
+ return advertisement_count
+ advertise_callback_list = []
+ advertise_data = android_device.droid.bleBuildAdvertiseData()
+ advertise_settings = android_device.droid.bleBuildAdvertiseSettings()
+ while (True):
+ advertise_callback = android_device.droid.bleGenBleAdvertiseCallback()
+ advertise_callback_list.append(advertise_callback)
+
+ android_device.droid.bleStartBleAdvertising(advertise_callback,
+ advertise_data,
+ advertise_settings)
+
+ regex = "(" + adv_succ.format(
+ advertise_callback) + "|" + adv_fail.format(
+ advertise_callback) + ")"
+ # wait for either success or failure event
+ evt = android_device.ed.pop_events(regex, bt_default_timeout,
+ small_timeout)
+ if evt[0]["name"] == adv_succ.format(advertise_callback):
+ advertisement_count += 1
+ android_device.log.info(
+ "Advertisement {} started.".format(advertisement_count))
+ else:
+ error = evt[0]["data"]["Error"]
+ if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS":
+ android_device.log.info(
+ "Advertisement failed to start. Reached max " +
+ "advertisements at {}".format(advertisement_count))
+ break
+ else:
+ raise BtTestUtilsError(
+ "Expected ADVERTISE_FAILED_TOO_MANY_ADVERTISERS," +
+ " but received bad error code {}".format(error))
+ try:
+ for adv in advertise_callback_list:
+ android_device.droid.bleStopBleAdvertising(adv)
+ except Exception:
+ android_device.log.error(
+ "Failed to stop advertisingment, resetting Bluetooth.")
+ reset_bluetooth([android_device])
+ return advertisement_count
+
+
+def disable_bluetooth(droid):
+ """Disable Bluetooth on input Droid object.
+
+ Args:
+ droid: The droid object to disable Bluetooth on.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ if droid.bluetoothCheckState() is True:
+ droid.bluetoothToggleState(False)
+ if droid.bluetoothCheckState() is True:
+ log.error("Failed to toggle Bluetooth off.")
+ return False
+ return True
+
+
+def disconnect_pri_from_sec(pri_ad, sec_ad, profiles_list):
+ """
+ Disconnect primary from secondary on a specific set of profiles
+ Args:
+ pri_ad - Primary android_device initiating disconnection
+ sec_ad - Secondary android droid (sl4a interface to keep the
+ method signature the same connect_pri_to_sec above)
+ profiles_list - List of profiles we want to disconnect from
+
+ Returns:
+ True on Success
+ False on Failure
+ """
+ # Sanity check to see if all the profiles in the given set is supported
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_list:
+ if profile not in supported_profiles:
+ pri_ad.log.info("Profile {} is not in supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
+ # Disconnecting on a already disconnected profile is a nop,
+ # so not checking for the connection state
+ try:
+ pri_ad.droid.bluetoothDisconnectConnectedProfile(
+ sec_ad.droid.bluetoothGetLocalAddress(), profiles_list)
+ except Exception as err:
+ pri_ad.log.error(
+ "Exception while trying to disconnect profile(s) {}: {}".format(
+ profiles_list, err))
+ return False
+
+ profile_disconnected = set()
+ pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
+
+ while not profile_disconnected.issuperset(profiles_list):
+ try:
+ profile_event = pri_ad.ed.pop_event(
+ bluetooth_profile_connection_state_changed, bt_default_timeout)
+ pri_ad.log.info("Got event {}".format(profile_event))
+ except Exception as e:
+ pri_ad.log.error(
+ "Did not disconnect from Profiles. Reason {}".format(e))
+ return False
+
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+
+ if state == bt_profile_states['disconnected'] and \
+ device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+ profile_disconnected.add(profile)
+ pri_ad.log.info(
+ "Profiles disconnected so far {}".format(profile_disconnected))
+
+ return True
+
+
+def enable_bluetooth(droid, ed):
+ if droid.bluetoothCheckState() is True:
+ return True
+
+ droid.bluetoothToggleState(True)
+ expected_bluetooth_on_event_name = bluetooth_on
+ try:
+ ed.pop_event(expected_bluetooth_on_event_name, bt_default_timeout)
+ except Exception:
+ log.info("Failed to toggle Bluetooth on (no broadcast received)")
+ if droid.bluetoothCheckState() is True:
+ log.info(".. actual state is ON")
+ return True
+ log.info(".. actual state is OFF")
+ return False
+
+ return True
+
+
+def factory_reset_bluetooth(android_devices):
+ """Clears Bluetooth stack of input Android device list.
+
+ Args:
+ android_devices: The Android device list to reset Bluetooth
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ for a in android_devices:
+ droid, ed = a.droid, a.ed
+ a.log.info("Reset state of bluetooth on device.")
+ if not bluetooth_enabled_check(a):
+ return False
+ # TODO: remove device unbond b/79418045
+ # Temporary solution to ensure all devices are unbonded
+ bonded_devices = droid.bluetoothGetBondedDevices()
+ for b in bonded_devices:
+ a.log.info("Removing bond for device {}".format(b['address']))
+ droid.bluetoothUnbond(b['address'])
+
+ droid.bluetoothFactoryReset()
+ wait_for_bluetooth_manager_state(droid)
+ if not enable_bluetooth(droid, ed):
+ return False
+ return True
+
+
+def generate_ble_advertise_objects(droid):
+ """Generate generic LE advertise objects.
+
+ Args:
+ droid: The droid object to generate advertise LE objects from.
+
+ Returns:
+ advertise_callback: The generated advertise callback id.
+ advertise_data: The generated advertise data id.
+ advertise_settings: The generated advertise settings id.
+ """
+ advertise_callback = droid.bleGenBleAdvertiseCallback()
+ advertise_data = droid.bleBuildAdvertiseData()
+ advertise_settings = droid.bleBuildAdvertiseSettings()
+ return advertise_callback, advertise_data, advertise_settings
+
+
+def generate_ble_scan_objects(droid):
+ """Generate generic LE scan objects.
+
+ Args:
+ droid: The droid object to generate LE scan objects from.
+
+ Returns:
+ filter_list: The generated scan filter list id.
+ scan_settings: The generated scan settings id.
+ scan_callback: The generated scan callback id.
+ """
+ filter_list = droid.bleGenFilterList()
+ scan_settings = droid.bleBuildScanSetting()
+ scan_callback = droid.bleGenScanCallback()
+ return filter_list, scan_settings, scan_callback
+
+
+def generate_id_by_size(size,
+ chars=(string.ascii_lowercase +
+ string.ascii_uppercase + string.digits)):
+ """Generate random ascii characters of input size and input char types
+
+ Args:
+ size: Input size of string.
+ chars: (Optional) Chars to use in generating a random string.
+
+ Returns:
+ String of random input chars at the input size.
+ """
+ return ''.join(random.choice(chars) for _ in range(size))
+
+
+def get_advanced_droid_list(android_devices):
+ """Add max_advertisement and batch_scan_supported attributes to input
+ Android devices
+
+ This will programatically determine maximum LE advertisements of each
+ input Android device.
+
+ Args:
+ android_devices: The Android devices to setup.
+
+ Returns:
+ List of Android devices with new attribtues.
+ """
+ droid_list = []
+ for a in android_devices:
+ d, e = a.droid, a.ed
+ model = d.getBuildModel()
+ max_advertisements = 1
+ batch_scan_supported = True
+ if model in advertisements_to_devices.keys():
+ max_advertisements = advertisements_to_devices[model]
+ else:
+ max_advertisements = determine_max_advertisements(a)
+ max_tries = 3
+ # Retry to calculate max advertisements
+ while max_advertisements == -1 and max_tries > 0:
+ a.log.info(
+ "Attempts left to determine max advertisements: {}".format(
+ max_tries))
+ max_advertisements = determine_max_advertisements(a)
+ max_tries -= 1
+ advertisements_to_devices[model] = max_advertisements
+ if model in batch_scan_not_supported_list:
+ batch_scan_supported = False
+ role = {
+ 'droid': d,
+ 'ed': e,
+ 'max_advertisements': max_advertisements,
+ 'batch_scan_supported': batch_scan_supported
+ }
+ droid_list.append(role)
+ return droid_list
+
+
+def get_bluetooth_crash_count(android_device):
+ out = android_device.adb.shell("dumpsys bluetooth_manager")
+ return int(re.search("crashed(.*\d)", out).group(1))
+
+
+def read_otp(ad):
+ """Reads and parses the OTP output to return TX power backoff
+
+ Reads the OTP registers from the phone, parses them to return a
+ dict of TX power backoffs for different power levels
+
+ Args:
+ ad : android device object
+
+ Returns :
+ otp_dict : power backoff dict
+ """
+
+ ad.adb.shell('svc bluetooth disable')
+ time.sleep(2)
+ otp_output = ad.adb.shell('bluetooth_sar_test -r')
+ ad.adb.shell('svc bluetooth enable')
+ time.sleep(2)
+ otp_dict = {
+ "BR": {
+ "10": 0,
+ "9": 0,
+ "8": 0
+ },
+ "EDR": {
+ "10": 0,
+ "9": 0,
+ "8": 0
+ },
+ "BLE": {
+ "10": 0,
+ "9": 0,
+ "8": 0
+ }
+ }
+
+ otp_regex = '\s+\[\s+PL10:\s+(\d+)\s+PL9:\s+(\d+)*\s+PL8:\s+(\d+)\s+\]'
+
+ for key in otp_dict:
+ bank_list = re.findall("{}{}".format(key, otp_regex), otp_output)
+ for bank_tuple in bank_list:
+ if ('0', '0', '0') != bank_tuple:
+ [otp_dict[key]["10"], otp_dict[key]["9"],
+ otp_dict[key]["8"]] = bank_tuple
+ return otp_dict
+
+
+def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True):
+ """ Function to get the bt metric from logcat.
+
+ Captures logcat for the specified duration and returns the bqr results.
+ Takes list of android objects as input. If a single android object is given,
+ converts it into a list.
+
+ Args:
+ ad_list: list of android_device objects
+ duration: time duration (seconds) for which the logcat is parsed.
+ tag: tag to be appended to the logcat dump.
+ processed: flag to process bqr output.
+
+ Returns:
+ metrics_dict: dict of metrics for each android device.
+ """
+
+ # Defining bqr quantitites and their regex to extract
+ regex_dict = {
+ "vsp_txpl": "VSP_TxPL:\s(\S+)",
+ "pwlv": "PwLv:\s(\S+)",
+ "rssi": "RSSI:\s[-](\d+)"
+ }
+ metrics_dict = {"rssi": {}, "pwlv": {}, "vsp_txpl": {}}
+
+ # Converting a single android device object to list
+ if not isinstance(ad_list, list):
+ ad_list = [ad_list]
+
+ #Time sync with the test machine
+ for ad in ad_list:
+ ad.droid.setTime(int(round(time.time() * 1000)))
+ time.sleep(0.5)
+
+ begin_time = utils.get_current_epoch_time()
+ time.sleep(duration)
+ end_time = utils.get_current_epoch_time()
+
+ for ad in ad_list:
+ bt_rssi_log = ad.cat_adb_log(tag, begin_time, end_time)
+ bqr_tag = "Handle:"
+
+ # Extracting supporting bqr quantities
+ for metric, regex in regex_dict.items():
+ bqr_metric = []
+ file_bt_log = open(bt_rssi_log, "r")
+ for line in file_bt_log:
+ if bqr_tag in line:
+ if re.findall(regex, line):
+ m = re.findall(regex, line)[0].strip(",")
+ bqr_metric.append(m)
+ metrics_dict[metric][ad.serial] = bqr_metric
+
+ # Ensures back-compatibility for vsp_txpl enabled DUTs
+ if metrics_dict["vsp_txpl"][ad.serial]:
+ metrics_dict["pwlv"][ad.serial] = metrics_dict["vsp_txpl"][
+ ad.serial]
+
+ # Formatting the raw data
+ metrics_dict["rssi"][ad.serial] = [
+ (-1) * int(x) for x in metrics_dict["rssi"][ad.serial]
+ ]
+ metrics_dict["pwlv"][ad.serial] = [
+ int(x, 16) for x in metrics_dict["pwlv"][ad.serial]
+ ]
+
+ # Processing formatted data if processing is required
+ if processed:
+ # Computes the average RSSI
+ metrics_dict["rssi"][ad.serial] = round(
+ sum(metrics_dict["rssi"][ad.serial]) /
+ len(metrics_dict["rssi"][ad.serial]), 2)
+ # Returns last noted value for power level
+ metrics_dict["pwlv"][ad.serial] = float(
+ sum(metrics_dict["pwlv"][ad.serial]) /
+ len(metrics_dict["pwlv"][ad.serial]))
+
+ return metrics_dict
+
+
+def get_bt_rssi(ad, duration=1, processed=True):
+ """Function to get average bt rssi from logcat.
+
+ This function returns the average RSSI for the given duration. RSSI values are
+ extracted from BQR.
+
+ Args:
+ ad: (list of) android_device object.
+ duration: time duration(seconds) for which logcat is parsed.
+
+ Returns:
+ avg_rssi: average RSSI on each android device for the given duration.
+ """
+ function_tag = "get_bt_rssi"
+ bqr_results = get_bt_metric(ad,
+ duration,
+ tag=function_tag,
+ processed=processed)
+ return bqr_results["rssi"]
+
+
+def enable_bqr(
+ ad_list,
+ bqr_interval=10,
+ bqr_event_mask=15,
+):
+ """Sets up BQR reporting.
+
+ Sets up BQR to report BT metrics at the requested frequency and toggles
+ airplane mode for the bqr settings to take effect.
+
+ Args:
+ ad_list: an android_device or list of android devices.
+ """
+ # Converting a single android device object to list
+ if not isinstance(ad_list, list):
+ ad_list = [ad_list]
+
+ for ad in ad_list:
+ #Setting BQR parameters
+ ad.adb.shell("setprop persist.bluetooth.bqr.event_mask {}".format(
+ bqr_event_mask))
+ ad.adb.shell("setprop persist.bluetooth.bqr.min_interval_ms {}".format(
+ bqr_interval))
+
+ ## Toggle airplane mode
+ ad.droid.connectivityToggleAirplaneMode(True)
+ ad.droid.connectivityToggleAirplaneMode(False)
+
+
+def disable_bqr(ad_list):
+ """Disables BQR reporting.
+
+ Args:
+ ad_list: an android_device or list of android devices.
+ """
+ # Converting a single android device object to list
+ if not isinstance(ad_list, list):
+ ad_list = [ad_list]
+
+ DISABLE_BQR_MASK = 0
+
+ for ad in ad_list:
+ #Disabling BQR
+ ad.adb.shell("setprop persist.bluetooth.bqr.event_mask {}".format(
+ DISABLE_BQR_MASK))
+
+ ## Toggle airplane mode
+ ad.droid.connectivityToggleAirplaneMode(True)
+ ad.droid.connectivityToggleAirplaneMode(False)
+
+
+def get_device_selector_dictionary(android_device_list):
+ """Create a dictionary of Bluetooth features vs Android devices.
+
+ Args:
+ android_device_list: The list of Android devices.
+ Returns:
+ A dictionary of profiles/features to Android devices.
+ """
+ selector_dict = {}
+ for ad in android_device_list:
+ uuids = ad.droid.bluetoothGetLocalUuids()
+
+ for profile, uuid_const in sig_uuid_constants.items():
+ uuid_check = sig_uuid_constants['BASE_UUID'].format(
+ uuid_const).lower()
+ if uuids and uuid_check in uuids:
+ if profile in selector_dict:
+ selector_dict[profile].append(ad)
+ else:
+ selector_dict[profile] = [ad]
+
+ # Various services may not be active during BT startup.
+ # If the device can be identified through adb shell pm list features
+ # then try to add them to the appropriate profiles / features.
+
+ # Android TV.
+ if "feature:com.google.android.tv.installed" in ad.features:
+ ad.log.info("Android TV device found.")
+ supported_profiles = ['AudioSink']
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+
+ # Android Auto
+ elif "feature:android.hardware.type.automotive" in ad.features:
+ ad.log.info("Android Auto device found.")
+ # Add: AudioSink , A/V_RemoteControl,
+ supported_profiles = [
+ 'AudioSink', 'A/V_RemoteControl', 'Message Notification Server'
+ ]
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ # Android Wear
+ elif "feature:android.hardware.type.watch" in ad.features:
+ ad.log.info("Android Wear device found.")
+ supported_profiles = []
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ # Android Phone
+ elif "feature:android.hardware.telephony" in ad.features:
+ ad.log.info("Android Phone device found.")
+ # Add: AudioSink
+ supported_profiles = [
+ 'AudioSource', 'A/V_RemoteControlTarget',
+ 'Message Access Server'
+ ]
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ return selector_dict
+
+
+def get_mac_address_of_generic_advertisement(scan_ad, adv_ad):
+ """Start generic advertisement and get it's mac address by LE scanning.
+
+ Args:
+ scan_ad: The Android device to use as the scanner.
+ adv_ad: The Android device to use as the advertiser.
+
+ Returns:
+ mac_address: The mac address of the advertisement.
+ advertise_callback: The advertise callback id of the active
+ advertisement.
+ """
+ adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
+ adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ adv_ad.droid.bleSetAdvertiseSettingsTxPowerLevel(
+ ble_advertise_settings_tx_powers['high'])
+ advertise_callback, advertise_data, advertise_settings = (
+ generate_ble_advertise_objects(adv_ad.droid))
+ adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
+ advertise_settings)
+ try:
+ adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+ bt_default_timeout)
+ except Empty as err:
+ raise BtTestUtilsError(
+ "Advertiser did not start successfully {}".format(err))
+ filter_list = scan_ad.droid.bleGenFilterList()
+ scan_settings = scan_ad.droid.bleBuildScanSetting()
+ scan_callback = scan_ad.droid.bleGenScanCallback()
+ scan_ad.droid.bleSetScanFilterDeviceName(
+ adv_ad.droid.bluetoothGetLocalName())
+ scan_ad.droid.bleBuildScanFilter(filter_list)
+ scan_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+ try:
+ event = scan_ad.ed.pop_event(
+ "BleScan{}onScanResults".format(scan_callback), bt_default_timeout)
+ except Empty as err:
+ raise BtTestUtilsError(
+ "Scanner did not find advertisement {}".format(err))
+ mac_address = event['data']['Result']['deviceInfo']['address']
+ return mac_address, advertise_callback, scan_callback
+
+
+def hid_device_send_key_data_report(host_id, device_ad, key, interval=1):
+ """Send a HID report simulating a 1-second keyboard press from host_ad to
+ device_ad
+
+ Args:
+ host_id: the Bluetooth MAC address or name of the HID host
+ device_ad: HID device
+ key: the key we want to send
+ interval: the interval between key press and key release
+ """
+ device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
+ hid_keyboard_report(key))
+ time.sleep(interval)
+ device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
+ hid_keyboard_report("00"))
+
+
+def hid_keyboard_report(key, modifier="00"):
+ """Get the HID keyboard report for the given key
+
+ Args:
+ key: the key we want
+ modifier: HID keyboard modifier bytes
+ Returns:
+ The byte array for the HID report.
+ """
+ return str(
+ bytearray.fromhex(" ".join(
+ [modifier, "00", key, "00", "00", "00", "00", "00"])), "utf-8")
+
+
+def is_a2dp_connected(sink, source):
+ """
+ Convenience Function to see if the 2 devices are connected on
+ A2dp.
+ Args:
+ sink: Audio Sink
+ source: Audio Source
+ Returns:
+ True if Connected
+ False if Not connected
+ """
+
+ devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
+ for device in devices:
+ sink.log.info("A2dp Connected device {}".format(device["name"]))
+ if (device["address"] == source.droid.bluetoothGetLocalAddress()):
+ return True
+ return False
+
+
+def is_a2dp_snk_device_connected(ad, addr):
+ """Determines if an AndroidDevice has A2DP snk connectivity to input address
+
+ Args:
+ ad: the Android device
+ addr: the address that's expected
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ devices = ad.droid.bluetoothA2dpSinkGetConnectedDevices()
+ ad.log.info("Connected A2DP Sink devices: {}".format(devices))
+ if addr in {d['address'] for d in devices}:
+ return True
+ return False
+
+
+def is_a2dp_src_device_connected(ad, addr):
+ """Determines if an AndroidDevice has A2DP connectivity to input address
+
+ Args:
+ ad: the Android device
+ addr: the address that's expected
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ devices = ad.droid.bluetoothA2dpGetConnectedDevices()
+ ad.log.info("Connected A2DP Source devices: {}".format(devices))
+ if addr in {d['address'] for d in devices}:
+ return True
+ return False
+
+
+def is_hfp_client_device_connected(ad, addr):
+ """Determines if an AndroidDevice has HFP connectivity to input address
+
+ Args:
+ ad: the Android device
+ addr: the address that's expected
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ devices = ad.droid.bluetoothHfpClientGetConnectedDevices()
+ ad.log.info("Connected HFP Client devices: {}".format(devices))
+ if addr in {d['address'] for d in devices}:
+ return True
+ return False
+
+
+def is_map_mce_device_connected(ad, addr):
+ """Determines if an AndroidDevice has MAP MCE connectivity to input address
+
+ Args:
+ ad: the Android device
+ addr: the address that's expected
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ devices = ad.droid.bluetoothMapClientGetConnectedDevices()
+ ad.log.info("Connected MAP MCE devices: {}".format(devices))
+ if addr in {d['address'] for d in devices}:
+ return True
+ return False
+
+
+def is_map_mse_device_connected(ad, addr):
+ """Determines if an AndroidDevice has MAP MSE connectivity to input address
+
+ Args:
+ ad: the Android device
+ addr: the address that's expected
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ devices = ad.droid.bluetoothMapGetConnectedDevices()
+ ad.log.info("Connected MAP MSE devices: {}".format(devices))
+ if addr in {d['address'] for d in devices}:
+ return True
+ return False
+
+
+def kill_bluetooth_process(ad):
+ """Kill Bluetooth process on Android device.
+
+ Args:
+ ad: Android device to kill BT process on.
+ """
+ ad.log.info("Killing Bluetooth process.")
+ pid = ad.adb.shell(
+ "ps | grep com.android.bluetooth | awk '{print $2}'").decode('ascii')
+ call(["adb -s " + ad.serial + " shell kill " + pid], shell=True)
+
+
+def log_energy_info(android_devices, state):
+ """Logs energy info of input Android devices.
+
+ Args:
+ android_devices: input Android device list to log energy info from.
+ state: the input state to log. Usually 'Start' or 'Stop' for logging.
+
+ Returns:
+ A logging string of the Bluetooth energy info reported.
+ """
+ return_string = "{} Energy info collection:\n".format(state)
+ # Bug: b/31966929
+ return return_string
+
+
+def orchestrate_and_verify_pan_connection(pan_dut, panu_dut):
+ """Setups up a PAN conenction between two android devices.
+
+ Args:
+ pan_dut: the Android device providing tethering services
+ panu_dut: the Android device using the internet connection from the
+ pan_dut
+ Returns:
+ True if PAN connection and verification is successful,
+ false if unsuccessful.
+ """
+ if not toggle_airplane_mode_by_adb(log, panu_dut, True):
+ panu_dut.log.error("Failed to toggle airplane mode on")
+ return False
+ if not toggle_airplane_mode_by_adb(log, panu_dut, False):
+ pan_dut.log.error("Failed to toggle airplane mode off")
+ return False
+ pan_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
+ panu_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
+ if not bluetooth_enabled_check(panu_dut):
+ return False
+ if not bluetooth_enabled_check(pan_dut):
+ return False
+ pan_dut.droid.bluetoothPanSetBluetoothTethering(True)
+ if not (pair_pri_to_sec(pan_dut, panu_dut)):
+ return False
+ if not pan_dut.droid.bluetoothPanIsTetheringOn():
+ pan_dut.log.error("Failed to enable Bluetooth tethering.")
+ return False
+ # Magic sleep needed to give the stack time in between bonding and
+ # connecting the PAN profile.
+ time.sleep(pan_connect_timeout)
+ panu_dut.droid.bluetoothConnectBonded(
+ pan_dut.droid.bluetoothGetLocalAddress())
+ if not verify_http_connection(log, panu_dut):
+ panu_dut.log.error("Can't verify http connection on PANU device.")
+ if not verify_http_connection(log, pan_dut):
+ pan_dut.log.info(
+ "Can't verify http connection on PAN service device")
+ return False
+ return True
+
+
+def orchestrate_bluetooth_socket_connection(
+ client_ad,
+ server_ad,
+ accept_timeout_ms=default_bluetooth_socket_timeout_ms,
+ uuid=None):
+ """Sets up the Bluetooth Socket connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ server_ad.droid.bluetoothStartPairingHelper()
+ client_ad.droid.bluetoothStartPairingHelper()
+
+ server_ad.droid.bluetoothSocketConnBeginAcceptThreadUuid(
+ (bluetooth_socket_conn_test_uuid if uuid is None else uuid),
+ accept_timeout_ms)
+ client_ad.droid.bluetoothSocketConnBeginConnectThreadUuid(
+ server_ad.droid.bluetoothGetLocalAddress(),
+ (bluetooth_socket_conn_test_uuid if uuid is None else uuid))
+
+ end_time = time.time() + bt_default_timeout
+ result = False
+ test_result = True
+ while time.time() < end_time:
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ test_result = True
+ client_ad.log.info("Bluetooth socket Client Connection Active")
+ break
+ else:
+ test_result = False
+ time.sleep(1)
+ if not test_result:
+ client_ad.log.error(
+ "Failed to establish a Bluetooth socket connection")
+ return False
+ return True
+
+
+def orchestrate_rfcomm_connection(client_ad,
+ server_ad,
+ accept_timeout_ms=default_rfcomm_timeout_ms,
+ uuid=None):
+ """Sets up the RFCOMM connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
+ result = orchestrate_bluetooth_socket_connection(
+ client_ad, server_ad, accept_timeout_ms,
+ (bt_rfcomm_uuids['default_uuid'] if uuid is None else uuid))
+
+ return result
+
+
+def pair_pri_to_sec(pri_ad, sec_ad, attempts=2, auto_confirm=True):
+ """Pairs pri droid to secondary droid.
+
+ Args:
+ pri_ad: Android device initiating connection
+ sec_ad: Android device accepting connection
+ attempts: Number of attempts to try until failure.
+ auto_confirm: Auto confirm passkey match for both devices
+
+ Returns:
+ Pass if True
+ Fail if False
+ """
+ pri_ad.droid.bluetoothStartConnectionStateChangeMonitor(
+ sec_ad.droid.bluetoothGetLocalAddress())
+ curr_attempts = 0
+ while curr_attempts < attempts:
+ if _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
+ return True
+ # Wait 2 seconds before unbound
+ time.sleep(2)
+ if not clear_bonded_devices(pri_ad):
+ log.error(
+ "Failed to clear bond for primary device at attempt {}".format(
+ str(curr_attempts)))
+ return False
+ if not clear_bonded_devices(sec_ad):
+ log.error(
+ "Failed to clear bond for secondary device at attempt {}".
+ format(str(curr_attempts)))
+ return False
+ # Wait 2 seconds after unbound
+ time.sleep(2)
+ curr_attempts += 1
+ log.error("pair_pri_to_sec failed to connect after {} attempts".format(
+ str(attempts)))
+ return False
+
+
+def _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
+ # Enable discovery on sec_ad so that pri_ad can find it.
+ # The timeout here is based on how much time it would take for two devices
+ # to pair with each other once pri_ad starts seeing devices.
+ pri_droid = pri_ad.droid
+ sec_droid = sec_ad.droid
+ pri_ad.ed.clear_all_events()
+ sec_ad.ed.clear_all_events()
+ log.info("Bonding device {} to {}".format(
+ pri_droid.bluetoothGetLocalAddress(),
+ sec_droid.bluetoothGetLocalAddress()))
+ sec_droid.bluetoothMakeDiscoverable(bt_default_timeout)
+ target_address = sec_droid.bluetoothGetLocalAddress()
+ log.debug("Starting paring helper on each device")
+ pri_droid.bluetoothStartPairingHelper(auto_confirm)
+ sec_droid.bluetoothStartPairingHelper(auto_confirm)
+ pri_ad.log.info("Primary device starting discovery and executing bond")
+ result = pri_droid.bluetoothDiscoverAndBond(target_address)
+ if not auto_confirm:
+ if not _wait_for_passkey_match(pri_ad, sec_ad):
+ return False
+ # Loop until we have bonded successfully or timeout.
+ end_time = time.time() + bt_default_timeout
+ pri_ad.log.info("Verifying devices are bonded")
+ while time.time() < end_time:
+ bonded_devices = pri_droid.bluetoothGetBondedDevices()
+ bonded = False
+ for d in bonded_devices:
+ if d['address'] == target_address:
+ pri_ad.log.info("Successfully bonded to device")
+ return True
+ time.sleep(0.1)
+ # Timed out trying to bond.
+ pri_ad.log.info("Failed to bond devices.")
+ return False
+
+
+def reset_bluetooth(android_devices):
+ """Resets Bluetooth state of input Android device list.
+
+ Args:
+ android_devices: The Android device list to reset Bluetooth state on.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ for a in android_devices:
+ droid, ed = a.droid, a.ed
+ a.log.info("Reset state of bluetooth on device.")
+ if droid.bluetoothCheckState() is True:
+ droid.bluetoothToggleState(False)
+ expected_bluetooth_off_event_name = bluetooth_off
+ try:
+ ed.pop_event(expected_bluetooth_off_event_name,
+ bt_default_timeout)
+ except Exception:
+ a.log.error("Failed to toggle Bluetooth off.")
+ return False
+ # temp sleep for b/17723234
+ time.sleep(3)
+ if not bluetooth_enabled_check(a):
+ return False
+ return True
+
+
+def scan_and_verify_n_advertisements(scn_ad, max_advertisements):
+ """Verify that input number of advertisements can be found from the scanning
+ Android device.
+
+ Args:
+ scn_ad: The Android device to start LE scanning on.
+ max_advertisements: The number of advertisements the scanner expects to
+ find.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ test_result = False
+ address_list = []
+ filter_list = scn_ad.droid.bleGenFilterList()
+ scn_ad.droid.bleBuildScanFilter(filter_list)
+ scan_settings = scn_ad.droid.bleBuildScanSetting()
+ scan_callback = scn_ad.droid.bleGenScanCallback()
+ scn_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+ start_time = time.time()
+ while (start_time + bt_default_timeout) > time.time():
+ event = None
+ try:
+ event = scn_ad.ed.pop_event(scan_result.format(scan_callback),
+ bt_default_timeout)
+ except Empty as error:
+ raise BtTestUtilsError(
+ "Failed to find scan event: {}".format(error))
+ address = event['data']['Result']['deviceInfo']['address']
+ if address not in address_list:
+ address_list.append(address)
+ if len(address_list) == max_advertisements:
+ test_result = True
+ break
+ scn_ad.droid.bleStopBleScan(scan_callback)
+ return test_result
+
+
+def set_bluetooth_codec(android_device,
+ codec_type,
+ sample_rate,
+ bits_per_sample,
+ channel_mode,
+ codec_specific_1=0):
+ """Sets the A2DP codec configuration on the AndroidDevice.
+
+ Args:
+ android_device (acts.controllers.android_device.AndroidDevice): the
+ android device for which to switch the codec.
+ codec_type (str): the desired codec type. Must be a key in
+ bt_constants.codec_types.
+ sample_rate (str): the desired sample rate. Must be a key in
+ bt_constants.sample_rates.
+ bits_per_sample (str): the desired bits per sample. Must be a key in
+ bt_constants.bits_per_samples.
+ channel_mode (str): the desired channel mode. Must be a key in
+ bt_constants.channel_modes.
+ codec_specific_1 (int): the desired bit rate (quality) for LDAC codec.
+ Returns:
+ bool: True if the codec config was successfully changed to the desired
+ values. Else False.
+ """
+ message = ("Set Android Device A2DP Bluetooth codec configuration:\n"
+ "\tCodec: {codec_type}\n"
+ "\tSample Rate: {sample_rate}\n"
+ "\tBits per Sample: {bits_per_sample}\n"
+ "\tChannel Mode: {channel_mode}".format(
+ codec_type=codec_type,
+ sample_rate=sample_rate,
+ bits_per_sample=bits_per_sample,
+ channel_mode=channel_mode))
+ android_device.log.info(message)
+
+ # Send SL4A command
+ droid, ed = android_device.droid, android_device.ed
+ if not droid.bluetoothA2dpSetCodecConfigPreference(
+ codec_types[codec_type], sample_rates[str(sample_rate)],
+ bits_per_samples[str(bits_per_sample)],
+ channel_modes[channel_mode], codec_specific_1):
+ android_device.log.warning(
+ "SL4A command returned False. Codec was not "
+ "changed.")
+ else:
+ try:
+ ed.pop_event(bluetooth_a2dp_codec_config_changed,
+ bt_default_timeout)
+ except Exception:
+ android_device.log.warning("SL4A event not registered. Codec "
+ "may not have been changed.")
+
+ # Validate codec value through ADB
+ # TODO (aidanhb): validate codec more robustly using SL4A
+ command = "dumpsys bluetooth_manager | grep -i 'current codec'"
+ out = android_device.adb.shell(command)
+ split_out = out.split(": ")
+ if len(split_out) != 2:
+ android_device.log.warning("Could not verify codec config change "
+ "through ADB.")
+ elif split_out[1].strip().upper() != codec_type:
+ android_device.log.error("Codec config was not changed.\n"
+ "\tExpected codec: {exp}\n"
+ "\tActual codec: {act}".format(
+ exp=codec_type, act=split_out[1].strip()))
+ return False
+ android_device.log.info("Bluetooth codec successfully changed.")
+ return True
+
+
+def set_bt_scan_mode(ad, scan_mode_value):
+ """Set Android device's Bluetooth scan mode.
+
+ Args:
+ ad: The Android device to set the scan mode on.
+ scan_mode_value: The value to set the scan mode to.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ droid, ed = ad.droid, ad.ed
+ if scan_mode_value == bt_scan_mode_types['state_off']:
+ disable_bluetooth(droid)
+ scan_mode = droid.bluetoothGetScanMode()
+ reset_bluetooth([ad])
+ if scan_mode != scan_mode_value:
+ return False
+ elif scan_mode_value == bt_scan_mode_types['none']:
+ droid.bluetoothMakeUndiscoverable()
+ scan_mode = droid.bluetoothGetScanMode()
+ if scan_mode != scan_mode_value:
+ return False
+ elif scan_mode_value == bt_scan_mode_types['connectable']:
+ droid.bluetoothMakeUndiscoverable()
+ droid.bluetoothMakeConnectable()
+ scan_mode = droid.bluetoothGetScanMode()
+ if scan_mode != scan_mode_value:
+ return False
+ elif (scan_mode_value == bt_scan_mode_types['connectable_discoverable']):
+ droid.bluetoothMakeDiscoverable()
+ scan_mode = droid.bluetoothGetScanMode()
+ if scan_mode != scan_mode_value:
+ return False
+ else:
+ # invalid scan mode
+ return False
+ return True
+
+
+def set_device_name(droid, name):
+ """Set and check Bluetooth local name on input droid object.
+
+ Args:
+ droid: Droid object to set local name on.
+ name: the Bluetooth local name to set.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ droid.bluetoothSetLocalName(name)
+ time.sleep(2)
+ droid_name = droid.bluetoothGetLocalName()
+ if droid_name != name:
+ return False
+ return True
+
+
+def set_profile_priority(host_ad, client_ad, profiles, priority):
+ """Sets the priority of said profile(s) on host_ad for client_ad"""
+ for profile in profiles:
+ host_ad.log.info("Profile {} on {} for {} set to priority {}".format(
+ profile, host_ad.droid.bluetoothGetLocalName(),
+ client_ad.droid.bluetoothGetLocalAddress(), priority.value))
+ if bt_profile_constants['a2dp_sink'] == profile:
+ host_ad.droid.bluetoothA2dpSinkSetPriority(
+ client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+ elif bt_profile_constants['headset_client'] == profile:
+ host_ad.droid.bluetoothHfpClientSetPriority(
+ client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+ elif bt_profile_constants['pbap_client'] == profile:
+ host_ad.droid.bluetoothPbapClientSetPriority(
+ client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+ else:
+ host_ad.log.error(
+ "Profile {} not yet supported for priority settings".format(
+ profile))
+
+
+def setup_multiple_devices_for_bt_test(android_devices):
+ """A common setup routine for Bluetooth on input Android device list.
+
+ Things this function sets up:
+ 1. Resets Bluetooth
+ 2. Set Bluetooth local name to random string of size 4
+ 3. Disable BLE background scanning.
+ 4. Enable Bluetooth snoop logging.
+
+ Args:
+ android_devices: Android device list to setup Bluetooth on.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ log.info("Setting up Android Devices")
+ # TODO: Temp fix for an selinux error.
+ for ad in android_devices:
+ ad.adb.shell("setenforce 0")
+ threads = []
+ try:
+ for a in android_devices:
+ thread = threading.Thread(target=factory_reset_bluetooth,
+ args=([[a]]))
+ threads.append(thread)
+ thread.start()
+ for t in threads:
+ t.join()
+
+ for a in android_devices:
+ d = a.droid
+ # TODO: Create specific RPC command to instantiate
+ # BluetoothConnectionFacade. This is just a workaround.
+ d.bluetoothStartConnectionStateChangeMonitor("")
+ setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
+ if not setup_result:
+ a.log.error("Failed to set device name.")
+ return setup_result
+ d.bluetoothDisableBLE()
+ utils.set_location_service(a, True)
+ bonded_devices = d.bluetoothGetBondedDevices()
+ for b in bonded_devices:
+ a.log.info("Removing bond for device {}".format(b['address']))
+ d.bluetoothUnbond(b['address'])
+ for a in android_devices:
+ a.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
+ getprop_result = a.adb.shell(
+ "getprop persist.bluetooth.btsnooplogmode") == "full"
+ if not getprop_result:
+ a.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
+ except Exception as err:
+ log.error("Something went wrong in multi device setup: {}".format(err))
+ return False
+ return setup_result
+
+
+def setup_n_advertisements(adv_ad, num_advertisements):
+ """Setup input number of advertisements on input Android device.
+
+ Args:
+ adv_ad: The Android device to start LE advertisements on.
+ num_advertisements: The number of advertisements to start.
+
+ Returns:
+ advertise_callback_list: List of advertisement callback ids.
+ """
+ adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ advertise_data = adv_ad.droid.bleBuildAdvertiseData()
+ advertise_settings = adv_ad.droid.bleBuildAdvertiseSettings()
+ advertise_callback_list = []
+ for i in range(num_advertisements):
+ advertise_callback = adv_ad.droid.bleGenBleAdvertiseCallback()
+ advertise_callback_list.append(advertise_callback)
+ adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
+ advertise_settings)
+ try:
+ adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+ bt_default_timeout)
+ adv_ad.log.info("Advertisement {} started.".format(i + 1))
+ except Empty as error:
+ adv_ad.log.error("Advertisement {} failed to start.".format(i + 1))
+ raise BtTestUtilsError(
+ "Test failed with Empty error: {}".format(error))
+ return advertise_callback_list
+
+
+def take_btsnoop_log(ad, testcase, testname):
+ """Grabs the btsnoop_hci log on a device and stores it in the log directory
+ of the test class.
+
+ If you want grab the btsnoop_hci log, call this function with android_device
+ objects in on_fail. Bug report takes a relative long time to take, so use
+ this cautiously.
+
+ Args:
+ ad: The android_device instance to take bugreport on.
+ testcase: Name of the test calss that triggered this snoop log.
+ testname: Name of the test case that triggered this bug report.
+ """
+ testname = "".join(x for x in testname if x.isalnum())
+ serial = ad.serial
+ device_model = ad.droid.getBuildModel()
+ device_model = device_model.replace(" ", "")
+ out_name = ','.join((testname, device_model, serial))
+ snoop_path = os.path.join(ad.device_log_path, 'BluetoothSnoopLogs')
+ os.makedirs(snoop_path, exist_ok=True)
+ cmd = ''.join(("adb -s ", serial, " pull ", btsnoop_log_path_on_device,
+ " ", snoop_path + '/' + out_name, ".btsnoop_hci.log"))
+ exe_cmd(cmd)
+ try:
+ cmd = ''.join(
+ ("adb -s ", serial, " pull ", btsnoop_last_log_path_on_device, " ",
+ snoop_path + '/' + out_name, ".btsnoop_hci.log.last"))
+ exe_cmd(cmd)
+ except Exception as err:
+ testcase.log.info(
+ "File does not exist {}".format(btsnoop_last_log_path_on_device))
+
+
+def take_btsnoop_logs(android_devices, testcase, testname):
+ """Pull btsnoop logs from an input list of android devices.
+
+ Args:
+ android_devices: the list of Android devices to pull btsnoop logs from.
+ testcase: Name of the test calss that triggered this snoop log.
+ testname: Name of the test case that triggered this bug report.
+ """
+ for a in android_devices:
+ take_btsnoop_log(a, testcase, testname)
+
+
+def teardown_n_advertisements(adv_ad, num_advertisements,
+ advertise_callback_list):
+ """Stop input number of advertisements on input Android device.
+
+ Args:
+ adv_ad: The Android device to stop LE advertisements on.
+ num_advertisements: The number of advertisements to stop.
+ advertise_callback_list: The list of advertisement callbacks to stop.
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ for n in range(num_advertisements):
+ adv_ad.droid.bleStopBleAdvertising(advertise_callback_list[n])
+ return True
+
+
+def verify_server_and_client_connected(client_ad, server_ad, log=True):
+ """Verify that input server and client Android devices are connected.
+
+ This code is under the assumption that there will only be
+ a single connection.
+
+ Args:
+ client_ad: the Android device to check number of active connections.
+ server_ad: the Android device to check number of active connections.
+
+ Returns:
+ True both server and client have at least 1 active connection,
+ false if unsuccessful.
+ """
+ test_result = True
+ if len(server_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
+ if log:
+ server_ad.log.error("No socket connections found on server.")
+ test_result = False
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
+ if log:
+ client_ad.log.error("No socket connections found on client.")
+ test_result = False
+ return test_result
+
+
+def wait_for_bluetooth_manager_state(droid,
+ state=None,
+ timeout=10,
+ threshold=5):
+ """ Waits for BlueTooth normalized state or normalized explicit state
+ args:
+ droid: droid device object
+ state: expected BlueTooth state
+ timeout: max timeout threshold
+ threshold: list len of bt state
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ all_states = []
+ get_state = lambda: droid.bluetoothGetLeState()
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ all_states.append(get_state())
+ if len(all_states) >= threshold:
+ # for any normalized state
+ if state is None:
+ if len(set(all_states[-threshold:])) == 1:
+ log.info("State normalized {}".format(
+ set(all_states[-threshold:])))
+ return True
+ else:
+ # explicit check against normalized state
+ if set([state]).issubset(all_states[-threshold:]):
+ return True
+ time.sleep(0.5)
+ log.error(
+ "Bluetooth state fails to normalize" if state is None else
+ "Failed to match bluetooth state, current state {} expected state {}".
+ format(get_state(), state))
+ return False
+
+
+def _wait_for_passkey_match(pri_ad, sec_ad):
+ pri_pin, sec_pin = -1, 1
+ pri_variant, sec_variant = -1, 1
+ pri_pairing_req, sec_pairing_req = None, None
+ try:
+ pri_pairing_req = pri_ad.ed.pop_event(
+ event_name="BluetoothActionPairingRequest",
+ timeout=bt_default_timeout)
+ pri_variant = pri_pairing_req["data"]["PairingVariant"]
+ pri_pin = pri_pairing_req["data"]["Pin"]
+ pri_ad.log.info("Primary device received Pin: {}, Variant: {}".format(
+ pri_pin, pri_variant))
+ sec_pairing_req = sec_ad.ed.pop_event(
+ event_name="BluetoothActionPairingRequest",
+ timeout=bt_default_timeout)
+ sec_variant = sec_pairing_req["data"]["PairingVariant"]
+ sec_pin = sec_pairing_req["data"]["Pin"]
+ sec_ad.log.info(
+ "Secondary device received Pin: {}, Variant: {}".format(
+ sec_pin, sec_variant))
+ except Empty as err:
+ log.error("Wait for pin error: {}".format(err))
+ log.error("Pairing request state, Primary: {}, Secondary: {}".format(
+ pri_pairing_req, sec_pairing_req))
+ return False
+ if pri_variant == sec_variant == pairing_variant_passkey_confirmation:
+ confirmation = pri_pin == sec_pin
+ if confirmation:
+ log.info("Pairing code matched, accepting connection")
+ else:
+ log.info("Pairing code mismatched, rejecting connection")
+ pri_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+ str(confirmation))
+ sec_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+ str(confirmation))
+ if not confirmation:
+ return False
+ elif pri_variant != sec_variant:
+ log.error("Pairing variant mismatched, abort connection")
+ return False
+ return True
+
+
+def write_read_verify_data(client_ad, server_ad, msg, binary=False):
+ """Verify that the client wrote data to the server Android device correctly.
+
+ Args:
+ client_ad: the Android device to perform the write.
+ server_ad: the Android device to read the data written.
+ msg: the message to write.
+ binary: if the msg arg is binary or not.
+
+ Returns:
+ True if the data written matches the data read, false if not.
+ """
+ client_ad.log.info("Write message.")
+ try:
+ if binary:
+ client_ad.droid.bluetoothSocketConnWriteBinary(msg)
+ else:
+ client_ad.droid.bluetoothSocketConnWrite(msg)
+ except Exception as err:
+ client_ad.log.error("Failed to write data: {}".format(err))
+ return False
+ server_ad.log.info("Read message.")
+ try:
+ if binary:
+ read_msg = server_ad.droid.bluetoothSocketConnReadBinary().rstrip(
+ "\r\n")
+ else:
+ read_msg = server_ad.droid.bluetoothSocketConnRead()
+ except Exception as err:
+ server_ad.log.error("Failed to read data: {}".format(err))
+ return False
+ log.info("Verify message.")
+ if msg != read_msg:
+ log.error("Mismatch! Read: {}, Expected: {}".format(read_msg, msg))
+ return False
+ return True
+
+
+class MediaControlOverSl4a(object):
+ """Media control using sl4a facade for general purpose.
+
+ """
+ def __init__(self, android_device, music_file):
+ """Initialize the media_control class.
+
+ Args:
+ android_dut: android_device object
+ music_file: location of the music file
+ """
+ self.android_device = android_device
+ self.music_file = music_file
+
+ def play(self):
+ """Play media.
+
+ """
+ self.android_device.droid.mediaPlayOpen('file://%s' % self.music_file,
+ 'default', True)
+ playing = self.android_device.droid.mediaIsPlaying()
+ asserts.assert_true(playing,
+ 'Failed to play music %s' % self.music_file)
+
+ def pause(self):
+ """Pause media.
+
+ """
+ self.android_device.droid.mediaPlayPause('default')
+ paused = not self.android_device.droid.mediaIsPlaying()
+ asserts.assert_true(paused,
+ 'Failed to pause music %s' % self.music_file)
+
+ def resume(self):
+ """Resume media.
+
+ """
+ self.android_device.droid.mediaPlayStart('default')
+ playing = self.android_device.droid.mediaIsPlaying()
+ asserts.assert_true(playing,
+ 'Failed to play music %s' % self.music_file)
+
+ def stop(self):
+ """Stop media.
+
+ """
+ self.android_device.droid.mediaPlayStop('default')
+ stopped = not self.android_device.droid.mediaIsPlaying()
+ asserts.assert_true(stopped,
+ 'Failed to stop music %s' % self.music_file)
diff --git a/acts_tests/acts_contrib/test_utils/bt/bta_lib.py b/acts_tests/acts_contrib/test_utils/bt/bta_lib.py
new file mode 100644
index 0000000..6c3be78
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/bta_lib.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Bluetooth adapter libraries
+"""
+
+from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
+
+import pprint
+
+
+class BtaLib():
+ def __init__(self, log, dut, target_mac_address=None):
+ self.advertisement_list = []
+ self.dut = dut
+ self.log = log
+ self.target_mac_addr = target_mac_address
+
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
+
+ def set_scan_mode(self, scan_mode):
+ """Set the Scan mode of the Bluetooth Adapter"""
+ set_bt_scan_mode(self.dut, bt_scan_mode_types[scan_mode])
+
+ def set_device_name(self, line):
+ """Set Bluetooth Adapter Name"""
+ self.dut.droid.bluetoothSetLocalName(line)
+
+ def enable(self):
+ """Enable Bluetooth Adapter"""
+ self.dut.droid.bluetoothToggleState(True)
+
+ def disable(self):
+ """Disable Bluetooth Adapter"""
+ self.dut.droid.bluetoothToggleState(False)
+
+ def init_bond(self):
+ """Initiate bond to PTS device"""
+ self.dut.droid.bluetoothDiscoverAndBond(self.target_mac_addr)
+
+ def start_discovery(self):
+ """Start BR/EDR Discovery"""
+ self.dut.droid.bluetoothStartDiscovery()
+
+ def stop_discovery(self):
+ """Stop BR/EDR Discovery"""
+ self.dut.droid.bluetoothCancelDiscovery()
+
+ def get_discovered_devices(self):
+ """Get Discovered Br/EDR Devices"""
+ if self.dut.droid.bluetoothIsDiscovering():
+ self.dut.droid.bluetoothCancelDiscovery()
+ self.log.info(
+ pprint.pformat(self.dut.droid.bluetoothGetDiscoveredDevices()))
+
+ def bond(self):
+ """Bond to PTS device"""
+ self.dut.droid.bluetoothBond(self.target_mac_addr)
+
+ def disconnect(self):
+ """BTA disconnect"""
+ self.dut.droid.bluetoothDisconnectConnected(self.target_mac_addr)
+
+ def unbond(self):
+ """Unbond from PTS device"""
+ self.dut.droid.bluetoothUnbond(self.target_mac_addr)
+
+ def start_pairing_helper(self, line):
+ """Start or stop Bluetooth Pairing Helper"""
+ if line:
+ self.dut.droid.bluetoothStartPairingHelper(bool(line))
+ else:
+ self.dut.droid.bluetoothStartPairingHelper()
+
+ def push_pairing_pin(self, line):
+ """Push pairing pin to the Android Device"""
+ self.dut.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+ line)
+
+ def get_pairing_pin(self):
+ """Get pairing PIN"""
+ self.log.info(
+ self.dut.ed.pop_event("BluetoothActionPairingRequest", 1))
+
+ def fetch_uuids_with_sdp(self):
+ """BTA fetch UUIDS with SDP"""
+ self.log.info(
+ self.dut.droid.bluetoothFetchUuidsWithSdp(self.target_mac_addr))
+
+ def connect_profiles(self):
+ """Connect available profiles"""
+ self.dut.droid.bluetoothConnectBonded(self.target_mac_addr)
+
+ def tts_speak(self):
+ """Open audio channel by speaking characters"""
+ self.dut.droid.ttsSpeak(self.target_mac_addr)
diff --git a/acts_tests/acts_contrib/test_utils/bt/config_lib.py b/acts_tests/acts_contrib/test_utils/bt/config_lib.py
new file mode 100644
index 0000000..2926243
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/config_lib.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Bluetooth Config Pusher
+"""
+
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
+from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
+
+import time
+import os
+
+
+class ConfigLib():
+ bluetooth_config_path = "/system/etc/bluetooth/bt_stack.conf"
+ conf_path = "{}/configs".format(
+ os.path.dirname(os.path.realpath(__file__)))
+ reset_config_path = "{}/bt_stack.conf".format(conf_path)
+ non_bond_config_path = "{}/non_bond_bt_stack.conf".format(conf_path)
+ disable_mitm_config_path = "{}/dis_mitm_bt_stack.conf".format(conf_path)
+
+ def __init__(self, log, dut):
+ self.dut = dut
+ self.log = log
+
+ def _reset_bluetooth(self):
+ self.dut.droid.bluetoothToggleState(False)
+ self.dut.droid.bluetoothToggleState(True)
+
+ def reset(self):
+ self.dut.adb.push("{} {}".format(self.reset_config_path,
+ self.bluetooth_config_path))
+ self._reset_bluetooth()
+
+ def set_nonbond(self):
+ self.dut.adb.push("{} {}".format(self.non_bond_config_path,
+ self.bluetooth_config_path))
+ self._reset_bluetooth()
+
+ def set_disable_mitm(self):
+ self.dut.adb.push("{} {}".format(self.disable_mitm_config_path,
+ self.bluetooth_config_path))
+ self._reset_bluetooth()
diff --git a/acts_tests/acts_contrib/test_utils/bt/configs/bt_stack.conf b/acts_tests/acts_contrib/test_utils/bt/configs/bt_stack.conf
new file mode 100644
index 0000000..4bcf15a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/configs/bt_stack.conf
@@ -0,0 +1,29 @@
+# Enable trace level reconfiguration function
+# Must be present before any TRC_ trace level settings
+TraceConf=true
+
+# Trace level configuration
+# BT_TRACE_LEVEL_NONE 0 ( No trace messages to be generated )
+# BT_TRACE_LEVEL_ERROR 1 ( Error condition trace messages )
+# BT_TRACE_LEVEL_WARNING 2 ( Warning condition trace messages )
+# BT_TRACE_LEVEL_API 3 ( API traces )
+# BT_TRACE_LEVEL_EVENT 4 ( Debug messages for events )
+# BT_TRACE_LEVEL_DEBUG 5 ( Full debug messages )
+# BT_TRACE_LEVEL_VERBOSE 6 ( Verbose messages ) - Currently supported for TRC_BTAPP only.
+TRC_BTM=5
+TRC_HCI=5
+TRC_L2CAP=5
+TRC_RFCOMM=5
+TRC_OBEX=5
+TRC_AVCT=5
+TRC_AVDT=5
+TRC_AVRC=5
+TRC_AVDT_SCB=5
+TRC_AVDT_CCB=5
+TRC_A2D=2
+TRC_SDP=5
+TRC_GATT=5
+TRC_SMP=5
+TRC_BTAPP=5
+TRC_BTIF=5
+
diff --git a/acts_tests/acts_contrib/test_utils/bt/configs/dis_mitm_bt_stack.conf b/acts_tests/acts_contrib/test_utils/bt/configs/dis_mitm_bt_stack.conf
new file mode 100644
index 0000000..120fc1e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/configs/dis_mitm_bt_stack.conf
@@ -0,0 +1,30 @@
+# Enable trace level reconfiguration function
+# Must be present before any TRC_ trace level settings
+TraceConf=true
+
+# Trace level configuration
+# BT_TRACE_LEVEL_NONE 0 ( No trace messages to be generated )
+# BT_TRACE_LEVEL_ERROR 1 ( Error condition trace messages )
+# BT_TRACE_LEVEL_WARNING 2 ( Warning condition trace messages )
+# BT_TRACE_LEVEL_API 3 ( API traces )
+# BT_TRACE_LEVEL_EVENT 4 ( Debug messages for events )
+# BT_TRACE_LEVEL_DEBUG 5 ( Full debug messages )
+# BT_TRACE_LEVEL_VERBOSE 6 ( Verbose messages ) - Currently supported for TRC_BTAPP only.
+TRC_BTM=5
+TRC_HCI=5
+TRC_L2CAP=5
+TRC_RFCOMM=5
+TRC_OBEX=5
+TRC_AVCT=5
+TRC_AVDT=5
+TRC_AVRC=5
+TRC_AVDT_SCB=5
+TRC_AVDT_CCB=5
+TRC_A2D=2
+TRC_SDP=5
+TRC_GATT=5
+TRC_SMP=5
+TRC_BTAPP=5
+TRC_BTIF=5
+
+PTS_SmpOptions=0x9,0x4,0xf,0xf,0x10
diff --git a/acts_tests/acts_contrib/test_utils/bt/configs/non_bond_bt_stack.conf b/acts_tests/acts_contrib/test_utils/bt/configs/non_bond_bt_stack.conf
new file mode 100644
index 0000000..3dedf7e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/configs/non_bond_bt_stack.conf
@@ -0,0 +1,30 @@
+# Enable trace level reconfiguration function
+# Must be present before any TRC_ trace level settings
+TraceConf=true
+
+# Trace level configuration
+# BT_TRACE_LEVEL_NONE 0 ( No trace messages to be generated )
+# BT_TRACE_LEVEL_ERROR 1 ( Error condition trace messages )
+# BT_TRACE_LEVEL_WARNING 2 ( Warning condition trace messages )
+# BT_TRACE_LEVEL_API 3 ( API traces )
+# BT_TRACE_LEVEL_EVENT 4 ( Debug messages for events )
+# BT_TRACE_LEVEL_DEBUG 5 ( Full debug messages )
+# BT_TRACE_LEVEL_VERBOSE 6 ( Verbose messages ) - Currently supported for TRC_BTAPP only.
+TRC_BTM=5
+TRC_HCI=5
+TRC_L2CAP=5
+TRC_RFCOMM=5
+TRC_OBEX=5
+TRC_AVCT=5
+TRC_AVDT=5
+TRC_AVRC=5
+TRC_AVDT_SCB=5
+TRC_AVDT_CCB=5
+TRC_A2D=2
+TRC_SDP=5
+TRC_GATT=5
+TRC_SMP=5
+TRC_BTAPP=5
+TRC_BTIF=5
+
+PTS_SmpOptions=0xC,0x4,0xf,0xf,0x10
diff --git a/acts_tests/acts_contrib/test_utils/bt/gatt_test_database.py b/acts_tests/acts_contrib/test_utils/bt/gatt_test_database.py
new file mode 100644
index 0000000..8c4f54c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/gatt_test_database.py
@@ -0,0 +1,1705 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
+
+STRING_512BYTES = '''
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+11111222223333344444555556666677777888889999900000
+111112222233
+'''
+STRING_50BYTES = '''
+11111222223333344444555556666677777888889999900000
+'''
+STRING_25BYTES = '''
+1111122222333334444455555
+'''
+
+INVALID_SMALL_DATABASE = {
+ 'services': [{
+ 'uuid': '00001800-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': gatt_char_types['device_name'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'instance_id': 0x0003,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'Test Database'
+ }, {
+ 'uuid': gatt_char_types['appearance'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'instance_id': 0x0005,
+ 'value_type': gatt_characteristic_value_format['sint32'],
+ 'offset': 0,
+ 'value': 17
+ }, {
+ 'uuid': gatt_char_types['peripheral_pref_conn'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'instance_id': 0x0007
+ }]
+ }, {
+ 'uuid': '00001801-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': gatt_char_types['service_changed'],
+ 'properties': gatt_characteristic['property_indicate'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'instance_id': 0x0012,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0000],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['client_char_cfg'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }]
+ }, {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'instance_id': 0x0015,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04]
+ }]
+ }]
+}
+
+# Corresponds to the PTS defined LARGE_DB_1
+LARGE_DB_1 = {
+ 'services': [
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 7,
+ 'characteristics': [{
+ 'uuid': '0000b008-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'] |
+ gatt_characteristic['property_extended_props'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x08],
+ 'descriptors': [{
+ 'uuid': '0000b015-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }, {
+ 'uuid': '0000b016-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }, {
+ 'uuid': '0000b017-0000-1000-8000-00805f9b34fb',
+ 'permissions':
+ gatt_characteristic['permission_read_encrypted_mitm'],
+ }]
+ }]
+ },
+ {
+ 'uuid': '0000a00d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['secondary'],
+ 'handles': 6,
+ 'characteristics': [{
+ 'uuid': '0000b00c-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_extended_props'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0C],
+ }, {
+ 'uuid': '0000b00b-0000-0000-0123-456789abcdef',
+ 'properties': gatt_characteristic['property_extended_props'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0B],
+ }]
+ },
+ {
+ 'uuid': '0000a00a-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 10,
+ 'characteristics': [{
+ 'uuid': '0000b001-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x01],
+ }, {
+ 'uuid': '0000b002-0000-0000-0123-456789abcdef',
+ 'properties': gatt_characteristic['property_extended_props'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ }, {
+ 'uuid': '0000b004-0000-0000-0123-456789abcdef',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ }, {
+ 'uuid': '0000b002-0000-0000-0123-456789abcdef',
+ 'properties': gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '11111222223333344444555556666677777888889999900000',
+ }, {
+ 'uuid': '0000b003-0000-0000-0123-456789abcdef',
+ 'properties': gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x03],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 3,
+ 'characteristics': [{
+ 'uuid': '0000b007-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x07],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 3,
+ 'characteristics': [{
+ 'uuid': '0000b006-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'] |
+ gatt_characteristic['property_write_no_response'] |
+ gatt_characteristic['property_notify'] |
+ gatt_characteristic['property_indicate'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x06],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 12,
+ 'characteristics': [
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ },
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['server_char_cfg'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': gatt_descriptor['disable_notification_value']
+ }]
+ },
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x0,
+ 'permissions': 0x0,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ 'descriptors': [{
+ 'uuid': '0000b012-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33
+ ]
+ }]
+ },
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ 'descriptors': [{
+ 'uuid': '0000b012-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33
+ ]
+ }]
+ },
+ ]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 7,
+ 'characteristics': [{
+ 'uuid': '0000b005-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_write'] |
+ gatt_characteristic['property_extended_props'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x05],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x03, 0x00]
+ }, {
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x80, 0x81, 0x82,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90
+ ]
+ }, {
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions':
+ gatt_descriptor['permission_read_encrypted_mitm'],
+ 'value': [0x00, 0x01, 0x30, 0x01, 0x11, 0x31]
+ }, {
+ 'uuid': '0000d5d4-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x44]
+ }]
+ }]
+ },
+ {
+ 'uuid': '0000a00c-0000-0000-0123-456789abcdef',
+ 'type': gatt_service_types['primary'],
+ 'handles': 7,
+ 'characteristics': [{
+ 'uuid': '0000b009-0000-0000-0123-456789abcdef',
+ 'enforce_initial_attribute_length': True,
+ 'properties': gatt_characteristic['property_write'] |
+ gatt_characteristic['property_extended_props'] |
+ gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x09],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': gatt_descriptor['enable_notification_value']
+ }, {
+ 'uuid': '0000d9d2-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ }, {
+ 'uuid': '0000d9d3-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_write'],
+ 'value': [0x33]
+ }]
+ }]
+ },
+ {
+ 'uuid': '0000a00f-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 18,
+ 'characteristics': [
+ {
+ 'uuid': '0000b00e-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "Length is ",
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x19, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00]
+ }]
+ },
+ {
+ 'uuid': '0000b00f-0000-1000-8000-00805f9b34fb',
+ 'enforce_initial_attribute_length': True,
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x65],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x04, 0x00, 0x01, 0x27, 0x01, 0x01, 0x00]
+ }]
+ },
+ {
+ 'uuid': '0000b006-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x34, 0x12],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x06, 0x00, 0x10, 0x27, 0x01, 0x02, 0x00]
+ }]
+ },
+ {
+ 'uuid': '0000b007-0000-1000-8000-00805f9b34fb',
+ 'enforce_initial_attribute_length': True,
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04, 0x03, 0x02, 0x01],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x08, 0x00, 0x17, 0x27, 0x01, 0x03, 0x00]
+ }]
+ },
+ {
+ 'uuid': '0000b010-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x65, 0x34, 0x12, 0x04, 0x03, 0x02, 0x01],
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_agreg_fmt'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0xa6, 0x00, 0xa9, 0x00, 0xac, 0x00]
+ }]
+ },
+ {
+ 'uuid': '0000b011-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['write_type_signed']
+ | #for some reason 0x40 is not working...
+ gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x12]
+ }
+ ]
+ },
+ {
+ 'uuid': '0000a00c-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 30,
+ 'characteristics': [{
+ 'uuid': '0000b00a-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0a],
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "111112222233333444445",
+ 'descriptors': [{
+ 'uuid': '0000b012-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "2222233333444445555566",
+ 'descriptors': [{
+ 'uuid': '0000b013-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "33333444445555566666777",
+ 'descriptors': [{
+ 'uuid': '0000b014-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22, 0x33
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33
+ ],
+ 'descriptors': [{
+ 'uuid': '0000b012-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44
+ ],
+ 'descriptors': [{
+ 'uuid': '0000b013-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55
+ ],
+ 'descriptors': [{
+ 'uuid': '0000b014-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "1111122222333334444455555666667777788888999",
+ 'descriptors': [{
+ 'uuid': '0000b012-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "22222333334444455555666667777788888999990000",
+ 'descriptors': [{
+ 'uuid': '0000b013-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44
+ ]
+ }]
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "333334444455555666667777788888999990000011111",
+ 'descriptors': [{
+ 'uuid': '0000b014-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34,
+ 0x56, 0x78, 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
+ ]
+ }]
+ }]
+ },
+ ]
+}
+
+# Corresponds to the PTS defined LARGE_DB_2
+LARGE_DB_2 = {
+ 'services': [
+ {
+ 'uuid': '0000a00c-0000-0000-0123-456789abdcef',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b00a-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0003,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0005,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '111112222233333444445',
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0007,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '2222233333444445555566',
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0009,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '33333444445555566666777',
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x000b,
+ 'properties': 0x0a0,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '1111122222333334444455555666667777788888999',
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x000d,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '22222333334444455555666667777788888999990000',
+ }, {
+ 'uuid': '0000b0002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x000f,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ }]
+ },
+ {
+ 'uuid': '0000a00c-0000-0000-0123-456789abcdef',
+ 'handles': 5,
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b009-0000-0000-0123-456789abcdef',
+ 'instance_id': 0x0023,
+ 'properties': 0x8a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x09],
+ 'descriptors': [{
+ 'uuid': '0000d9d2-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ }, {
+ 'uuid': '0000d9d3-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_write'],
+ 'value': [0x33]
+ }, {
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_write'],
+ 'value': gatt_descriptor['enable_notification_value']
+ }]
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b007-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0012,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ }]
+ },
+ ]
+}
+
+DB_TEST = {
+ 'services': [{
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x01],
+ 'enforce_initial_attribute_length': True,
+ 'descriptors': [{
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x01] * 30
+ }]
+ }, ]
+ }]
+}
+
+PTS_TEST2 = {
+ 'services': [{
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [
+ {
+ 'uuid': '000018ba-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000060aa-0000-0000-0123-456789abcdef',
+ 'properties': 0x02,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x20,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000004d5e-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000001b44-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000006b98-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08 | 0x10 | 0x04,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000247f-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000247f-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000247f-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x00,
+ 'permissions': 0x00,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000247f-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000d62-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08 | 0x80,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000002e85-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000004a64-0000-0000-0123-456789abcdef',
+ 'properties': 0x02 | 0x08 | 0x80,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000005b4a-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000001c81-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000006b98-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000001b44-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000c55-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '0000014dd-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000c55-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000c55-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000c55-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000c55-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '00000008f-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02,
+ 'permissions': 0x10,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000000af2-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x32
+ ],
+ },
+ {
+ 'uuid': '000002aad-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000002ab0-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ {
+ 'uuid': '000002ab3-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_512BYTES,
+ },
+ ]
+ }]
+}
+
+PTS_TEST = {
+ 'services': [{
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [
+ {
+ 'uuid': '000018ba-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_25BYTES,
+ },
+ {
+ 'uuid': '000060aa-0000-1000-8000-00805f9b34fb',
+ 'properties': 0x02 | 0x08,
+ 'permissions': 0x10 | 0x01,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': STRING_25BYTES,
+ },
+ ]
+ }]
+}
+
+# Corresponds to the PTS defined LARGE_DB_3
+LARGE_DB_3 = {
+ 'services': [
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0003,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ },
+ {
+ 'uuid': '0000b004-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0013,
+ 'properties': 0x10,
+ 'permissions': 0x17,
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x04],
+ 'descriptors': [
+ {
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x09]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['client_char_cfg'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x01, 0x00]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['server_char_cfg'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_agreg_fmt'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_valid_range'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid':
+ gatt_char_desc_uuids['external_report_reference'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['report_reference'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ ]
+ },
+ {
+ 'uuid': gatt_char_types['service_changed'],
+ 'instance_id': 0x0023,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['appearance'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['peripheral_priv_flag'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['reconnection_address'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['system_id'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['model_number_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['serial_number_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['firmware_revision_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['hardware_revision_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['software_revision_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['manufacturer_name_string'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ {
+ 'uuid': gatt_char_types['pnp_id'],
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ ]
+ },
+ {
+ 'uuid': '0000a00d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['secondary'],
+ 'handles': 5,
+ 'characteristics': [{
+ 'uuid': '0000b00c-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0023,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0c],
+ }, {
+ 'uuid': '0000b00b-0000-0000-0123-456789abcdef',
+ 'instance_id': 0x0025,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0b],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b008-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0032,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x08],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b007-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0042,
+ 'properties': gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x07],
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b006-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0052,
+ 'properties': 0x3e,
+ 'permissions': gatt_characteristic['permission_write_encrypted_mitm'] |
+ gatt_characteristic['permission_read_encrypted_mitm'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x06],
+ }]
+ },
+ {
+ 'uuid': '0000a00a-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 10,
+ 'characteristics': [{
+ 'uuid': '0000b001-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0074,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x01],
+ }, {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'enforce_initial_attribute_length': True,
+ 'instance_id': 0x0076,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '11111222223333344444555556666677777888889999900000',
+ }, {
+ 'uuid': '0000b003-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x0078,
+ 'properties': gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x03],
+ }]
+ },
+ {
+ 'uuid': '0000a00c-0000-0000-0123-456789abcdef',
+ 'type': gatt_service_types['primary'],
+ 'handles': 10,
+ 'characteristics': [{
+ 'uuid': '0000b009-0000-0000-0123-456789abcdef',
+ 'instance_id': 0x0082,
+ 'properties': 0x8a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x09],
+ 'descriptors': [
+ {
+ 'uuid': '0000b009-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x09]
+ },
+ {
+ 'uuid': '0000d9d2-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x01, 0x00]
+ },
+ {
+ 'uuid': '0000d9d3-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_write'],
+ 'value': [0x22]
+ },
+ ]
+ }]
+ },
+ {
+ 'uuid': '0000a00b-0000-0000-0123-456789abcdef',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b009-0000-0000-0123-456789abcdef',
+ 'instance_id': 0x0092,
+ 'properties': 0x8a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x05],
+ 'descriptors': [
+ {
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'value': [0] * 26
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_ext_props'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x03, 0x00]
+ },
+ {
+ 'uuid': '0000d5d4-0000-0000-0123-456789abcdef',
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x44]
+ },
+ {
+ 'uuid': gatt_char_desc_uuids['char_fmt_uuid'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x04, 0x00, 0x01, 0x30, 0x01, 0x11, 0x31]
+ },
+ ]
+ }]
+ },
+ {
+ 'uuid': '0000a00c-0000-0000-0123-456789abcdef',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [
+ {
+ 'uuid': '0000b00a-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00a2,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0a],
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00a4,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '111112222233333444445',
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00a6,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '2222233333444445555566',
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00a8,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '33333444445555566666777',
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00aa,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '1111122222333334444455555666667777788888999',
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00ac,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '22222333334444455555666667777788888999990000',
+ },
+ {
+ 'uuid': '0000b002-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0x00ae,
+ 'enforce_initial_attribute_length': True,
+ 'properties': 0x0a,
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': '333334444455555666667777788888999990000011111',
+ },
+ ]
+ },
+ {
+ 'uuid': '0000a00e-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': '0000b00d-0000-1000-8000-00805f9b34fb',
+ 'instance_id': 0xffff,
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x0d],
+ }]
+ },
+ ]
+}
+
+TEST_DB_1 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 4,
+ 'characteristics': [{
+ 'uuid': '00002a29-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002a,
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x01]
+ }]
+ }]
+ }]
+}
+
+TEST_DB_2 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 4,
+ 'characteristics': [{
+ 'uuid': '00002a29-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions':
+ gatt_characteristic['permission_read_encrypted_mitm'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002a,
+ }, {
+ 'uuid': '00002a30-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions':
+ gatt_characteristic['permission_read_encrypted_mitm'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002b,
+ }]
+ }]
+}
+
+TEST_DB_3 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 4,
+ 'characteristics': [{
+ 'uuid': '00002a29-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002a,
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x01]
+ }, {
+ 'uuid': '00002a20-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ 'instance_id': 0x002c,
+ 'value': [0x01]
+ }]
+ }, {
+ 'uuid': '00002a30-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] |
+ gatt_characteristic['property_write'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002b,
+ }]
+ }]
+}
+
+TEST_DB_4 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 4,
+ 'characteristics': [{
+ 'uuid': '00002a29-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_write_no_response'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': "test",
+ 'instance_id': 0x002a,
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['char_user_desc'],
+ 'permissions':
+ gatt_descriptor['permission_read_encrypted_mitm'],
+ 'value': [0] * 512
+ }]
+ }]
+ }]
+}
+
+TEST_DB_5 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': 'b2c83efa-34ca-11e6-ac61-9e71128cae77',
+ 'properties': gatt_characteristic['property_write'] |
+ gatt_characteristic['property_read'] |
+ gatt_characteristic['property_notify'],
+ 'permissions': gatt_characteristic['permission_read'] |
+ gatt_characteristic['permission_write'],
+ 'value_type': gatt_characteristic_value_format['byte'],
+ 'value': [0x1],
+ 'instance_id': 0x002c,
+ 'descriptors': [{
+ 'uuid': '00002902-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'] |
+ gatt_descriptor['permission_write'],
+ }]
+ }]
+ }]
+}
+
+TEST_DB_6 = {
+ 'services': [{
+ 'uuid': '0000180d-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'handles': 4,
+ 'characteristics': [{
+ 'uuid': '00002a29-0000-1000-8000-00805f9b34fb',
+ 'properties': gatt_characteristic['property_read'] | gatt_characteristic['property_notify'],
+ 'permissions': gatt_characteristic['permission_read_encrypted_mitm'],
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'test',
+ 'instance_id': 0x002a,
+ 'descriptors': [{
+ 'uuid': '00002a19-0000-1000-8000-00805f9b34fb',
+ 'permissions': gatt_descriptor['permission_read'],
+ 'value': [0x01] * 30
+ }]
+ }]
+ }]
+}
+
+SIMPLE_READ_DESCRIPTOR = {
+ 'services': [{
+ 'uuid': '0000a00a-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': 'aa7edd5a-4d1d-4f0e-883a-d145616a1630',
+ 'properties': gatt_characteristic['property_read'],
+ 'permissions': gatt_characteristic['permission_read'],
+ 'instance_id': 0x002a,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'Test Database',
+ 'descriptors': [{
+ 'uuid': gatt_char_desc_uuids['client_char_cfg'],
+ 'permissions': gatt_descriptor['permission_read'],
+ }]
+ }]
+ }]
+}
+
+CHARACTERISTIC_PROPERTY_WRITE_NO_RESPONSE = {
+ 'services': [{
+ 'uuid': '0000a00a-0000-1000-8000-00805f9b34fb',
+ 'type': gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid': 'aa7edd5a-4d1d-4f0e-883a-d145616a1630',
+ 'properties': gatt_characteristic['property_write_no_response'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'instance_id': 0x0042,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'Test Database'
+ }, {
+ 'uuid': 'aa7edd6a-4d1d-4f0e-883a-d145616a1630',
+ 'properties': gatt_characteristic['property_write_no_response'],
+ 'permissions': gatt_characteristic['permission_write'] |
+ gatt_characteristic['permission_read'],
+ 'instance_id': 0x004d,
+ 'value_type': gatt_characteristic_value_format['string'],
+ 'value': 'Test Database'
+ }]
+ }]
+}
+
+GATT_SERVER_DB_MAPPING = {
+ 'LARGE_DB_1': LARGE_DB_1,
+ 'LARGE_DB_3': LARGE_DB_3,
+ 'INVALID_SMALL_DATABASE': INVALID_SMALL_DATABASE,
+ 'SIMPLE_READ_DESCRIPTOR': SIMPLE_READ_DESCRIPTOR,
+ 'CHARACTERISTIC_PROPERTY_WRITE_NO_RESPONSE':
+ CHARACTERISTIC_PROPERTY_WRITE_NO_RESPONSE,
+ 'TEST_DB_1': TEST_DB_1,
+ 'TEST_DB_2': TEST_DB_2,
+ 'TEST_DB_3': TEST_DB_3,
+ 'TEST_DB_4': TEST_DB_4,
+ 'TEST_DB_5': TEST_DB_5,
+ 'LARGE_DB_3_PLUS': LARGE_DB_3,
+ 'DB_TEST': DB_TEST,
+ 'PTS_TEST': PTS_TEST,
+ 'PTS_TEST2': PTS_TEST2,
+ 'TEST_DB_6': TEST_DB_6,
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py b/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py
new file mode 100644
index 0000000..86f0950
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py
@@ -0,0 +1,576 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+GATT Client Libraries
+"""
+
+from acts_contrib.test_utils.bt.bt_constants import default_le_connection_interval_ms
+from acts_contrib.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import le_default_supervision_timeout
+from acts_contrib.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
+
+import time
+import os
+
+
+class GattClientLib():
+ def __init__(self, log, dut, target_mac_addr=None):
+ self.dut = dut
+ self.log = log
+ self.gatt_callback = None
+ self.bluetooth_gatt = None
+ self.discovered_services_index = None
+ self.target_mac_addr = target_mac_addr
+ self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb"
+
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
+
+ def connect_over_le_based_off_name(self, autoconnect, name):
+ """Perform GATT connection over LE"""
+ self.dut.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
+ filter_list = self.dut.droid.bleGenFilterList()
+ scan_settings = self.dut.droid.bleBuildScanSetting()
+ scan_callback = self.dut.droid.bleGenScanCallback()
+ event_name = scan_result.format(scan_callback)
+ self.dut.droid.bleSetScanFilterDeviceName("BLE Rect")
+ self.dut.droid.bleBuildScanFilter(filter_list)
+ self.dut.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+
+ try:
+ event = self.dut.ed.pop_event(event_name, 10)
+ self.log.info("Found scan result: {}".format(event))
+ except Exception:
+ self.log.info("Didn't find any scan results.")
+ mac_addr = event['data']['Result']['deviceInfo']['address']
+ self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
+ self.dut, mac_addr, autoconnect, transport=gatt_transport['le'])
+ self.dut.droid.bleStopBleScan(scan_callback)
+ self.discovered_services_index = None
+
+ def connect_over_le(self, autoconnect):
+ """Perform GATT connection over LE"""
+ self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
+ self.dut,
+ self.target_mac_addr,
+ autoconnect,
+ transport=gatt_transport['le'])
+ self.discovered_services_index = None
+
+ def connect_over_bredr(self):
+ """Perform GATT connection over BREDR"""
+ self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
+ self.dut,
+ self.target_mac_addr,
+ False,
+ transport=gatt_transport['bredr'])
+
+ def disconnect(self):
+ """Perform GATT disconnect"""
+ cmd = "Disconnect GATT connection"
+ try:
+ disconnect_gatt_connection(self.dut, self.bluetooth_gatt,
+ self.gatt_callback)
+ except Exception as err:
+ self.log.info("Cmd {} failed with {}".format(cmd, err))
+ try:
+ self.dut.droid.gattClientClose(self.bluetooth_gatt)
+ except Exception as err:
+ self.log.info("Cmd failed with {}".format(err))
+
+ def _setup_discovered_services_index(self):
+ if not self.discovered_services_index:
+ self.dut.droid.gattClientDiscoverServices(self.bluetooth_gatt)
+ expected_event = gatt_cb_strings['gatt_serv_disc'].format(
+ self.gatt_callback)
+ event = self.dut.ed.pop_event(expected_event, 10)
+ self.discovered_services_index = event['data']['ServicesIndex']
+
+ def read_char_by_uuid(self, line):
+ """GATT client read Characteristic by UUID."""
+ uuid = line
+ if len(line) == 4:
+ uuid = self.generic_uuid.format(line)
+ self.dut.droid.gattClientReadUsingCharacteristicUuid(
+ self.bluetooth_gatt, uuid, 0x0001, 0xFFFF)
+
+ def request_mtu(self, mtu):
+ """Request MTU Change of input value"""
+ setup_gatt_mtu(self.dut, self.bluetooth_gatt, self.gatt_callback,
+ int(mtu))
+
+ def list_all_uuids(self):
+ """From the GATT Client, discover services and list all services,
+ chars and descriptors
+ """
+ self._setup_discovered_services_index()
+ log_gatt_server_uuids(self.dut, self.discovered_services_index,
+ self.bluetooth_gatt)
+
+ def discover_services(self):
+ """GATT Client discover services of GATT Server"""
+ self.dut.droid.gattClientDiscoverServices(self.bluetooth_gatt)
+
+ def refresh(self):
+ """Perform Gatt Client Refresh"""
+ self.dut.droid.gattClientRefresh(self.bluetooth_gatt)
+
+ def read_char_by_instance_id(self, id):
+ """From the GATT Client, discover services and list all services,
+ chars and descriptors
+ """
+ if not id:
+ self.log.info("Invalid id")
+ return
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, int(id, 16))
+
+ def write_char_by_instance_id(self, line):
+ """GATT Client Write to Characteristic by instance ID"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def write_char_by_instance_id_value(self, line):
+ """GATT Client Write to Characteristic by instance ID"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ write_value = args[1]
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), [int(write_value)])
+
+ def mod_write_char_by_instance_id(self, line):
+ """GATT Client Write to Char that doesn't have write permission"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientModifyAccessAndWriteCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def write_invalid_char_by_instance_id(self, line):
+ """GATT Client Write to Char that doesn't exists"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteInvalidCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def mod_read_char_by_instance_id(self, line):
+ """GATT Client Read Char that doesn't have write permission"""
+ instance_id = line
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientModifyAccessAndReadCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16))
+
+ def read_invalid_char_by_instance_id(self, line):
+ """GATT Client Read Char that doesn't exists"""
+ instance_id = line
+ self.dut.droid.gattClientReadInvalidCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16))
+
+ def mod_write_desc_by_instance_id(self, line):
+ """GATT Client Write to Desc that doesn't have write permission"""
+ cmd = ""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientModifyAccessAndWriteDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def write_invalid_desc_by_instance_id(self, line):
+ """GATT Client Write to Desc that doesn't exists"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteInvalidDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def mod_read_desc_by_instance_id(self, line):
+ """GATT Client Read Desc that doesn't have write permission"""
+ cmd = ""
+ instance_id = line
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientModifyAccessAndReadDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16))
+
+ def read_invalid_desc_by_instance_id(self, line):
+ """GATT Client Read Desc that doesn't exists"""
+ instance_id = line
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadInvalidDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16))
+
+ def mod_read_char_by_uuid_and_instance_id(self, line):
+ """GATT Client Read Char that doesn't have write permission"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [uuid] [instance_id]")
+ return
+ uuid = args[0]
+ instance_id = args[1]
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientModifyAccessAndReadCharacteristicByUuidAndInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), self.generic_uuid.format(uuid))
+
+ def read_invalid_char_by_uuid(self, line):
+ """GATT Client Read Char that doesn't exists"""
+ uuid = line
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientReadInvalidCharacteristicByUuid(
+ self.bluetooth_gatt, self.discovered_services_index,
+ self.generic_uuid.format(uuid))
+
+ def write_desc_by_instance_id(self, line):
+ """GATT Client Write to Descriptor by instance ID"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [instanceID] [size]")
+ return
+ instance_id = args[0]
+ size = args[1]
+ write_value = []
+ for i in range(int(size)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def write_desc_notification_by_instance_id(self, line):
+ """GATT Client Write to Descriptor by instance ID"""
+ args = line.split()
+ instance_id = args[0]
+ switch = int(args[1])
+ write_value = [0x00, 0x00]
+ if switch == 2:
+ write_value = [0x02, 0x00]
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteDescriptorByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), write_value)
+
+ def enable_notification_desc_by_instance_id(self, line):
+ """GATT Client Enable Notification on Descriptor by instance ID"""
+ instance_id = line
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ self.dut.droid.
+ gattClientGetDiscoveredDescriptorUuidsByIndex(
+ self.discovered_services_index, i, j))
+ for k in range(len(descriptor_uuids)):
+ desc_inst_id = self.dut.droid.gattClientGetDescriptorInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j, k)
+ if desc_inst_id == int(instance_id, 16):
+ self.dut.droid.gattClientDescriptorSetValueByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k,
+ gatt_descriptor['enable_notification_value'])
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientWriteDescriptorByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k)
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientSetCharacteristicNotificationByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, True)
+
+ def enable_indication_desc_by_instance_id(self, line):
+ """GATT Client Enable indication on Descriptor by instance ID"""
+ instance_id = line
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ self.dut.droid.
+ gattClientGetDiscoveredDescriptorUuidsByIndex(
+ self.discovered_services_index, i, j))
+ for k in range(len(descriptor_uuids)):
+ desc_inst_id = self.dut.droid.gattClientGetDescriptorInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j, k)
+ if desc_inst_id == int(instance_id, 16):
+ self.dut.droid.gattClientDescriptorSetValueByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k,
+ gatt_descriptor['enable_indication_value'])
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientWriteDescriptorByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k)
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientSetCharacteristicNotificationByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, True)
+
+ def char_enable_all_notifications(self):
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ self.dut.droid.gattClientSetCharacteristicNotificationByIndex(
+ self.bluetooth_gatt, self.discovered_services_index, i, j,
+ True)
+
+ def read_char_by_invalid_instance_id(self, line):
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ self.dut.droid.gattClientReadInvalidCharacteristicInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, 0,
+ int(line, 16))
+
+ def begin_reliable_write(self):
+ """Begin a reliable write on the Bluetooth Gatt Client"""
+ self.dut.droid.gattClientBeginReliableWrite(self.bluetooth_gatt)
+
+ def abort_reliable_write(self):
+ """Abort a reliable write on the Bluetooth Gatt Client"""
+ self.dut.droid.gattClientAbortReliableWrite(self.bluetooth_gatt)
+
+ def execute_reliable_write(self):
+ """Execute a reliable write on the Bluetooth Gatt Client"""
+ self.dut.droid.gattExecuteReliableWrite(self.bluetooth_gatt)
+
+ def read_all_char(self):
+ """GATT Client read all Characteristic values"""
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ char_inst_id = self.dut.droid.gattClientGetCharacteristicInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i, j)
+ self.log.info("Reading characteristic {} {}".format(
+ hex(char_inst_id), characteristic_uuids[j]))
+ self.dut.droid.gattClientReadCharacteristicByIndex(
+ self.bluetooth_gatt, self.discovered_services_index, i, j)
+ time.sleep(1) # Necessary for PTS
+
+ def read_all_desc(self):
+ """GATT Client read all Descriptor values"""
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ self.dut.droid.
+ gattClientGetDiscoveredDescriptorUuidsByIndex(
+ self.discovered_services_index, i, j))
+ for k in range(len(descriptor_uuids)):
+ time.sleep(1)
+ try:
+ self.log.info("Reading descriptor {}".format(
+ descriptor_uuids[k]))
+ self.dut.droid.gattClientReadDescriptorByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k)
+ except Exception as err:
+ self.log.info(
+ "Failed to read to descriptor: {}".format(
+ descriptor_uuids[k]))
+
+ def write_all_char(self, line):
+ """Write to every Characteristic on the GATT server"""
+ args = line.split()
+ write_value = []
+ for i in range(int(line)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ char_inst_id = self.dut.droid.gattClientGetCharacteristicInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i, j)
+ self.log.info("Writing to {} {}".format(
+ hex(char_inst_id), characteristic_uuids[j]))
+ try:
+ self.dut.droid.gattClientCharacteristicSetValueByIndex(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j, write_value)
+ self.dut.droid.gattClientWriteCharacteristicByIndex(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j)
+ time.sleep(1)
+ except Exception as err:
+ self.log.info(
+ "Failed to write to characteristic: {}".format(
+ characteristic_uuids[j]))
+
+ def write_all_desc(self, line):
+ """ Write to every Descriptor on the GATT server """
+ args = line.split()
+ write_value = []
+ for i in range(int(line)):
+ write_value.append(i % 256)
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ self.dut.droid.
+ gattClientGetDiscoveredDescriptorUuidsByIndex(
+ self.discovered_services_index, i, j))
+ for k in range(len(descriptor_uuids)):
+ time.sleep(1)
+ desc_inst_id = self.dut.droid.gattClientGetDescriptorInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j, k)
+ self.log.info("Writing to {} {}".format(
+ hex(desc_inst_id), descriptor_uuids[k]))
+ try:
+ self.dut.droid.gattClientDescriptorSetValueByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k,
+ write_value)
+ self.dut.droid.gattClientWriteDescriptorByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k)
+ except Exception as err:
+ self.log.info(
+ "Failed to write to descriptor: {}".format(
+ descriptor_uuids[k]))
+
+ def discover_service_by_uuid(self, line):
+ """ Discover service by UUID """
+ uuid = line
+ if len(line) == 4:
+ uuid = self.generic_uuid.format(line)
+ self.dut.droid.gattClientDiscoverServiceByUuid(self.bluetooth_gatt,
+ uuid)
+
+ def request_le_connection_parameters(self):
+ le_min_ce_len = 0
+ le_max_ce_len = 0
+ le_connection_interval = 0
+ minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ return_status = self.dut.droid.gattClientRequestLeConnectionParameters(
+ self.bluetooth_gatt, minInterval, maxInterval, 0,
+ le_default_supervision_timeout, le_min_ce_len, le_max_ce_len)
+ self.log.info(
+ "Result of request le connection param: {}".format(return_status))
+
+ def socket_conn_begin_connect_thread_psm(self, line):
+ args = line.split()
+ is_ble = bool(int(args[0]))
+ secured_conn = bool(int(args[1]))
+ psm_value = int(args[2]) # 1
+ self.dut.droid.bluetoothSocketConnBeginConnectThreadPsm(
+ self.target_mac_addr, is_ble, psm_value, secured_conn)
+
+ def socket_conn_begin_accept_thread_psm(self, line):
+ accept_timeout_ms = default_bluetooth_socket_timeout_ms
+ is_ble = True
+ secured_conn = False
+ self.dut.droid.bluetoothSocketConnBeginAcceptThreadPsm(
+ accept_timeout_ms, is_ble, secured_conn)
diff --git a/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py b/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py
new file mode 100644
index 0000000..ebfe1bd
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import time
+import os
+
+from acts.keys import Config
+from acts.utils import rand_ascii_str
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_server_responses
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import small_timeout
+from acts_contrib.test_utils.bt.gatt_test_database import STRING_512BYTES
+
+from acts.utils import exe_cmd
+from math import ceil
+
+
+class GattServerLib():
+
+ characteristic_list = []
+ default_timeout = 10
+ descriptor_list = []
+ dut = None
+ gatt_server = None
+ gatt_server_callback = None
+ gatt_server_list = []
+ log = None
+ service_list = []
+ write_mapping = {}
+
+ def __init__(self, log, dut):
+ self.dut = dut
+ self.log = log
+
+ def list_all_uuids(self):
+ """From the GATT Client, discover services and list all services,
+ chars and descriptors.
+ """
+ self.log.info("Service List:")
+ for service in self.dut.droid.gattGetServiceUuidList(self.gatt_server):
+ self.dut.log.info("GATT Server service uuid: {}".format(service))
+ self.log.info("Characteristics List:")
+ for characteristic in self.characteristic_list:
+ instance_id = self.dut.droid.gattServerGetCharacteristicInstanceId(
+ characteristic)
+ uuid = self.dut.droid.gattServerGetCharacteristicUuid(
+ characteristic)
+ self.dut.log.info(
+ "GATT Server characteristic handle uuid: {} {}".format(
+ hex(instance_id), uuid))
+ # TODO: add getting insance ids and uuids from each descriptor.
+
+ def open(self):
+ """Open an empty GATT Server instance"""
+ self.gatt_server_callback = self.dut.droid.gattServerCreateGattServerCallback(
+ )
+ self.gatt_server = self.dut.droid.gattServerOpenGattServer(
+ self.gatt_server_callback)
+ self.gatt_server_list.append(self.gatt_server)
+
+ def clear_services(self):
+ """Clear BluetoothGattServices from BluetoothGattServer"""
+ self.dut.droid.gattServerClearServices(self.gatt_server)
+
+ def close_bluetooth_gatt_servers(self):
+ """Close Bluetooth Gatt Servers"""
+ try:
+ for btgs in self.gatt_server_list:
+ self.dut.droid.gattServerClose(btgs)
+ except Exception as err:
+ self.log.error(
+ "Failed to close Bluetooth GATT Servers: {}".format(err))
+ self.characteristic_list = []
+ self.descriptor_list = []
+ self.gatt_server_list = []
+ self.service_list = []
+
+ def characteristic_set_value_by_instance_id(self, instance_id, value):
+ """Set Characteristic value by instance id"""
+ self.dut.droid.gattServerCharacteristicSetValueByInstanceId(
+ int(instance_id, 16), value)
+
+ def notify_characteristic_changed(self, instance_id, confirm):
+ """ Notify characteristic changed """
+ self.dut.droid.gattServerNotifyCharacteristicChangedByInstanceId(
+ self.gatt_server, 0, int(instance_id, 16), confirm)
+
+ def send_response(self, user_input):
+ """Send a single response to the GATT Client"""
+ args = user_input.split()
+ mtu = 23
+ if len(args) == 2:
+ user_input = args[0]
+ mtu = int(args[1])
+ desc_read = gatt_event['desc_read_req']['evt'].format(
+ self.gatt_server_callback)
+ desc_write = gatt_event['desc_write_req']['evt'].format(
+ self.gatt_server_callback)
+ char_read = gatt_event['char_read_req']['evt'].format(
+ self.gatt_server_callback)
+ char_write_req = gatt_event['char_write_req']['evt'].format(
+ self.gatt_server_callback)
+ char_write = gatt_event['char_write']['evt'].format(
+ self.gatt_server_callback)
+ execute_write = gatt_event['exec_write']['evt'].format(
+ self.gatt_server_callback)
+ regex = "({}|{}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
+ char_write, execute_write,
+ char_write_req)
+ events = self.dut.ed.pop_events(regex, 5, small_timeout)
+ status = 0
+ if user_input:
+ status = gatt_server_responses.get(user_input)
+ for event in events:
+ self.log.debug("Found event: {}.".format(event))
+ request_id = event['data']['requestId']
+ if event['name'] == execute_write:
+ if ('execute' in event['data']
+ and event['data']['execute'] == True):
+ for key in self.write_mapping:
+ value = self.write_mapping[key]
+ self.log.info("Writing key, value: {}, {}".format(
+ key, value))
+ self.dut.droid.gattServerSetByteArrayValueByInstanceId(
+ key, value)
+ else:
+ self.log.info("Execute result is false")
+ self.write_mapping = {}
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, 0, [])
+ continue
+ offset = event['data']['offset']
+ instance_id = event['data']['instanceId']
+ if (event['name'] == desc_write or event['name'] == char_write
+ or event['name'] == char_write_req):
+ if ('preparedWrite' in event['data']
+ and event['data']['preparedWrite'] == True):
+ value = event['data']['value']
+ if instance_id in self.write_mapping.keys():
+ self.write_mapping[
+ instance_id] = self.write_mapping[instance_id] + value
+ self.log.info(
+ "New Prepared Write Value for {}: {}".format(
+ instance_id, self.write_mapping[instance_id]))
+ else:
+ self.log.info("write mapping key, value {}, {}".format(
+ instance_id, value))
+ self.write_mapping[instance_id] = value
+ self.log.info("current value {}, {}".format(
+ instance_id, value))
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, 0, value)
+ continue
+ else:
+ self.dut.droid.gattServerSetByteArrayValueByInstanceId(
+ event['data']['instanceId'], event['data']['value'])
+
+ try:
+ data = self.dut.droid.gattServerGetReadValueByInstanceId(
+ int(event['data']['instanceId']))
+ except Exception as err:
+ self.log.error(err)
+ if not data:
+ data = [1]
+ self.log.info(
+ "GATT Server Send Response [request_id, status, offset, data]" \
+ " [{}, {}, {}, {}]".
+ format(request_id, status, offset, data))
+ data = data[offset:offset + mtu - 1]
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, offset, data)
+
+ def _setup_service(self, serv):
+ service = self.dut.droid.gattServerCreateService(
+ serv['uuid'], serv['type'])
+ if 'handles' in serv:
+ self.dut.droid.gattServerServiceSetHandlesToReserve(
+ service, serv['handles'])
+ return service
+
+ def _setup_characteristic(self, char):
+ characteristic = \
+ self.dut.droid.gattServerCreateBluetoothGattCharacteristic(
+ char['uuid'], char['properties'], char['permissions'])
+ if 'instance_id' in char:
+ self.dut.droid.gattServerCharacteristicSetInstanceId(
+ characteristic, char['instance_id'])
+ set_id = self.dut.droid.gattServerCharacteristicGetInstanceId(
+ characteristic)
+ if set_id != char['instance_id']:
+ self.log.error(
+ "Instance ID did not match up. Found {} Expected {}".
+ format(set_id, char['instance_id']))
+ if 'value_type' in char:
+ value_type = char['value_type']
+ value = char['value']
+ if value_type == gatt_characteristic_value_format['string']:
+ self.log.info("Set String value result: {}".format(
+ self.dut.droid.gattServerCharacteristicSetStringValue(
+ characteristic, value)))
+ elif value_type == gatt_characteristic_value_format['byte']:
+ self.log.info("Set Byte Array value result: {}".format(
+ self.dut.droid.gattServerCharacteristicSetByteValue(
+ characteristic, value)))
+ else:
+ self.log.info("Set Int value result: {}".format(
+ self.dut.droid.gattServerCharacteristicSetIntValue(
+ characteristic, value, value_type, char['offset'])))
+ return characteristic
+
+ def _setup_descriptor(self, desc):
+ descriptor = self.dut.droid.gattServerCreateBluetoothGattDescriptor(
+ desc['uuid'], desc['permissions'])
+ if 'value' in desc:
+ self.dut.droid.gattServerDescriptorSetByteValue(
+ descriptor, desc['value'])
+ if 'instance_id' in desc:
+ self.dut.droid.gattServerDescriptorSetInstanceId(
+ descriptor, desc['instance_id'])
+ self.descriptor_list.append(descriptor)
+ return descriptor
+
+ def setup_gatts_db(self, database):
+ """Setup GATT Server database"""
+ self.gatt_server_callback = \
+ self.dut.droid.gattServerCreateGattServerCallback()
+ self.gatt_server = self.dut.droid.gattServerOpenGattServer(
+ self.gatt_server_callback)
+ self.gatt_server_list.append(self.gatt_server)
+ for serv in database['services']:
+ service = self._setup_service(serv)
+ self.service_list.append(service)
+ if 'characteristics' in serv:
+ for char in serv['characteristics']:
+ characteristic = self._setup_characteristic(char)
+ if 'descriptors' in char:
+ for desc in char['descriptors']:
+ descriptor = self._setup_descriptor(desc)
+ self.dut.droid.gattServerCharacteristicAddDescriptor(
+ characteristic, descriptor)
+ self.characteristic_list.append(characteristic)
+ self.dut.droid.gattServerAddCharacteristicToService(
+ service, characteristic)
+ self.dut.droid.gattServerAddService(self.gatt_server, service)
+ expected_event = gatt_cb_strings['serv_added'].format(
+ self.gatt_server_callback)
+ self.dut.ed.pop_event(expected_event, 10)
+ return self.gatt_server, self.gatt_server_callback
+
+ def send_continuous_response(self, user_input):
+ """Send the same response"""
+ desc_read = gatt_event['desc_read_req']['evt'].format(
+ self.gatt_server_callback)
+ desc_write = gatt_event['desc_write_req']['evt'].format(
+ self.gatt_server_callback)
+ char_read = gatt_event['char_read_req']['evt'].format(
+ self.gatt_server_callback)
+ char_write = gatt_event['char_write']['evt'].format(
+ self.gatt_server_callback)
+ execute_write = gatt_event['exec_write']['evt'].format(
+ self.gatt_server_callback)
+ regex = "({}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
+ char_write, execute_write)
+ offset = 0
+ status = 0
+ mtu = 23
+ char_value = []
+ for i in range(512):
+ char_value.append(i % 256)
+ len_min = 470
+ end_time = time.time() + 180
+ i = 0
+ num_packets = ceil((len(char_value) + 1) / (mtu - 1))
+ while time.time() < end_time:
+ events = self.dut.ed.pop_events(regex, 10, small_timeout)
+ for event in events:
+ start_offset = i * (mtu - 1)
+ i += 1
+ self.log.debug("Found event: {}.".format(event))
+ request_id = event['data']['requestId']
+ data = char_value[start_offset:start_offset + mtu - 1]
+ if not data:
+ data = [1]
+ self.log.debug(
+ "GATT Server Send Response [request_id, status, offset, " \
+ "data] [{}, {}, {}, {}]".format(request_id, status, offset,
+ data))
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, offset, data)
+
+ def send_continuous_response_data(self, user_input):
+ """Send the same response with data"""
+ desc_read = gatt_event['desc_read_req']['evt'].format(
+ self.gatt_server_callback)
+ desc_write = gatt_event['desc_write_req']['evt'].format(
+ self.gatt_server_callback)
+ char_read = gatt_event['char_read_req']['evt'].format(
+ self.gatt_server_callback)
+ char_write = gatt_event['char_write']['evt'].format(
+ self.gatt_server_callback)
+ execute_write = gatt_event['exec_write']['evt'].format(
+ self.gatt_server_callback)
+ regex = "({}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
+ char_write, execute_write)
+ offset = 0
+ status = 0
+ mtu = 11
+ char_value = []
+ len_min = 470
+ end_time = time.time() + 180
+ i = 0
+ num_packets = ceil((len(char_value) + 1) / (mtu - 1))
+ while time.time() < end_time:
+ events = self.dut.ed.pop_events(regex, 10, small_timeout)
+ for event in events:
+ self.log.info(event)
+ request_id = event['data']['requestId']
+ if event['name'] == execute_write:
+ if ('execute' in event['data']
+ and event['data']['execute'] == True):
+ for key in self.write_mapping:
+ value = self.write_mapping[key]
+ self.log.debug("Writing key, value: {}, {}".format(
+ key, value))
+ self.dut.droid.gattServerSetByteArrayValueByInstanceId(
+ key, value)
+ self.write_mapping = {}
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, 0, [1])
+ continue
+ offset = event['data']['offset']
+ instance_id = event['data']['instanceId']
+ if (event['name'] == desc_write
+ or event['name'] == char_write):
+ if ('preparedWrite' in event['data']
+ and event['data']['preparedWrite'] == True):
+ value = event['data']['value']
+ if instance_id in self.write_mapping:
+ self.write_mapping[
+ instance_id] = self.write_mapping[instance_id] + value
+ else:
+ self.write_mapping[instance_id] = value
+ else:
+ self.dut.droid.gattServerSetByteArrayValueByInstanceId(
+ event['data']['instanceId'],
+ event['data']['value'])
+ try:
+ data = self.dut.droid.gattServerGetReadValueByInstanceId(
+ int(event['data']['instanceId']))
+ except Exception as err:
+ self.log.error(err)
+ if not data:
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, offset, [1])
+ else:
+ self.dut.droid.gattServerSendResponse(
+ self.gatt_server, 0, request_id, status, offset,
+ data[offset:offset + 17])
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/__init__.py b/acts_tests/acts_contrib/test_utils/bt/loggers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py b/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py
new file mode 100644
index 0000000..98c925e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py
@@ -0,0 +1,162 @@
+# /usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import base64
+from google.protobuf import message
+import os
+import time
+
+from acts.metrics.core import ProtoMetric
+from acts.metrics.logger import MetricLogger
+from acts_contrib.test_utils.bt.loggers.protos import bluetooth_metric_pb2
+
+
+def recursive_assign(proto, dct):
+ """Assign values in dct to proto recursively."""
+ for metric in dir(proto):
+ if metric in dct:
+ if (isinstance(dct[metric], dict) and
+ isinstance(getattr(proto, metric), message.Message)):
+ recursive_assign(getattr(proto, metric), dct[metric])
+ else:
+ setattr(proto, metric, dct[metric])
+
+
+class BluetoothMetricLogger(MetricLogger):
+ """A logger for gathering Bluetooth test metrics
+
+ Attributes:
+ proto_module: Module used to store Bluetooth metrics in a proto
+ results: Stores ProtoMetrics to be published for each logger context
+ proto_map: Maps test case names to the appropriate protos for each case
+ """
+
+ def __init__(self, event):
+ super().__init__(event=event)
+ self.proto_module = bluetooth_metric_pb2
+ self.results = []
+ self.start_time = int(time.time())
+
+ self.proto_map = {'BluetoothPairAndConnectTest': self.proto_module
+ .BluetoothPairAndConnectTestResult(),
+ 'BluetoothReconnectTest': self.proto_module
+ .BluetoothReconnectTestResult(),
+ 'BluetoothThroughputTest': self.proto_module
+ .BluetoothDataTestResult(),
+ 'BluetoothLatencyTest': self.proto_module
+ .BluetoothDataTestResult(),
+ 'BtCodecSweepTest': self.proto_module
+ .BluetoothAudioTestResult(),
+ 'BtRangeCodecTest': self.proto_module
+ .BluetoothAudioTestResult(),
+ }
+
+ @staticmethod
+ def get_configuration_data(device):
+ """Gets the configuration data of a device.
+
+ Gets the configuration data of a device and organizes it in a
+ dictionary.
+
+ Args:
+ device: The device object to get the configuration data from.
+
+ Returns:
+ A dictionary containing configuration data of a device.
+ """
+ # TODO(b/126931820): Genericize and move to lib when generic DUT interface is implemented
+ data = {'device_class': device.__class__.__name__}
+
+ if device.__class__.__name__ == 'AndroidDevice':
+ # TODO(b/124066126): Add remaining config data
+ data = {'device_class': 'phone',
+ 'device_model': device.model,
+ 'android_release_id': device.build_info['build_id'],
+ 'android_build_type': device.build_info['build_type'],
+ 'android_build_number': device.build_info[
+ 'incremental_build_id'],
+ 'android_branch_name': 'git_qt-release',
+ 'software_version': device.build_info['build_id']}
+
+ if device.__class__.__name__ == 'ParentDevice':
+ data = {'device_class': 'headset',
+ 'device_model': device.dut_type,
+ 'software_version': device.get_version()[1][
+ 'Fw Build Label'],
+ 'android_build_number': device.version}
+
+ return data
+
+ def add_config_data_to_proto(self, proto, pri_device, conn_device=None):
+ """Add to configuration data field of proto.
+
+ Adds test start time and device configuration info.
+ Args:
+ proto: protobuf to add configuration data to.
+ pri_device: some controller object.
+ conn_device: optional second controller object.
+ """
+ pri_device_proto = proto.configuration_data.primary_device
+ conn_device_proto = proto.configuration_data.connected_device
+ proto.configuration_data.test_date_time = self.start_time
+
+ pri_config = self.get_configuration_data(pri_device)
+
+ for metric in dir(pri_device_proto):
+ if metric in pri_config:
+ setattr(pri_device_proto, metric, pri_config[metric])
+
+ if conn_device:
+ conn_config = self.get_configuration_data(conn_device)
+
+ for metric in dir(conn_device_proto):
+ if metric in conn_config:
+ setattr(conn_device_proto, metric, conn_config[metric])
+
+ def get_proto_dict(self, test, proto):
+ """Return dict with proto, readable ascii proto, and test name."""
+ return {'proto': base64.b64encode(ProtoMetric(test, proto)
+ .get_binary()).decode('utf-8'),
+ 'proto_ascii': ProtoMetric(test, proto).get_ascii(),
+ 'test_name': test}
+
+ def add_proto_to_results(self, proto, test):
+ """Adds proto as ProtoMetric object to self.results."""
+ self.results.append(ProtoMetric(test, proto))
+
+ def get_results(self, results, test, pri_device, conn_device=None):
+ """Gets the metrics associated with each test case.
+
+ Gets the test case metrics and configuration data for each test case and
+ stores them for publishing.
+
+ Args:
+ results: A dictionary containing test metrics.
+ test: The name of the test case associated with these results.
+ pri_device: The primary AndroidDevice object for the test.
+ conn_device: The connected AndroidDevice object for the test, if
+ applicable.
+
+ """
+
+ proto_result = self.proto_map[test]
+ recursive_assign(proto_result, results)
+ self.add_config_data_to_proto(proto_result, pri_device, conn_device)
+ self.add_proto_to_results(proto_result, test)
+ return self.get_proto_dict(test, proto_result)
+
+ def end(self, event):
+ return self.publisher.publish(self.results)
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/protos/__init__.py b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric.proto b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric.proto
new file mode 100644
index 0000000..572f277
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric.proto
@@ -0,0 +1,104 @@
+syntax = "proto2";
+
+package wireless.android.platform.testing.bluetooth.metrics;
+
+message BluetoothTestDevice {
+ optional string device_class = 1;
+ optional string device_model = 2;
+ optional string hardware_version = 3;
+ optional string software_version = 4;
+ optional string android_build_type = 5;
+ optional string android_branch_name = 6;
+ optional string android_build_number = 7;
+ optional string android_release_id = 8;
+}
+
+message BluetoothContinuousTestResultHeader {
+ optional int64 test_date_time = 1;
+ optional BluetoothTestDevice primary_device = 2;
+ optional BluetoothTestDevice connected_device = 3;
+}
+
+message BluetoothReconnectTestResult {
+ optional BluetoothContinuousTestResultHeader configuration_data = 1;
+ optional int32 connection_attempt_count = 2;
+ optional int32 connection_successful_count = 3;
+ optional int32 connection_failed_count = 4;
+ optional int32 connection_max_time_millis = 5;
+ optional int32 connection_min_time_millis = 6;
+ optional int32 connection_avg_time_millis = 7;
+ optional int32 acl_connection_max_time_millis = 8;
+ optional int32 acl_connection_min_time_millis = 9;
+ optional int32 acl_connection_avg_time_millis = 10;
+}
+
+message BluetoothPairAndConnectTestResult {
+ optional BluetoothContinuousTestResultHeader configuration_data = 1;
+ optional int32 pair_attempt_count = 2;
+ optional int32 pair_successful_count = 3;
+ optional int32 pair_failed_count = 4;
+ optional int32 pair_max_time_millis = 5;
+ optional int32 pair_min_time_millis = 6;
+ optional int32 pair_avg_time_millis = 7;
+ optional int32 first_connection_max_time_millis = 8;
+ optional int32 first_connection_min_time_millis = 9;
+ optional int32 first_connection_avg_time_millis = 10;
+}
+
+message BluetoothA2dpCodecConfig {
+ enum BluetoothA2dpCodec {
+ SBC = 0;
+ AAC = 1;
+ APTX = 2;
+ APTX_HD = 3;
+ LDAC = 4;
+ }
+ optional BluetoothA2dpCodec codec_type = 1;
+ optional int32 sample_rate = 2;
+ optional int32 bits_per_sample = 3;
+ optional int32 channel_mode = 4;
+}
+
+message AudioTestDataPoint {
+ optional int64 timestamp_since_beginning_of_test_millis = 1;
+ optional int64 audio_streaming_duration_millis = 2;
+ optional int32 attenuation_db = 3;
+ optional float total_harmonic_distortion_plus_noise_percent = 4;
+ optional int32 audio_glitches_count = 5;
+}
+
+message BluetoothAudioTestResult {
+ optional BluetoothContinuousTestResultHeader configuration_data = 1;
+ enum AudioProfile {
+ A2DP = 0;
+ HFP = 1;
+ HAP = 2;
+ }
+ optional AudioProfile audio_profile = 2;
+ optional int32 audio_latency_min_millis = 3;
+ optional int32 audio_latency_max_millis = 4;
+ optional int32 audio_latency_avg_millis = 5;
+ optional int32 audio_glitches_count = 6;
+ optional int32 audio_missed_packets_count = 7;
+ optional float total_harmonic_distortion_plus_noise = 8;
+ optional int64 audio_streaming_duration_millis = 9;
+ optional BluetoothA2dpCodecConfig a2dp_codec_config = 10;
+ repeated AudioTestDataPoint data_points = 11;
+}
+
+message BluetoothDataTestResult {
+ optional BluetoothContinuousTestResultHeader configuration_data = 1;
+ enum DataTransferProtocol {
+ RFCOMM = 0;
+ L2CAP = 1;
+ LE_COC = 2;
+ }
+ optional DataTransferProtocol data_transfer_protocol = 2;
+ optional int32 data_latency_min_millis = 3;
+ optional int32 data_latency_max_millis = 4;
+ optional int32 data_latency_avg_millis = 5;
+ optional int32 data_throughput_min_bytes_per_second = 6;
+ optional int32 data_throughput_max_bytes_per_second = 7;
+ optional int32 data_throughput_avg_bytes_per_second = 8;
+ optional int32 data_packet_size = 9;
+}
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py
new file mode 100644
index 0000000..d3c0a9f
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py
@@ -0,0 +1,831 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: bluetooth_metric.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='bluetooth_metric.proto',
+ package='wireless.android.platform.testing.bluetooth.metrics',
+ syntax='proto2',
+ serialized_options=None,
+ create_key=_descriptor._internal_create_key,
+ serialized_pb=b'\n\x16\x62luetooth_metric.proto\x12\x33wireless.android.platform.testing.bluetooth.metrics\"\xe8\x01\n\x13\x42luetoothTestDevice\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x02 \x01(\t\x12\x18\n\x10hardware_version\x18\x03 \x01(\t\x12\x18\n\x10software_version\x18\x04 \x01(\t\x12\x1a\n\x12\x61ndroid_build_type\x18\x05 \x01(\t\x12\x1b\n\x13\x61ndroid_branch_name\x18\x06 \x01(\t\x12\x1c\n\x14\x61ndroid_build_number\x18\x07 \x01(\t\x12\x1a\n\x12\x61ndroid_release_id\x18\x08 \x01(\t\"\x83\x02\n#BluetoothContinuousTestResultHeader\x12\x16\n\x0etest_date_time\x18\x01 \x01(\x03\x12`\n\x0eprimary_device\x18\x02 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\x12\x62\n\x10\x63onnected_device\x18\x03 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\"\xe0\x03\n\x1c\x42luetoothReconnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12 \n\x18\x63onnection_attempt_count\x18\x02 \x01(\x05\x12#\n\x1b\x63onnection_successful_count\x18\x03 \x01(\x05\x12\x1f\n\x17\x63onnection_failed_count\x18\x04 \x01(\x05\x12\"\n\x1a\x63onnection_max_time_millis\x18\x05 \x01(\x05\x12\"\n\x1a\x63onnection_min_time_millis\x18\x06 \x01(\x05\x12\"\n\x1a\x63onnection_avg_time_millis\x18\x07 \x01(\x05\x12&\n\x1e\x61\x63l_connection_max_time_millis\x18\x08 \x01(\x05\x12&\n\x1e\x61\x63l_connection_min_time_millis\x18\t \x01(\x05\x12&\n\x1e\x61\x63l_connection_avg_time_millis\x18\n \x01(\x05\"\xc7\x03\n!BluetoothPairAndConnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x1a\n\x12pair_attempt_count\x18\x02 \x01(\x05\x12\x1d\n\x15pair_successful_count\x18\x03 \x01(\x05\x12\x19\n\x11pair_failed_count\x18\x04 \x01(\x05\x12\x1c\n\x14pair_max_time_millis\x18\x05 \x01(\x05\x12\x1c\n\x14pair_min_time_millis\x18\x06 \x01(\x05\x12\x1c\n\x14pair_avg_time_millis\x18\x07 \x01(\x05\x12(\n first_connection_max_time_millis\x18\x08 \x01(\x05\x12(\n first_connection_min_time_millis\x18\t \x01(\x05\x12(\n first_connection_avg_time_millis\x18\n \x01(\x05\"\x9d\x02\n\x18\x42luetoothA2dpCodecConfig\x12t\n\ncodec_type\x18\x01 \x01(\x0e\x32`.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.BluetoothA2dpCodec\x12\x13\n\x0bsample_rate\x18\x02 \x01(\x05\x12\x17\n\x0f\x62its_per_sample\x18\x03 \x01(\x05\x12\x14\n\x0c\x63hannel_mode\x18\x04 \x01(\x05\"G\n\x12\x42luetoothA2dpCodec\x12\x07\n\x03SBC\x10\x00\x12\x07\n\x03\x41\x41\x43\x10\x01\x12\x08\n\x04\x41PTX\x10\x02\x12\x0b\n\x07\x41PTX_HD\x10\x03\x12\x08\n\x04LDAC\x10\x04\"\xdb\x01\n\x12\x41udioTestDataPoint\x12\x30\n(timestamp_since_beginning_of_test_millis\x18\x01 \x01(\x03\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\x02 \x01(\x03\x12\x16\n\x0e\x61ttenuation_db\x18\x03 \x01(\x05\x12\x34\n,total_harmonic_distortion_plus_noise_percent\x18\x04 \x01(\x02\x12\x1c\n\x14\x61udio_glitches_count\x18\x05 \x01(\x05\"\xf6\x05\n\x18\x42luetoothAudioTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12q\n\raudio_profile\x18\x02 \x01(\x0e\x32Z.wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.AudioProfile\x12 \n\x18\x61udio_latency_min_millis\x18\x03 \x01(\x05\x12 \n\x18\x61udio_latency_max_millis\x18\x04 \x01(\x05\x12 \n\x18\x61udio_latency_avg_millis\x18\x05 \x01(\x05\x12\x1c\n\x14\x61udio_glitches_count\x18\x06 \x01(\x05\x12\"\n\x1a\x61udio_missed_packets_count\x18\x07 \x01(\x05\x12,\n$total_harmonic_distortion_plus_noise\x18\x08 \x01(\x02\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\t \x01(\x03\x12h\n\x11\x61\x32\x64p_codec_config\x18\n \x01(\x0b\x32M.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig\x12\\\n\x0b\x64\x61ta_points\x18\x0b \x03(\x0b\x32G.wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint\"*\n\x0c\x41udioProfile\x12\x08\n\x04\x41\x32\x44P\x10\x00\x12\x07\n\x03HFP\x10\x01\x12\x07\n\x03HAP\x10\x02\"\xd5\x04\n\x17\x42luetoothDataTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x81\x01\n\x16\x64\x61ta_transfer_protocol\x18\x02 \x01(\x0e\x32\x61.wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.DataTransferProtocol\x12\x1f\n\x17\x64\x61ta_latency_min_millis\x18\x03 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_max_millis\x18\x04 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_avg_millis\x18\x05 \x01(\x05\x12,\n$data_throughput_min_bytes_per_second\x18\x06 \x01(\x05\x12,\n$data_throughput_max_bytes_per_second\x18\x07 \x01(\x05\x12,\n$data_throughput_avg_bytes_per_second\x18\x08 \x01(\x05\x12\x18\n\x10\x64\x61ta_packet_size\x18\t \x01(\x05\"9\n\x14\x44\x61taTransferProtocol\x12\n\n\x06RFCOMM\x10\x00\x12\t\n\x05L2CAP\x10\x01\x12\n\n\x06LE_COC\x10\x02'
+)
+
+
+
+_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC = _descriptor.EnumDescriptor(
+ name='BluetoothA2dpCodec',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.BluetoothA2dpCodec',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='SBC', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='AAC', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='APTX', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='APTX_HD', index=3, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='LDAC', index=4, number=4,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=1732,
+ serialized_end=1803,
+)
+_sym_db.RegisterEnumDescriptor(_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC)
+
+_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE = _descriptor.EnumDescriptor(
+ name='AudioProfile',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.AudioProfile',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='A2DP', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HFP', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HAP', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=2744,
+ serialized_end=2786,
+)
+_sym_db.RegisterEnumDescriptor(_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE)
+
+_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL = _descriptor.EnumDescriptor(
+ name='DataTransferProtocol',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.DataTransferProtocol',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='RFCOMM', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='L2CAP', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='LE_COC', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=3329,
+ serialized_end=3386,
+)
+_sym_db.RegisterEnumDescriptor(_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL)
+
+
+_BLUETOOTHTESTDEVICE = _descriptor.Descriptor(
+ name='BluetoothTestDevice',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='device_class', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.device_class', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='device_model', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.device_model', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='hardware_version', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.hardware_version', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='software_version', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.software_version', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='android_build_type', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_build_type', index=4,
+ number=5, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='android_branch_name', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_branch_name', index=5,
+ number=6, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='android_build_number', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_build_number', index=6,
+ number=7, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='android_release_id', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_release_id', index=7,
+ number=8, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=80,
+ serialized_end=312,
+)
+
+
+_BLUETOOTHCONTINUOUSTESTRESULTHEADER = _descriptor.Descriptor(
+ name='BluetoothContinuousTestResultHeader',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='test_date_time', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.test_date_time', index=0,
+ number=1, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='primary_device', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.primary_device', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connected_device', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.connected_device', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=315,
+ serialized_end=574,
+)
+
+
+_BLUETOOTHRECONNECTTESTRESULT = _descriptor.Descriptor(
+ name='BluetoothReconnectTestResult',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.configuration_data', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_attempt_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_attempt_count', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_successful_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_successful_count', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_failed_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_failed_count', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_max_time_millis', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_min_time_millis', index=5,
+ number=6, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_avg_time_millis', index=6,
+ number=7, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='acl_connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_max_time_millis', index=7,
+ number=8, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='acl_connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_min_time_millis', index=8,
+ number=9, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='acl_connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_avg_time_millis', index=9,
+ number=10, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=577,
+ serialized_end=1057,
+)
+
+
+_BLUETOOTHPAIRANDCONNECTTESTRESULT = _descriptor.Descriptor(
+ name='BluetoothPairAndConnectTestResult',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.configuration_data', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_attempt_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_attempt_count', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_successful_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_successful_count', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_failed_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_failed_count', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_max_time_millis', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_min_time_millis', index=5,
+ number=6, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_avg_time_millis', index=6,
+ number=7, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='first_connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_max_time_millis', index=7,
+ number=8, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='first_connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_min_time_millis', index=8,
+ number=9, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='first_connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_avg_time_millis', index=9,
+ number=10, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1060,
+ serialized_end=1515,
+)
+
+
+_BLUETOOTHA2DPCODECCONFIG = _descriptor.Descriptor(
+ name='BluetoothA2dpCodecConfig',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='codec_type', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.codec_type', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='sample_rate', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.sample_rate', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='bits_per_sample', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.bits_per_sample', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='channel_mode', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.channel_mode', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1518,
+ serialized_end=1803,
+)
+
+
+_AUDIOTESTDATAPOINT = _descriptor.Descriptor(
+ name='AudioTestDataPoint',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='timestamp_since_beginning_of_test_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.timestamp_since_beginning_of_test_millis', index=0,
+ number=1, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_streaming_duration_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.audio_streaming_duration_millis', index=1,
+ number=2, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='attenuation_db', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.attenuation_db', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='total_harmonic_distortion_plus_noise_percent', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.total_harmonic_distortion_plus_noise_percent', index=3,
+ number=4, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_glitches_count', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.audio_glitches_count', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1806,
+ serialized_end=2025,
+)
+
+
+_BLUETOOTHAUDIOTESTRESULT = _descriptor.Descriptor(
+ name='BluetoothAudioTestResult',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.configuration_data', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_profile', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_profile', index=1,
+ number=2, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_latency_min_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_min_millis', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_latency_max_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_max_millis', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_latency_avg_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_avg_millis', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_glitches_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_glitches_count', index=5,
+ number=6, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_missed_packets_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_missed_packets_count', index=6,
+ number=7, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='total_harmonic_distortion_plus_noise', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.total_harmonic_distortion_plus_noise', index=7,
+ number=8, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_streaming_duration_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_streaming_duration_millis', index=8,
+ number=9, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='a2dp_codec_config', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.a2dp_codec_config', index=9,
+ number=10, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_points', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.data_points', index=10,
+ number=11, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2028,
+ serialized_end=2786,
+)
+
+
+_BLUETOOTHDATATESTRESULT = _descriptor.Descriptor(
+ name='BluetoothDataTestResult',
+ full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.configuration_data', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_transfer_protocol', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_transfer_protocol', index=1,
+ number=2, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_latency_min_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_min_millis', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_latency_max_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_max_millis', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_latency_avg_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_avg_millis', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_throughput_min_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_min_bytes_per_second', index=5,
+ number=6, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_throughput_max_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_max_bytes_per_second', index=6,
+ number=7, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_throughput_avg_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_avg_bytes_per_second', index=7,
+ number=8, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='data_packet_size', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_packet_size', index=8,
+ number=9, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2789,
+ serialized_end=3386,
+)
+
+_BLUETOOTHCONTINUOUSTESTRESULTHEADER.fields_by_name['primary_device'].message_type = _BLUETOOTHTESTDEVICE
+_BLUETOOTHCONTINUOUSTESTRESULTHEADER.fields_by_name['connected_device'].message_type = _BLUETOOTHTESTDEVICE
+_BLUETOOTHRECONNECTTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
+_BLUETOOTHPAIRANDCONNECTTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
+_BLUETOOTHA2DPCODECCONFIG.fields_by_name['codec_type'].enum_type = _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC
+_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC.containing_type = _BLUETOOTHA2DPCODECCONFIG
+_BLUETOOTHAUDIOTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
+_BLUETOOTHAUDIOTESTRESULT.fields_by_name['audio_profile'].enum_type = _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE
+_BLUETOOTHAUDIOTESTRESULT.fields_by_name['a2dp_codec_config'].message_type = _BLUETOOTHA2DPCODECCONFIG
+_BLUETOOTHAUDIOTESTRESULT.fields_by_name['data_points'].message_type = _AUDIOTESTDATAPOINT
+_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE.containing_type = _BLUETOOTHAUDIOTESTRESULT
+_BLUETOOTHDATATESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
+_BLUETOOTHDATATESTRESULT.fields_by_name['data_transfer_protocol'].enum_type = _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL
+_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL.containing_type = _BLUETOOTHDATATESTRESULT
+DESCRIPTOR.message_types_by_name['BluetoothTestDevice'] = _BLUETOOTHTESTDEVICE
+DESCRIPTOR.message_types_by_name['BluetoothContinuousTestResultHeader'] = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
+DESCRIPTOR.message_types_by_name['BluetoothReconnectTestResult'] = _BLUETOOTHRECONNECTTESTRESULT
+DESCRIPTOR.message_types_by_name['BluetoothPairAndConnectTestResult'] = _BLUETOOTHPAIRANDCONNECTTESTRESULT
+DESCRIPTOR.message_types_by_name['BluetoothA2dpCodecConfig'] = _BLUETOOTHA2DPCODECCONFIG
+DESCRIPTOR.message_types_by_name['AudioTestDataPoint'] = _AUDIOTESTDATAPOINT
+DESCRIPTOR.message_types_by_name['BluetoothAudioTestResult'] = _BLUETOOTHAUDIOTESTRESULT
+DESCRIPTOR.message_types_by_name['BluetoothDataTestResult'] = _BLUETOOTHDATATESTRESULT
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+BluetoothTestDevice = _reflection.GeneratedProtocolMessageType('BluetoothTestDevice', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHTESTDEVICE,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice)
+ })
+_sym_db.RegisterMessage(BluetoothTestDevice)
+
+BluetoothContinuousTestResultHeader = _reflection.GeneratedProtocolMessageType('BluetoothContinuousTestResultHeader', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHCONTINUOUSTESTRESULTHEADER,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader)
+ })
+_sym_db.RegisterMessage(BluetoothContinuousTestResultHeader)
+
+BluetoothReconnectTestResult = _reflection.GeneratedProtocolMessageType('BluetoothReconnectTestResult', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHRECONNECTTESTRESULT,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult)
+ })
+_sym_db.RegisterMessage(BluetoothReconnectTestResult)
+
+BluetoothPairAndConnectTestResult = _reflection.GeneratedProtocolMessageType('BluetoothPairAndConnectTestResult', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHPAIRANDCONNECTTESTRESULT,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult)
+ })
+_sym_db.RegisterMessage(BluetoothPairAndConnectTestResult)
+
+BluetoothA2dpCodecConfig = _reflection.GeneratedProtocolMessageType('BluetoothA2dpCodecConfig', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHA2DPCODECCONFIG,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig)
+ })
+_sym_db.RegisterMessage(BluetoothA2dpCodecConfig)
+
+AudioTestDataPoint = _reflection.GeneratedProtocolMessageType('AudioTestDataPoint', (_message.Message,), {
+ 'DESCRIPTOR' : _AUDIOTESTDATAPOINT,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint)
+ })
+_sym_db.RegisterMessage(AudioTestDataPoint)
+
+BluetoothAudioTestResult = _reflection.GeneratedProtocolMessageType('BluetoothAudioTestResult', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHAUDIOTESTRESULT,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult)
+ })
+_sym_db.RegisterMessage(BluetoothAudioTestResult)
+
+BluetoothDataTestResult = _reflection.GeneratedProtocolMessageType('BluetoothDataTestResult', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHDATATESTRESULT,
+ '__module__' : 'bluetooth_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult)
+ })
+_sym_db.RegisterMessage(BluetoothDataTestResult)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/bt/native_bt_test_utils.py b/acts_tests/acts_contrib/test_utils/bt/native_bt_test_utils.py
new file mode 100644
index 0000000..2568249
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/native_bt_test_utils.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import logging
+
+from subprocess import call
+import time
+
+log = logging
+
+
+def setup_native_bluetooth(native_devices):
+ for n in native_devices:
+ droid = n.droid
+ pid = n.adb.shell("pidof -s bluetoothtbd")
+ if not pid:
+ call(
+ ["adb -s " + n.serial + " shell sh -c \"bluetoothtbd\" &"],
+ shell=True)
+ droid.BtBinderInitInterface()
+ time.sleep(5) #temporary sleep statement
+ droid.BtBinderEnable()
+ time.sleep(5) #temporary sleep statement
+ droid.BtBinderRegisterBLE()
+ time.sleep(5) #temporary sleep statement
diff --git a/acts_tests/acts_contrib/test_utils/bt/protos/__init__.py b/acts_tests/acts_contrib/test_utils/bt/protos/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/protos/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth.proto b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth.proto
new file mode 100644
index 0000000..46ab968
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth.proto
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+// C++ namespace: bluetooth::metrics::BluetoothMetricsProto
+package bluetooth.metrics.BluetoothMetricsProto;
+
+option java_package = "com.android.bluetooth";
+option java_outer_classname = "BluetoothMetricsProto";
+
+message BluetoothLog {
+ // Session information that gets logged for every BT connection.
+ repeated BluetoothSession session = 1;
+
+ // Session information that gets logged for every Pair event.
+ repeated PairEvent pair_event = 2;
+
+ // Information for Wake locks.
+ repeated WakeEvent wake_event = 3;
+
+ // Scan event information.
+ repeated ScanEvent scan_event = 4;
+
+ // Number of bonded devices.
+ optional int32 num_bonded_devices = 5;
+
+ // Number of BluetoothSession including discarded ones beyond capacity
+ optional int64 num_bluetooth_session = 6;
+
+ // Number of PairEvent including discarded ones beyond capacity
+ optional int64 num_pair_event = 7;
+
+ // Number of WakeEvent including discarded ones beyond capacity
+ optional int64 num_wake_event = 8;
+
+ // Number of ScanEvent including discarded ones beyond capacity
+ optional int64 num_scan_event = 9;
+
+ // Statistics about Bluetooth profile connections
+ repeated ProfileConnectionStats profile_connection_stats = 10;
+
+ // Statistics about Headset profile connections
+ repeated HeadsetProfileConnectionStats headset_profile_connection_stats = 11;
+}
+
+// The information about the device.
+message DeviceInfo {
+ // Device type.
+ enum DeviceType {
+ // Type is unknown.
+ DEVICE_TYPE_UNKNOWN = 0;
+
+ DEVICE_TYPE_BREDR = 1;
+
+ DEVICE_TYPE_LE = 2;
+
+ DEVICE_TYPE_DUMO = 3;
+ }
+
+ // Device class
+ // https://cs.corp.google.com/#android/system/bt/stack/include/btm_api.h&q=major_computer.
+ optional int32 device_class = 1;
+
+ // Device type.
+ optional DeviceType device_type = 2;
+}
+
+// Information that gets logged for every Bluetooth connection.
+message BluetoothSession {
+ // Type of technology used in the connection.
+ enum ConnectionTechnologyType {
+ CONNECTION_TECHNOLOGY_TYPE_UNKNOWN = 0;
+
+ CONNECTION_TECHNOLOGY_TYPE_LE = 1;
+
+ CONNECTION_TECHNOLOGY_TYPE_BREDR = 2;
+ }
+
+ enum DisconnectReasonType {
+ UNKNOWN = 0;
+
+ // A metrics dump takes a snapshot of current Bluetooth session and thus
+ // is not a real disconnect, but a discontinuation in metrics logging.
+ // This enum indicates this situation.
+ METRICS_DUMP = 1;
+
+ NEXT_START_WITHOUT_END_PREVIOUS = 2;
+ }
+
+ // Duration of the session.
+ optional int64 session_duration_sec = 2;
+
+ // Technology type.
+ optional ConnectionTechnologyType connection_technology_type = 3;
+
+ // Reason for disconnecting.
+ optional string disconnect_reason = 4 [deprecated = true];
+
+ // The information about the device which it is connected to.
+ optional DeviceInfo device_connected_to = 5;
+
+ // The information about the RFComm session.
+ optional RFCommSession rfcomm_session = 6;
+
+ // The information about the A2DP audio session.
+ optional A2DPSession a2dp_session = 7;
+
+ // Numeric reason for disconnecting as defined in metrics.h
+ optional DisconnectReasonType disconnect_reason_type = 8;
+}
+
+message RFCommSession {
+ // bytes transmitted.
+ optional int32 rx_bytes = 1;
+
+ // bytes transmitted.
+ optional int32 tx_bytes = 2;
+}
+
+enum A2dpSourceCodec {
+ A2DP_SOURCE_CODEC_UNKNOWN = 0;
+ A2DP_SOURCE_CODEC_SBC = 1;
+ A2DP_SOURCE_CODEC_AAC = 2;
+ A2DP_SOURCE_CODEC_APTX = 3;
+ A2DP_SOURCE_CODEC_APTX_HD = 4;
+ A2DP_SOURCE_CODEC_LDAC = 5;
+}
+
+// Session information that gets logged for A2DP session.
+message A2DPSession {
+ // Media timer in milliseconds.
+ optional int32 media_timer_min_millis = 1;
+
+ // Media timer in milliseconds.
+ optional int32 media_timer_max_millis = 2;
+
+ // Media timer in milliseconds.
+ optional int32 media_timer_avg_millis = 3;
+
+ // Buffer overruns count.
+ optional int32 buffer_overruns_max_count = 4;
+
+ // Buffer overruns total.
+ optional int32 buffer_overruns_total = 5;
+
+ // Buffer underruns average.
+ optional float buffer_underruns_average = 6;
+
+ // Buffer underruns count.
+ optional int32 buffer_underruns_count = 7;
+
+ // Total audio time in this A2DP session
+ optional int64 audio_duration_millis = 8;
+
+ // Audio codec used in this A2DP session in A2DP source role
+ optional A2dpSourceCodec source_codec = 9;
+
+ // Whether A2DP offload is enabled in this A2DP session
+ optional bool is_a2dp_offload = 10;
+}
+
+message PairEvent {
+ // The reason for disconnecting
+ // See: system/bt/stack/include/hcidefs.h, HCI_ERR_CONN_FAILED_ESTABLISHMENT
+ optional int32 disconnect_reason = 1;
+
+ // Pair event time
+ optional int64 event_time_millis =
+ 2; // [(datapol.semantic_type) = ST_TIMESTAMP];
+
+ // The information about the device which it is paired to.
+ optional DeviceInfo device_paired_with = 3;
+}
+
+message WakeEvent {
+ // Information about the wake event type.
+ enum WakeEventType {
+ UNKNOWN = 0;
+ // WakeLock was acquired.
+ ACQUIRED = 1;
+ // WakeLock was released.
+ RELEASED = 2;
+ }
+
+ // Information about the wake event type.
+ optional WakeEventType wake_event_type = 1;
+
+ // Initiator of the scan. Only the first three names will be stored.
+ // e.g. com.company.app
+ optional string requestor = 2;
+
+ // Name of the wakelock (e.g. bluedroid_timer).
+ optional string name = 3;
+
+ // Time of the event.
+ optional int64 event_time_millis =
+ 4; // [(datapol.semantic_type) = ST_TIMESTAMP];
+}
+
+message ScanEvent {
+ // Scan type.
+ enum ScanTechnologyType {
+ SCAN_TYPE_UNKNOWN = 0;
+
+ SCAN_TECH_TYPE_LE = 1;
+
+ SCAN_TECH_TYPE_BREDR = 2;
+
+ SCAN_TECH_TYPE_BOTH = 3;
+ }
+
+ // Scan event type.
+ enum ScanEventType {
+ // Scan started.
+ SCAN_EVENT_START = 0;
+ // Scan stopped.
+ SCAN_EVENT_STOP = 1;
+ }
+
+ // Scan event type.
+ optional ScanEventType scan_event_type = 1;
+
+ // Initiator of the scan. Only the first three names will be stored.
+ // e.g. com.company.app
+ optional string initiator = 2;
+
+ // Technology used for scanning.
+ optional ScanTechnologyType scan_technology_type = 3;
+
+ // Number of results returned.
+ optional int32 number_results = 4;
+
+ // Time of the event.
+ optional int64 event_time_millis =
+ 5; // [(datapol.semantic_type) = ST_TIMESTAMP];
+}
+
+// Profile IDs defined in BluetoothProfile API class
+// Values must match API class values
+enum ProfileId {
+ PROFILE_UNKNOWN = 0;
+ HEADSET = 1;
+ A2DP = 2;
+ HEALTH = 3;
+ HID_HOST = 4;
+ PAN = 5;
+ PBAP = 6;
+ GATT = 7;
+ GATT_SERVER = 8;
+ MAP = 9;
+ SAP = 10;
+ A2DP_SINK = 11;
+ AVRCP_CONTROLLER = 12;
+ AVRCP = 13;
+ HEADSET_CLIENT = 16;
+ PBAP_CLIENT = 17;
+ MAP_CLIENT = 18;
+ HID_DEVICE = 19;
+ OPP = 20;
+ HEARING_AID = 21;
+}
+
+// Statistics about Bluetooth profile connections
+message ProfileConnectionStats {
+ // Profile id defined in BluetoothProfile.java
+ optional ProfileId profile_id = 1;
+
+ // Number of times that this profile is connected since last metrics dump
+ optional int32 num_times_connected = 2;
+}
+
+enum HeadsetProfileType {
+ HEADSET_PROFILE_UNKNOWN = 0;
+ HSP = 1;
+ HFP = 2;
+}
+
+// Statistics about headset profile connections
+message HeadsetProfileConnectionStats {
+ // Type of headset profile connected
+ optional HeadsetProfileType headset_profile_type = 1;
+
+ // Number of times this type of headset profile is connected
+ optional int32 num_times_connected = 2;
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py
new file mode 100644
index 0000000..bce90e4
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py
@@ -0,0 +1,1140 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: bluetooth.proto
+"""Generated protocol buffer code."""
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='bluetooth.proto',
+ package='bluetooth.metrics.BluetoothMetricsProto',
+ syntax='proto2',
+ serialized_options=b'\n\025com.android.bluetoothB\025BluetoothMetricsProtoH\003',
+ create_key=_descriptor._internal_create_key,
+ serialized_pb=b'\n\x0f\x62luetooth.proto\x12\'bluetooth.metrics.BluetoothMetricsProto\"\x8a\x05\n\x0c\x42luetoothLog\x12J\n\x07session\x18\x01 \x03(\x0b\x32\x39.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession\x12\x46\n\npair_event\x18\x02 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.PairEvent\x12\x46\n\nwake_event\x18\x03 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.WakeEvent\x12\x46\n\nscan_event\x18\x04 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ScanEvent\x12\x1a\n\x12num_bonded_devices\x18\x05 \x01(\x05\x12\x1d\n\x15num_bluetooth_session\x18\x06 \x01(\x03\x12\x16\n\x0enum_pair_event\x18\x07 \x01(\x03\x12\x16\n\x0enum_wake_event\x18\x08 \x01(\x03\x12\x16\n\x0enum_scan_event\x18\t \x01(\x03\x12\x61\n\x18profile_connection_stats\x18\n \x03(\x0b\x32?.bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats\x12p\n headset_profile_connection_stats\x18\x0b \x03(\x0b\x32\x46.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats\"\xdf\x01\n\nDeviceInfo\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\x05\x12S\n\x0b\x64\x65vice_type\x18\x02 \x01(\x0e\x32>.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.DeviceType\"f\n\nDeviceType\x12\x17\n\x13\x44\x45VICE_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11\x44\x45VICE_TYPE_BREDR\x10\x01\x12\x12\n\x0e\x44\x45VICE_TYPE_LE\x10\x02\x12\x14\n\x10\x44\x45VICE_TYPE_DUMO\x10\x03\"\x8f\x06\n\x10\x42luetoothSession\x12\x1c\n\x14session_duration_sec\x18\x02 \x01(\x03\x12v\n\x1a\x63onnection_technology_type\x18\x03 \x01(\x0e\x32R.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.ConnectionTechnologyType\x12\x1d\n\x11\x64isconnect_reason\x18\x04 \x01(\tB\x02\x18\x01\x12P\n\x13\x64\x65vice_connected_to\x18\x05 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\x12N\n\x0erfcomm_session\x18\x06 \x01(\x0b\x32\x36.bluetooth.metrics.BluetoothMetricsProto.RFCommSession\x12J\n\x0c\x61\x32\x64p_session\x18\x07 \x01(\x0b\x32\x34.bluetooth.metrics.BluetoothMetricsProto.A2DPSession\x12n\n\x16\x64isconnect_reason_type\x18\x08 \x01(\x0e\x32N.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.DisconnectReasonType\"\x8b\x01\n\x18\x43onnectionTechnologyType\x12&\n\"CONNECTION_TECHNOLOGY_TYPE_UNKNOWN\x10\x00\x12!\n\x1d\x43ONNECTION_TECHNOLOGY_TYPE_LE\x10\x01\x12$\n CONNECTION_TECHNOLOGY_TYPE_BREDR\x10\x02\"Z\n\x14\x44isconnectReasonType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0cMETRICS_DUMP\x10\x01\x12#\n\x1fNEXT_START_WITHOUT_END_PREVIOUS\x10\x02\"3\n\rRFCommSession\x12\x10\n\x08rx_bytes\x18\x01 \x01(\x05\x12\x10\n\x08tx_bytes\x18\x02 \x01(\x05\"\xf9\x02\n\x0b\x41\x32\x44PSession\x12\x1e\n\x16media_timer_min_millis\x18\x01 \x01(\x05\x12\x1e\n\x16media_timer_max_millis\x18\x02 \x01(\x05\x12\x1e\n\x16media_timer_avg_millis\x18\x03 \x01(\x05\x12!\n\x19\x62uffer_overruns_max_count\x18\x04 \x01(\x05\x12\x1d\n\x15\x62uffer_overruns_total\x18\x05 \x01(\x05\x12 \n\x18\x62uffer_underruns_average\x18\x06 \x01(\x02\x12\x1e\n\x16\x62uffer_underruns_count\x18\x07 \x01(\x05\x12\x1d\n\x15\x61udio_duration_millis\x18\x08 \x01(\x03\x12N\n\x0csource_codec\x18\t \x01(\x0e\x32\x38.bluetooth.metrics.BluetoothMetricsProto.A2dpSourceCodec\x12\x17\n\x0fis_a2dp_offload\x18\n \x01(\x08\"\x92\x01\n\tPairEvent\x12\x19\n\x11\x64isconnect_reason\x18\x01 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x02 \x01(\x03\x12O\n\x12\x64\x65vice_paired_with\x18\x03 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\"\xdc\x01\n\tWakeEvent\x12Y\n\x0fwake_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.WakeEvent.WakeEventType\x12\x11\n\trequestor\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x19\n\x11\x65vent_time_millis\x18\x04 \x01(\x03\"8\n\rWakeEventType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x41\x43QUIRED\x10\x01\x12\x0c\n\x08RELEASED\x10\x02\"\xc4\x03\n\tScanEvent\x12Y\n\x0fscan_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanEventType\x12\x11\n\tinitiator\x18\x02 \x01(\t\x12\x63\n\x14scan_technology_type\x18\x03 \x01(\x0e\x32\x45.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanTechnologyType\x12\x16\n\x0enumber_results\x18\x04 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x05 \x01(\x03\"u\n\x12ScanTechnologyType\x12\x15\n\x11SCAN_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11SCAN_TECH_TYPE_LE\x10\x01\x12\x18\n\x14SCAN_TECH_TYPE_BREDR\x10\x02\x12\x17\n\x13SCAN_TECH_TYPE_BOTH\x10\x03\":\n\rScanEventType\x12\x14\n\x10SCAN_EVENT_START\x10\x00\x12\x13\n\x0fSCAN_EVENT_STOP\x10\x01\"}\n\x16ProfileConnectionStats\x12\x46\n\nprofile_id\x18\x01 \x01(\x0e\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ProfileId\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05\"\x97\x01\n\x1dHeadsetProfileConnectionStats\x12Y\n\x14headset_profile_type\x18\x01 \x01(\x0e\x32;.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileType\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05*\xbd\x01\n\x0f\x41\x32\x64pSourceCodec\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_UNKNOWN\x10\x00\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_SBC\x10\x01\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_AAC\x10\x02\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_APTX\x10\x03\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_APTX_HD\x10\x04\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_LDAC\x10\x05*\xa0\x02\n\tProfileId\x12\x13\n\x0fPROFILE_UNKNOWN\x10\x00\x12\x0b\n\x07HEADSET\x10\x01\x12\x08\n\x04\x41\x32\x44P\x10\x02\x12\n\n\x06HEALTH\x10\x03\x12\x0c\n\x08HID_HOST\x10\x04\x12\x07\n\x03PAN\x10\x05\x12\x08\n\x04PBAP\x10\x06\x12\x08\n\x04GATT\x10\x07\x12\x0f\n\x0bGATT_SERVER\x10\x08\x12\x07\n\x03MAP\x10\t\x12\x07\n\x03SAP\x10\n\x12\r\n\tA2DP_SINK\x10\x0b\x12\x14\n\x10\x41VRCP_CONTROLLER\x10\x0c\x12\t\n\x05\x41VRCP\x10\r\x12\x12\n\x0eHEADSET_CLIENT\x10\x10\x12\x0f\n\x0bPBAP_CLIENT\x10\x11\x12\x0e\n\nMAP_CLIENT\x10\x12\x12\x0e\n\nHID_DEVICE\x10\x13\x12\x07\n\x03OPP\x10\x14\x12\x0f\n\x0bHEARING_AID\x10\x15*C\n\x12HeadsetProfileType\x12\x1b\n\x17HEADSET_PROFILE_UNKNOWN\x10\x00\x12\x07\n\x03HSP\x10\x01\x12\x07\n\x03HFP\x10\x02\x42\x30\n\x15\x63om.android.bluetoothB\x15\x42luetoothMetricsProtoH\x03'
+)
+
+_A2DPSOURCECODEC = _descriptor.EnumDescriptor(
+ name='A2dpSourceCodec',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.A2dpSourceCodec',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_SBC', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_AAC', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_APTX', index=3, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_APTX_HD', index=4, number=4,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SOURCE_CODEC_LDAC', index=5, number=5,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=3267,
+ serialized_end=3456,
+)
+_sym_db.RegisterEnumDescriptor(_A2DPSOURCECODEC)
+
+A2dpSourceCodec = enum_type_wrapper.EnumTypeWrapper(_A2DPSOURCECODEC)
+_PROFILEID = _descriptor.EnumDescriptor(
+ name='ProfileId',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileId',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='PROFILE_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HEADSET', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HEALTH', index=3, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HID_HOST', index=4, number=4,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='PAN', index=5, number=5,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='PBAP', index=6, number=6,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='GATT', index=7, number=7,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='GATT_SERVER', index=8, number=8,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='MAP', index=9, number=9,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SAP', index=10, number=10,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='A2DP_SINK', index=11, number=11,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='AVRCP_CONTROLLER', index=12, number=12,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='AVRCP', index=13, number=13,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HEADSET_CLIENT', index=14, number=16,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='PBAP_CLIENT', index=15, number=17,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='MAP_CLIENT', index=16, number=18,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HID_DEVICE', index=17, number=19,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='OPP', index=18, number=20,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HEARING_AID', index=19, number=21,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=3459,
+ serialized_end=3747,
+)
+_sym_db.RegisterEnumDescriptor(_PROFILEID)
+
+ProfileId = enum_type_wrapper.EnumTypeWrapper(_PROFILEID)
+_HEADSETPROFILETYPE = _descriptor.EnumDescriptor(
+ name='HeadsetProfileType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='HEADSET_PROFILE_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HSP', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='HFP', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=3749,
+ serialized_end=3816,
+)
+_sym_db.RegisterEnumDescriptor(_HEADSETPROFILETYPE)
+
+HeadsetProfileType = enum_type_wrapper.EnumTypeWrapper(_HEADSETPROFILETYPE)
+A2DP_SOURCE_CODEC_UNKNOWN = 0
+A2DP_SOURCE_CODEC_SBC = 1
+A2DP_SOURCE_CODEC_AAC = 2
+A2DP_SOURCE_CODEC_APTX = 3
+A2DP_SOURCE_CODEC_APTX_HD = 4
+A2DP_SOURCE_CODEC_LDAC = 5
+PROFILE_UNKNOWN = 0
+HEADSET = 1
+A2DP = 2
+HEALTH = 3
+HID_HOST = 4
+PAN = 5
+PBAP = 6
+GATT = 7
+GATT_SERVER = 8
+MAP = 9
+SAP = 10
+A2DP_SINK = 11
+AVRCP_CONTROLLER = 12
+AVRCP = 13
+HEADSET_CLIENT = 16
+PBAP_CLIENT = 17
+MAP_CLIENT = 18
+HID_DEVICE = 19
+OPP = 20
+HEARING_AID = 21
+HEADSET_PROFILE_UNKNOWN = 0
+HSP = 1
+HFP = 2
+
+
+_DEVICEINFO_DEVICETYPE = _descriptor.EnumDescriptor(
+ name='DeviceType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.DeviceType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='DEVICE_TYPE_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='DEVICE_TYPE_BREDR', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='DEVICE_TYPE_LE', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='DEVICE_TYPE_DUMO', index=3, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=835,
+ serialized_end=937,
+)
+_sym_db.RegisterEnumDescriptor(_DEVICEINFO_DEVICETYPE)
+
+_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE = _descriptor.EnumDescriptor(
+ name='ConnectionTechnologyType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.ConnectionTechnologyType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='CONNECTION_TECHNOLOGY_TYPE_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CONNECTION_TECHNOLOGY_TYPE_LE', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CONNECTION_TECHNOLOGY_TYPE_BREDR', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=1492,
+ serialized_end=1631,
+)
+_sym_db.RegisterEnumDescriptor(_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE)
+
+_BLUETOOTHSESSION_DISCONNECTREASONTYPE = _descriptor.EnumDescriptor(
+ name='DisconnectReasonType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.DisconnectReasonType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='METRICS_DUMP', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='NEXT_START_WITHOUT_END_PREVIOUS', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=1633,
+ serialized_end=1723,
+)
+_sym_db.RegisterEnumDescriptor(_BLUETOOTHSESSION_DISCONNECTREASONTYPE)
+
+_WAKEEVENT_WAKEEVENTTYPE = _descriptor.EnumDescriptor(
+ name='WakeEventType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.WakeEventType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='ACQUIRED', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='RELEASED', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=2472,
+ serialized_end=2528,
+)
+_sym_db.RegisterEnumDescriptor(_WAKEEVENT_WAKEEVENTTYPE)
+
+_SCANEVENT_SCANTECHNOLOGYTYPE = _descriptor.EnumDescriptor(
+ name='ScanTechnologyType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanTechnologyType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_TYPE_UNKNOWN', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_TECH_TYPE_LE', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_TECH_TYPE_BREDR', index=2, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_TECH_TYPE_BOTH', index=3, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=2806,
+ serialized_end=2923,
+)
+_sym_db.RegisterEnumDescriptor(_SCANEVENT_SCANTECHNOLOGYTYPE)
+
+_SCANEVENT_SCANEVENTTYPE = _descriptor.EnumDescriptor(
+ name='ScanEventType',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanEventType',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_EVENT_START', index=0, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SCAN_EVENT_STOP', index=1, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=2925,
+ serialized_end=2983,
+)
+_sym_db.RegisterEnumDescriptor(_SCANEVENT_SCANEVENTTYPE)
+
+
+_BLUETOOTHLOG = _descriptor.Descriptor(
+ name='BluetoothLog',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.session', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pair_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.pair_event', index=1,
+ number=2, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='wake_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.wake_event', index=2,
+ number=3, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='scan_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.scan_event', index=3,
+ number=4, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_bonded_devices', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_bonded_devices', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_bluetooth_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_bluetooth_session', index=5,
+ number=6, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_pair_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_pair_event', index=6,
+ number=7, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_wake_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_wake_event', index=7,
+ number=8, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_scan_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_scan_event', index=8,
+ number=9, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='profile_connection_stats', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.profile_connection_stats', index=9,
+ number=10, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='headset_profile_connection_stats', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.headset_profile_connection_stats', index=10,
+ number=11, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=61,
+ serialized_end=711,
+)
+
+
+_DEVICEINFO = _descriptor.Descriptor(
+ name='DeviceInfo',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='device_class', full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.device_class', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='device_type', full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.device_type', index=1,
+ number=2, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _DEVICEINFO_DEVICETYPE,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=714,
+ serialized_end=937,
+)
+
+
+_BLUETOOTHSESSION = _descriptor.Descriptor(
+ name='BluetoothSession',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='session_duration_sec', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.session_duration_sec', index=0,
+ number=2, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='connection_technology_type', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.connection_technology_type', index=1,
+ number=3, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='disconnect_reason', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.disconnect_reason', index=2,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=b'\030\001', file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='device_connected_to', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.device_connected_to', index=3,
+ number=5, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='rfcomm_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.rfcomm_session', index=4,
+ number=6, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='a2dp_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.a2dp_session', index=5,
+ number=7, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='disconnect_reason_type', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.disconnect_reason_type', index=6,
+ number=8, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE,
+ _BLUETOOTHSESSION_DISCONNECTREASONTYPE,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=940,
+ serialized_end=1723,
+)
+
+
+_RFCOMMSESSION = _descriptor.Descriptor(
+ name='RFCommSession',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='rx_bytes', full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession.rx_bytes', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='tx_bytes', full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession.tx_bytes', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1725,
+ serialized_end=1776,
+)
+
+
+_A2DPSESSION = _descriptor.Descriptor(
+ name='A2DPSession',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='media_timer_min_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_min_millis', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='media_timer_max_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_max_millis', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='media_timer_avg_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_avg_millis', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='buffer_overruns_max_count', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_overruns_max_count', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='buffer_overruns_total', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_overruns_total', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='buffer_underruns_average', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_underruns_average', index=5,
+ number=6, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='buffer_underruns_count', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_underruns_count', index=6,
+ number=7, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='audio_duration_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.audio_duration_millis', index=7,
+ number=8, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='source_codec', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.source_codec', index=8,
+ number=9, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='is_a2dp_offload', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.is_a2dp_offload', index=9,
+ number=10, type=8, cpp_type=7, label=1,
+ has_default_value=False, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1779,
+ serialized_end=2156,
+)
+
+
+_PAIREVENT = _descriptor.Descriptor(
+ name='PairEvent',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='disconnect_reason', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.disconnect_reason', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.event_time_millis', index=1,
+ number=2, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='device_paired_with', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.device_paired_with', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2159,
+ serialized_end=2305,
+)
+
+
+_WAKEEVENT = _descriptor.Descriptor(
+ name='WakeEvent',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='wake_event_type', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.wake_event_type', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='requestor', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.requestor', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='name', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.name', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.event_time_millis', index=3,
+ number=4, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _WAKEEVENT_WAKEEVENTTYPE,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2308,
+ serialized_end=2528,
+)
+
+
+_SCANEVENT = _descriptor.Descriptor(
+ name='ScanEvent',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='scan_event_type', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.scan_event_type', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='initiator', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.initiator', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='scan_technology_type', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.scan_technology_type', index=2,
+ number=3, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='number_results', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.number_results', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.event_time_millis', index=4,
+ number=5, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _SCANEVENT_SCANTECHNOLOGYTYPE,
+ _SCANEVENT_SCANEVENTTYPE,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2531,
+ serialized_end=2983,
+)
+
+
+_PROFILECONNECTIONSTATS = _descriptor.Descriptor(
+ name='ProfileConnectionStats',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='profile_id', full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats.profile_id', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_times_connected', full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats.num_times_connected', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2985,
+ serialized_end=3110,
+)
+
+
+_HEADSETPROFILECONNECTIONSTATS = _descriptor.Descriptor(
+ name='HeadsetProfileConnectionStats',
+ full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='headset_profile_type', full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats.headset_profile_type', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='num_times_connected', full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats.num_times_connected', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=3113,
+ serialized_end=3264,
+)
+
+_BLUETOOTHLOG.fields_by_name['session'].message_type = _BLUETOOTHSESSION
+_BLUETOOTHLOG.fields_by_name['pair_event'].message_type = _PAIREVENT
+_BLUETOOTHLOG.fields_by_name['wake_event'].message_type = _WAKEEVENT
+_BLUETOOTHLOG.fields_by_name['scan_event'].message_type = _SCANEVENT
+_BLUETOOTHLOG.fields_by_name['profile_connection_stats'].message_type = _PROFILECONNECTIONSTATS
+_BLUETOOTHLOG.fields_by_name['headset_profile_connection_stats'].message_type = _HEADSETPROFILECONNECTIONSTATS
+_DEVICEINFO.fields_by_name['device_type'].enum_type = _DEVICEINFO_DEVICETYPE
+_DEVICEINFO_DEVICETYPE.containing_type = _DEVICEINFO
+_BLUETOOTHSESSION.fields_by_name['connection_technology_type'].enum_type = _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE
+_BLUETOOTHSESSION.fields_by_name['device_connected_to'].message_type = _DEVICEINFO
+_BLUETOOTHSESSION.fields_by_name['rfcomm_session'].message_type = _RFCOMMSESSION
+_BLUETOOTHSESSION.fields_by_name['a2dp_session'].message_type = _A2DPSESSION
+_BLUETOOTHSESSION.fields_by_name['disconnect_reason_type'].enum_type = _BLUETOOTHSESSION_DISCONNECTREASONTYPE
+_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE.containing_type = _BLUETOOTHSESSION
+_BLUETOOTHSESSION_DISCONNECTREASONTYPE.containing_type = _BLUETOOTHSESSION
+_A2DPSESSION.fields_by_name['source_codec'].enum_type = _A2DPSOURCECODEC
+_PAIREVENT.fields_by_name['device_paired_with'].message_type = _DEVICEINFO
+_WAKEEVENT.fields_by_name['wake_event_type'].enum_type = _WAKEEVENT_WAKEEVENTTYPE
+_WAKEEVENT_WAKEEVENTTYPE.containing_type = _WAKEEVENT
+_SCANEVENT.fields_by_name['scan_event_type'].enum_type = _SCANEVENT_SCANEVENTTYPE
+_SCANEVENT.fields_by_name['scan_technology_type'].enum_type = _SCANEVENT_SCANTECHNOLOGYTYPE
+_SCANEVENT_SCANTECHNOLOGYTYPE.containing_type = _SCANEVENT
+_SCANEVENT_SCANEVENTTYPE.containing_type = _SCANEVENT
+_PROFILECONNECTIONSTATS.fields_by_name['profile_id'].enum_type = _PROFILEID
+_HEADSETPROFILECONNECTIONSTATS.fields_by_name['headset_profile_type'].enum_type = _HEADSETPROFILETYPE
+DESCRIPTOR.message_types_by_name['BluetoothLog'] = _BLUETOOTHLOG
+DESCRIPTOR.message_types_by_name['DeviceInfo'] = _DEVICEINFO
+DESCRIPTOR.message_types_by_name['BluetoothSession'] = _BLUETOOTHSESSION
+DESCRIPTOR.message_types_by_name['RFCommSession'] = _RFCOMMSESSION
+DESCRIPTOR.message_types_by_name['A2DPSession'] = _A2DPSESSION
+DESCRIPTOR.message_types_by_name['PairEvent'] = _PAIREVENT
+DESCRIPTOR.message_types_by_name['WakeEvent'] = _WAKEEVENT
+DESCRIPTOR.message_types_by_name['ScanEvent'] = _SCANEVENT
+DESCRIPTOR.message_types_by_name['ProfileConnectionStats'] = _PROFILECONNECTIONSTATS
+DESCRIPTOR.message_types_by_name['HeadsetProfileConnectionStats'] = _HEADSETPROFILECONNECTIONSTATS
+DESCRIPTOR.enum_types_by_name['A2dpSourceCodec'] = _A2DPSOURCECODEC
+DESCRIPTOR.enum_types_by_name['ProfileId'] = _PROFILEID
+DESCRIPTOR.enum_types_by_name['HeadsetProfileType'] = _HEADSETPROFILETYPE
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+BluetoothLog = _reflection.GeneratedProtocolMessageType('BluetoothLog', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHLOG,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.BluetoothLog)
+ })
+_sym_db.RegisterMessage(BluetoothLog)
+
+DeviceInfo = _reflection.GeneratedProtocolMessageType('DeviceInfo', (_message.Message,), {
+ 'DESCRIPTOR' : _DEVICEINFO,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.DeviceInfo)
+ })
+_sym_db.RegisterMessage(DeviceInfo)
+
+BluetoothSession = _reflection.GeneratedProtocolMessageType('BluetoothSession', (_message.Message,), {
+ 'DESCRIPTOR' : _BLUETOOTHSESSION,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.BluetoothSession)
+ })
+_sym_db.RegisterMessage(BluetoothSession)
+
+RFCommSession = _reflection.GeneratedProtocolMessageType('RFCommSession', (_message.Message,), {
+ 'DESCRIPTOR' : _RFCOMMSESSION,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.RFCommSession)
+ })
+_sym_db.RegisterMessage(RFCommSession)
+
+A2DPSession = _reflection.GeneratedProtocolMessageType('A2DPSession', (_message.Message,), {
+ 'DESCRIPTOR' : _A2DPSESSION,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.A2DPSession)
+ })
+_sym_db.RegisterMessage(A2DPSession)
+
+PairEvent = _reflection.GeneratedProtocolMessageType('PairEvent', (_message.Message,), {
+ 'DESCRIPTOR' : _PAIREVENT,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.PairEvent)
+ })
+_sym_db.RegisterMessage(PairEvent)
+
+WakeEvent = _reflection.GeneratedProtocolMessageType('WakeEvent', (_message.Message,), {
+ 'DESCRIPTOR' : _WAKEEVENT,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.WakeEvent)
+ })
+_sym_db.RegisterMessage(WakeEvent)
+
+ScanEvent = _reflection.GeneratedProtocolMessageType('ScanEvent', (_message.Message,), {
+ 'DESCRIPTOR' : _SCANEVENT,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.ScanEvent)
+ })
+_sym_db.RegisterMessage(ScanEvent)
+
+ProfileConnectionStats = _reflection.GeneratedProtocolMessageType('ProfileConnectionStats', (_message.Message,), {
+ 'DESCRIPTOR' : _PROFILECONNECTIONSTATS,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats)
+ })
+_sym_db.RegisterMessage(ProfileConnectionStats)
+
+HeadsetProfileConnectionStats = _reflection.GeneratedProtocolMessageType('HeadsetProfileConnectionStats', (_message.Message,), {
+ 'DESCRIPTOR' : _HEADSETPROFILECONNECTIONSTATS,
+ '__module__' : 'bluetooth_pb2'
+ # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats)
+ })
+_sym_db.RegisterMessage(HeadsetProfileConnectionStats)
+
+
+DESCRIPTOR._options = None
+_BLUETOOTHSESSION.fields_by_name['disconnect_reason']._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ics_lib.py b/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ics_lib.py
new file mode 100644
index 0000000..f2f9b2c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ics_lib.py
@@ -0,0 +1,365 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""This is a placeholder for all ICS values in PTS
+ that matter to Fuchsia devices.
+"""
+
+# A2DP Values are just a placeholder.
+A2DP_ICS = {
+ b'TSPC_ALL': b'FALSE',
+ b'TSPC_A2DP_0_1': b'FALSE',
+ b'TSPC_A2DP_0_2': b'FALSE',
+ b'TSPC_A2DP_0_3': b'FALSE',
+ b'TSPC_A2DP_1_1': b'TRUE',
+ b'TSPC_A2DP_1_2': b'TRUE',
+ b'TSPC_A2DP_2_1': b'TRUE',
+ b'TSPC_A2DP_2a_1': b'FALSE',
+ b'TSPC_A2DP_2a_2': b'TRUE',
+ b'TSPC_A2DP_2a_3': b'FALSE',
+ b'TSPC_A2DP_2b_1': b'FALSE',
+ b'TSPC_A2DP_2b_2': b'FALSE',
+ b'TSPC_A2DP_2_2': b'TRUE',
+ b'TSPC_A2DP_2_3': b'TRUE',
+ b'TSPC_A2DP_2_4': b'TRUE',
+ b'TSPC_A2DP_2_5': b'TRUE',
+ b'TSPC_A2DP_2_6': b'TRUE',
+ b'TSPC_A2DP_2_7': b'TRUE',
+ b'TSPC_A2DP_2_8': b'FALSE',
+ b'TSPC_A2DP_2_9': b'FALSE',
+ b'TSPC_A2DP_2_10': b'TRUE',
+ b'TSPC_A2DP_2_10a': b'FALSE',
+ b'TSPC_A2DP_2_11': b'FALSE',
+ b'TSPC_A2DP_2_12': b'FALSE',
+ b'TSPC_A2DP_2_13': b'TRUE',
+ b'TSPC_A2DP_2_14': b'TRUE',
+ b'TSPC_A2DP_2_15': b'FALSE',
+ b'TSPC_A2DP_2_16': b'FALSE',
+ b'TSPC_A2DP_2_17': b'FALSE',
+ b'TSPC_A2DP_3_1': b'TRUE',
+ b'TSPC_A2DP_3_1a': b'FALSE',
+ b'TSPC_A2DP_3_2': b'TRUE',
+ b'TSPC_A2DP_3_3': b'FALSE',
+ b'TSPC_A2DP_3_4': b'FALSE',
+ b'TSPC_A2DP_3_5': b'TRUE',
+ b'TSPC_A2DP_3_6': b'FALSE',
+ b'TSPC_A2DP_3_7': b'FALSE',
+ b'TSPC_A2DP_3_8': b'FALSE',
+ b'TSPC_A2DP_3a_1': b'TRUE',
+ b'TSPC_A2DP_3a_2': b'FALSE',
+ b'TSPC_A2DP_3a_3': b'TRUE',
+ b'TSPC_A2DP_3a_4': b'TRUE',
+ b'TSPC_A2DP_3a_5': b'TRUE',
+ b'TSPC_A2DP_3a_6': b'TRUE',
+ b'TSPC_A2DP_3a_7': b'TRUE',
+ b'TSPC_A2DP_3a_8': b'TRUE',
+ b'TSPC_A2DP_3a_9': b'FALSE',
+ b'TSPC_A2DP_3a_10': b'TRUE',
+ b'TSPC_A2DP_3a_11': b'FALSE',
+ b'TSPC_A2DP_3a_12': b'TRUE',
+ b'TSPC_A2DP_4_1': b'TRUE',
+ b'TSPC_A2DP_4_2': b'TRUE',
+ b'TSPC_A2DP_4_3': b'FALSE',
+ b'TSPC_A2DP_4_4': b'TRUE',
+ b'TSPC_A2DP_4_5': b'TRUE',
+ b'TSPC_A2DP_4_6': b'FALSE',
+ b'TSPC_A2DP_4_7': b'TRUE',
+ b'TSPC_A2DP_4_8': b'FALSE',
+ b'TSPC_A2DP_4_9': b'TRUE',
+ b'TSPC_A2DP_4_10': b'TRUE',
+ b'TSPC_A2DP_4_10a': b'FALSE',
+ b'TSPC_A2DP_4_11': b'FALSE',
+ b'TSPC_A2DP_4_12': b'FALSE',
+ b'TSPC_A2DP_4_13': b'TRUE',
+ b'TSPC_A2DP_4_14': b'TRUE',
+ b'TSPC_A2DP_4_15': b'FALSE',
+ b'TSPC_A2DP_5_1': b'TRUE',
+ b'TSPC_A2DP_5_1a': b'TRUE',
+ b'TSPC_A2DP_5_2': b'TRUE',
+ b'TSPC_A2DP_5_3': b'FALSE',
+ b'TSPC_A2DP_5_4': b'FALSE',
+ b'TSPC_A2DP_5_5': b'FALSE',
+ b'TSPC_A2DP_5a_1': b'TRUE',
+ b'TSPC_A2DP_5a_2': b'TRUE',
+ b'TSPC_A2DP_5a_3': b'TRUE',
+ b'TSPC_A2DP_5a_4': b'TRUE',
+ b'TSPC_A2DP_5a_5': b'TRUE',
+ b'TSPC_A2DP_5a_6': b'TRUE',
+ b'TSPC_A2DP_5a_7': b'TRUE',
+ b'TSPC_A2DP_5a_8': b'TRUE',
+ b'TSPC_A2DP_5a_9': b'TRUE',
+ b'TSPC_A2DP_5a_10': b'TRUE',
+ b'TSPC_A2DP_5a_11': b'TRUE',
+ b'TSPC_A2DP_5a_12': b'TRUE',
+ b'TSPC_A2DP_7a_1': b'FALSE',
+ b'TSPC_A2DP_7a_2': b'FALSE',
+ b'TSPC_A2DP_7a_3': b'FALSE',
+ b'TSPC_A2DP_7b_1': b'FALSE',
+ b'TSPC_A2DP_7b_2': b'FALSE',
+
+ # Not available in Launch Studio Yet
+ b'TSPC_A2DP_10_1': b'FALSE',
+ b'TSPC_A2DP_10_2': b'FALSE',
+ b'TSPC_A2DP_10_3': b'FALSE',
+ b'TSPC_A2DP_10_4': b'FALSE',
+ b'TSPC_A2DP_10_5': b'FALSE',
+ b'TSPC_A2DP_10_6': b'FALSE',
+ b'TSPC_A2DP_11_1': b'FALSE',
+ b'TSPC_A2DP_11_2': b'FALSE',
+ b'TSPC_A2DP_11_3': b'FALSE',
+ b'TSPC_A2DP_11_4': b'FALSE',
+ b'TSPC_A2DP_11_5': b'FALSE',
+ b'TSPC_A2DP_11_6': b'FALSE',
+ b'TSPC_A2DP_12_2': b'FALSE',
+ b'TSPC_A2DP_12_3': b'FALSE',
+ b'TSPC_A2DP_12_3': b'FALSE',
+ b'TSPC_A2DP_12_4': b'FALSE',
+ b'TSPC_A2DP_13_1': b'FALSE',
+ b'TSPC_A2DP_13_2': b'FALSE',
+ b'TSPC_A2DP_13_3': b'FALSE',
+ b'TSPC_A2DP_13_4': b'FALSE',
+ b'TSPC_A2DP_14_1': b'FALSE',
+ b'TSPC_A2DP_14_2': b'FALSE',
+ b'TSPC_A2DP_14_3': b'FALSE',
+ b'TSPC_A2DP_14_4': b'FALSE',
+ b'TSPC_A2DP_14_5': b'FALSE',
+ b'TSPC_A2DP_15_1': b'FALSE',
+ b'TSPC_A2DP_15_2': b'FALSE',
+ b'TSPC_A2DP_15_3': b'FALSE',
+ b'TSPC_A2DP_15_4': b'FALSE',
+ b'TSPC_A2DP_15_5': b'FALSE',
+ b'TSPC_A2DP_15_6': b'FALSE',
+ b'TSPC_A2DP_3_2a': b'FALSE',
+ b'TSPC_A2DP_3_2b': b'FALSE',
+ b'TSPC_A2DP_3_2c': b'FALSE',
+ b'TSPC_A2DP_3_2d': b'FALSE',
+ b'TSPC_A2DP_3_2e': b'FALSE',
+ b'TSPC_A2DP_3_2f': b'FALSE',
+ b'TSPC_A2DP_5_2a': b'FALSE',
+ b'TSPC_A2DP_5_2b': b'FALSE',
+ b'TSPC_A2DP_5_2c': b'FALSE',
+ b'TSPC_A2DP_8_2': b'FALSE',
+ b'TSPC_A2DP_8_3': b'FALSE',
+ b'TSPC_A2DP_8_4': b'FALSE',
+ b'TSPC_A2DP_9_1': b'FALSE',
+ b'TSPC_A2DP_9_2': b'FALSE',
+ b'TSPC_A2DP_9_3': b'FALSE',
+ b'TSPC_A2DP_9_4': b'FALSE',
+
+}
+
+
+GATT_ICS = {
+ b'TSPC_GATT_1_1': b'TRUE',
+ b'TSPC_GATT_1_2': b'TRUE',
+ b'TSPC_GATT_1a_1': b'TRUE',
+ b'TSPC_GATT_1a_2': b'TRUE',
+ b'TSPC_GATT_1a_3': b'TRUE',
+ b'TSPC_GATT_1a_4': b'TRUE',
+ b'TSPC_GATT_1a_5': b'FALSE',
+ b'TSPC_GATT_1a_6': b'FALSE',
+ b'TSPC_GATT_1a_7': b'FALSE',
+ b'TSPC_GATT_1a_8': b'FALSE',
+ b'TSPC_GATT_2_1': b'FALSE',
+ b'TSPC_GATT_2_2': b'TRUE',
+ b'TSPC_GATT_3_1': b'TRUE',
+ b'TSPC_GATT_3_2': b'TRUE',
+ b'TSPC_GATT_3_3': b'TRUE',
+ b'TSPC_GATT_3_4': b'TRUE',
+ b'TSPC_GATT_3_5': b'TRUE',
+ b'TSPC_GATT_3_6': b'FALSE',
+ b'TSPC_GATT_3_7': b'TRUE',
+ b'TSPC_GATT_3_8': b'TRUE',
+ b'TSPC_GATT_3_9': b'TRUE',
+ b'TSPC_GATT_3_10': b'TRUE',
+ b'TSPC_GATT_3_11': b'FALSE',
+ b'TSPC_GATT_3_12': b'TRUE',
+ b'TSPC_GATT_3_13': b'FALSE',
+ b'TSPC_GATT_3_14': b'TRUE',
+ b'TSPC_GATT_3_15': b'TRUE',
+ b'TSPC_GATT_3_16': b'TRUE',
+ b'TSPC_GATT_3_17': b'TRUE',
+ b'TSPC_GATT_3_18': b'TRUE',
+ b'TSPC_GATT_3_19': b'TRUE',
+ b'TSPC_GATT_3_20': b'TRUE',
+ b'TSPC_GATT_3_21': b'TRUE',
+ b'TSPC_GATT_3_22': b'TRUE',
+ b'TSPC_GATT_3_23': b'TRUE',
+ b'TSPC_GATT_3_24': b'FALSE',
+ b'TSPC_GATT_3_25': b'FALSE',
+ b'TSPC_GATT_3_26': b'FALSE',
+ b'TSPC_GATT_3B_1': b'FALSE',
+ b'TSPC_GATT_3B_2': b'FALSE',
+ b'TSPC_GATT_3B_3': b'FALSE',
+ b'TSPC_GATT_3B_4': b'FALSE',
+ b'TSPC_GATT_3B_5': b'FALSE',
+ b'TSPC_GATT_3B_6': b'FALSE',
+ b'TSPC_GATT_3B_7': b'FALSE',
+ b'TSPC_GATT_3B_8': b'FALSE',
+ b'TSPC_GATT_3B_9': b'FALSE',
+ b'TSPC_GATT_3B_10': b'FALSE',
+ b'TSPC_GATT_3B_11': b'FALSE',
+ b'TSPC_GATT_3B_12': b'FALSE',
+ b'TSPC_GATT_3B_13': b'FALSE',
+ b'TSPC_GATT_3B_14': b'FALSE',
+ b'TSPC_GATT_3B_15': b'FALSE',
+ b'TSPC_GATT_3B_16': b'FALSE',
+ b'TSPC_GATT_3B_17': b'FALSE',
+ b'TSPC_GATT_3B_18': b'FALSE',
+ b'TSPC_GATT_3B_19': b'FALSE',
+ b'TSPC_GATT_3B_20': b'FALSE',
+ b'TSPC_GATT_3B_21': b'FALSE',
+ b'TSPC_GATT_3B_22': b'FALSE',
+ b'TSPC_GATT_3B_23': b'FALSE',
+ b'TSPC_GATT_3B_24': b'FALSE',
+ b'TSPC_GATT_3B_25': b'FALSE',
+ b'TSPC_GATT_3B_26': b'FALSE',
+ b'TSPC_GATT_3B_27': b'FALSE',
+ b'TSPC_GATT_3B_28': b'FALSE',
+ b'TSPC_GATT_3B_29': b'FALSE',
+ b'TSPC_GATT_3B_30': b'FALSE',
+ b'TSPC_GATT_3B_31': b'FALSE',
+ b'TSPC_GATT_3B_32': b'FALSE',
+ b'TSPC_GATT_3B_33': b'FALSE',
+ b'TSPC_GATT_3B_34': b'FALSE',
+ b'TSPC_GATT_3B_35': b'FALSE',
+ b'TSPC_GATT_3B_36': b'FALSE',
+ b'TSPC_GATT_3B_37': b'FALSE',
+ b'TSPC_GATT_3B_38': b'FALSE',
+ b'TSPC_GATT_4_1': b'TRUE',
+ b'TSPC_GATT_4_2': b'TRUE',
+ b'TSPC_GATT_4_3': b'TRUE',
+ b'TSPC_GATT_4_4': b'TRUE',
+ b'TSPC_GATT_4_5': b'TRUE',
+ b'TSPC_GATT_4_6': b'TRUE',
+ b'TSPC_GATT_4_7': b'TRUE',
+ b'TSPC_GATT_4_8': b'TRUE',
+ b'TSPC_GATT_4_9': b'TRUE',
+ b'TSPC_GATT_4_10': b'TRUE',
+ b'TSPC_GATT_4_11': b'FALSE',
+ b'TSPC_GATT_4_12': b'TRUE',
+ b'TSPC_GATT_4_13': b'FALSE',
+ b'TSPC_GATT_4_14': b'TRUE',
+ b'TSPC_GATT_4_15': b'TRUE',
+ b'TSPC_GATT_4_16': b'TRUE',
+ b'TSPC_GATT_4_17': b'TRUE',
+ b'TSPC_GATT_4_18': b'TRUE',
+ b'TSPC_GATT_4_19': b'TRUE',
+ b'TSPC_GATT_4_20': b'TRUE',
+ b'TSPC_GATT_4_21': b'TRUE',
+ b'TSPC_GATT_4_22': b'TRUE',
+ b'TSPC_GATT_4_23': b'TRUE',
+ b'TSPC_GATT_4_24': b'FALSE',
+ b'TSPC_GATT_4_25': b'FALSE',
+ b'TSPC_GATT_4_26': b'FALSE',
+ b'TSPC_GATT_4_27': b'FALSE',
+ b'TSPC_GATT_4B_1': b'FALSE',
+ b'TSPC_GATT_4B_2': b'FALSE',
+ b'TSPC_GATT_4B_3': b'FALSE',
+ b'TSPC_GATT_4B_4': b'FALSE',
+ b'TSPC_GATT_4B_5': b'FALSE',
+ b'TSPC_GATT_4B_6': b'FALSE',
+ b'TSPC_GATT_4B_7': b'FALSE',
+ b'TSPC_GATT_4B_8': b'FALSE',
+ b'TSPC_GATT_4B_9': b'FALSE',
+ b'TSPC_GATT_4B_10': b'FALSE',
+ b'TSPC_GATT_4B_11': b'FALSE',
+ b'TSPC_GATT_4B_12': b'FALSE',
+ b'TSPC_GATT_4B_13': b'FALSE',
+ b'TSPC_GATT_4B_14': b'FALSE',
+ b'TSPC_GATT_4B_15': b'FALSE',
+ b'TSPC_GATT_4B_16': b'FALSE',
+ b'TSPC_GATT_4B_17': b'FALSE',
+ b'TSPC_GATT_4B_18': b'FALSE',
+ b'TSPC_GATT_4B_19': b'FALSE',
+ b'TSPC_GATT_4B_20': b'FALSE',
+ b'TSPC_GATT_4B_21': b'FALSE',
+ b'TSPC_GATT_4B_22': b'FALSE',
+ b'TSPC_GATT_4B_23': b'FALSE',
+ b'TSPC_GATT_4B_24': b'FALSE',
+ b'TSPC_GATT_4B_25': b'FALSE',
+ b'TSPC_GATT_4B_26': b'FALSE',
+ b'TSPC_GATT_4B_27': b'FALSE',
+ b'TSPC_GATT_4B_28': b'FALSE',
+ b'TSPC_GATT_4B_29': b'FALSE',
+ b'TSPC_GATT_4B_30': b'FALSE',
+ b'TSPC_GATT_4B_31': b'FALSE',
+ b'TSPC_GATT_4B_32': b'FALSE',
+ b'TSPC_GATT_4B_33': b'FALSE',
+ b'TSPC_GATT_4B_34': b'FALSE',
+ b'TSPC_GATT_4B_35': b'FALSE',
+ b'TSPC_GATT_4B_36': b'FALSE',
+ b'TSPC_GATT_4B_37': b'FALSE',
+ b'TSPC_GATT_4B_38': b'FALSE',
+ b'TSPC_GATT_6_2': b'TRUE',
+ b'TSPC_GATT_6_3': b'TRUE',
+ b'TSPC_GATT_7_1': b'TRUE',
+ b'TSPC_GATT_7_2': b'TRUE',
+ b'TSPC_GATT_7_3': b'TRUE',
+ b'TSPC_GATT_7_4': b'TRUE',
+ b'TSPC_GATT_7_5': b'FALSE',
+ b'TSPC_GATT_7_6': b'FALSE',
+ b'TSPC_GATT_7_7': b'FALSE',
+ b'TSPC_GATT_8_1': b'TRUE',
+ b'TSPC_GAP_0_2': b'FALSE',
+ b'TSPC_GAP_24_2': b'TRUE',
+ b'TSPC_GAP_24_3': b'TRUE',
+ b'TSPC_GAP_34_2': b'TRUE',
+ b'TSPC_GAP_34_3': b'TRUE',
+ b'TSPC_ALL': b'FALSE',
+}
+
+
+SDP_ICS = {
+ b'TSPC_ALL': b'FALSE',
+ b'TSPC_SDP_1_1': b'TRUE',
+ b'TSPC_SDP_1_2': b'TRUE',
+ b'TSPC_SDP_1_3': b'TRUE',
+ b'TSPC_SDP_1b_1': b'TRUE',
+ b'TSPC_SDP_1b_2': b'TRUE',
+ b'TSPC_SDP_2_1': b'TRUE',
+ b'TSPC_SDP_2_2': b'TRUE',
+ b'TSPC_SDP_2_3': b'TRUE',
+ b'TSPC_SDP_3_1': b'TRUE',
+ b'TSPC_SDP_4_1': b'TRUE',
+ b'TSPC_SDP_4_2': b'TRUE',
+ b'TSPC_SDP_4_3': b'TRUE',
+ b'TSPC_SDP_5_1': b'TRUE',
+ b'TSPC_SDP_6_1': b'TRUE',
+ b'TSPC_SDP_6_2': b'TRUE',
+ b'TSPC_SDP_6_3': b'TRUE',
+ b'TSPC_SDP_7_1': b'TRUE',
+ b'TSPC_SDP_8_1': b'FALSE',
+ b'TSPC_SDP_8_2': b'FALSE',
+ b'TSPC_SDP_9_1': b'TRUE',
+ b'TSPC_SDP_9_2': b'TRUE',
+ b'TSPC_SDP_9_3': b'FALSE',
+ b'TSPC_SDP_9_4': b'FALSE',
+ b'TSPC_SDP_9_5': b'TRUE',
+ b'TSPC_SDP_9_6': b'TRUE',
+ b'TSPC_SDP_9_7': b'FALSE',
+ b'TSPC_SDP_9_8': b'FALSE',
+ b'TSPC_SDP_9_9': b'TRUE',
+ b'TSPC_SDP_9_10': b'TRUE',
+ b'TSPC_SDP_9_11': b'TRUE',
+ b'TSPC_SDP_9_12': b'FALSE',
+ b'TSPC_SDP_9_13': b'FALSE',
+ b'TSPC_SDP_9_14': b'TRUE',
+ b'TSPC_SDP_9_15': b'FALSE',
+ b'TSPC_SDP_9_16': b'FALSE',
+ b'TSPC_SDP_9_17': b'TRUE',
+ b'TSPC_SDP_9_18': b'TRUE',
+ b'TSPC_SDP_9_19': b'TRUE',
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ixit_lib.py b/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ixit_lib.py
new file mode 100644
index 0000000..b7d1c80
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/pts/fuchsia_pts_ixit_lib.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""This is a placeholder for all IXIT values in PTS
+ that matter to Fuchsia devices.
+"""
+
+A2DP_IXIT = {
+ b'TSPX_security_enabled': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_bd_addr_iut': (b'OCTETSTRING', b'000000000000'),
+ b'TSPX_SRC_class_of_device': (b'OCTETSTRING', b'080418'),
+ b'TSPX_SNK_class_of_device': (b'OCTETSTRING', b'04041C'),
+ b'TSPX_pin_code': (b'IA5STRING', b'0000'),
+ b'TSPX_delete_link_key': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_time_guard': (b'INTEGER', b'300000'),
+ b'TSPX_use_implicit_send': (b'BOOLEAN', b'TRUE'),
+ b'TSPX_media_directory':
+ (b'IA5STRING', b'C:\Program Files\Bluetooth SIG\Bluetooth PTS\\bin\\audio'),
+ b'TSPX_auth_password': (b'IA5STRING', b'0000'),
+ b'TSPX_auth_user_id': (b'IA5STRING', b'PTS'),
+ b'TSPX_rfcomm_channel': (b'INTEGER', b'8'),
+ b'TSPX_l2cap_psm': (b'OCTETSTRING', b'1011'),
+ b'TSPX_no_confirmations': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_cover_art_uuid': (b'OCTETSTRING', b'3EEE'),
+}
+
+GATT_IXIT = {
+ b'TSPX_bd_addr_iut': (b'OCTETSTRING', b'000000000000'),
+ b'TSPX_iut_device_name_in_adv_packet_for_random_address': (b'IA5STRING', b'tbd'),
+ b'TSPX_security_enabled': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_delete_link_key': (b'BOOLEAN', b'TRUE'),
+ b'TSPX_time_guard': (b'INTEGER', b'180000'),
+ b'TSPX_selected_handle': (b'OCTETSTRING', b'0012'),
+ b'TSPX_use_implicit_send': (b'BOOLEAN', b'TRUE'),
+ b'TSPX_secure_simple_pairing_pass_key_confirmation': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_iut_use_dynamic_bd_addr': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_iut_setup_att_over_br_edr': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_tester_database_file': (b'IA5STRING', b'C:\Program Files\Bluetooth SIG\Bluetooth PTS\Data\SIGDatabase\GATT_Qualification_Test_Databases.xml'),
+ b'TSPX_iut_is_client_periphral': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_iut_is_server_central': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_mtu_size': (b'INTEGER', b'23'),
+ b'TSPX_pin_code': (b'IA5STRING', b'0000'),
+ b'TSPX_use_dynamic_pin': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_delete_ltk': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_tester_appearance': (b'OCTETSTRING', b'0000'),
+}
+
+SDP_IXIT = {
+ b'TSPX_sdp_service_search_pattern': (b'IA5STRING', b'0100'),
+ b'TSPX_sdp_service_search_pattern_no_results': (b'IA5STRING', b'EEEE'),
+ b'TSPX_sdp_service_search_pattern_additional_protocol_descriptor_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_bluetooth_profile_descriptor_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_browse_group_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_client_exe_url': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_documentation_url': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_icon_url': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_language_base_attribute_id_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_protocol_descriptor_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_provider_name': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_availability': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_data_base_state': (b'IA5STRING', b'1000'),
+ b'TSPX_sdp_service_search_pattern_service_description': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_id': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_info_time_to_live': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_version_number_list': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_name': (b'IA5STRING', b''),
+ b'TSPX_sdp_service_search_pattern_service_record_state': (b'IA5STRING', b''),
+ b'TSPX_sdp_unsupported_attribute_id': (b'OCTETSTRING', b'EEEE'),
+ b'TSPX_security_enabled': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_delete_link_key': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_bd_addr_iut': (b'OCTETSTRING', b''),
+ b'TSPX_class_of_device_pts': (b'OCTETSTRING', b'200404'),
+ b'TSPX_class_of_device_test_pts_initiator': (b'BOOLEAN', b'TRUE'),
+ b'TSPX_limited_inquiry_used': (b'BOOLEAN', b'FALSE'),
+ b'TSPX_pin_code': (b'IA5STRING', b'0000'),
+ b'TSPX_time_guard': (b'INTEGER', b'200000'),
+ b'TSPX_device_search_time': (b'INTEGER', b'20'),
+ b'TSPX_use_implicit_send': (b'BOOLEAN', b'TRUE'),
+ b'TSPX_secure_simple_pairing_pass_key_confirmation': (b'BOOLEAN', b'FALSE'),
+}
diff --git a/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py b/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py
new file mode 100644
index 0000000..6be3d2e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py
@@ -0,0 +1,358 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""This is the PTS base class that is inherited from all PTS
+Tests.
+"""
+
+import ctypes
+import random
+import re
+import time
+import traceback
+
+from ctypes import *
+from datetime import datetime
+
+from acts import signals
+from acts.base_test import BaseTestClass
+from acts.controllers.bluetooth_pts_device import VERDICT_STRINGS
+from acts.controllers.fuchsia_device import FuchsiaDevice
+from acts.signals import TestSignal
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import create_bluetooth_device
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
+
+
+class PtsBaseClass(BaseTestClass):
+ """ Class for representing common functionality across all PTS tests.
+
+ This includes the ability to rerun tests due to PTS instability,
+ common PTS action mappings, and setup/teardown related devices.
+
+ """
+ scan_timeout_seconds = 10
+ peer_identifier = None
+
+ def setup_class(self):
+ super().setup_class()
+ if 'dut' in self.user_params:
+ if self.user_params['dut'] == 'fuchsia_devices':
+ self.dut = create_bluetooth_device(self.fuchsia_devices[0])
+ elif self.user_params['dut'] == 'android_devices':
+ self.dut = create_bluetooth_device(self.android_devices[0])
+ else:
+ raise ValueError('Invalid DUT specified in config. (%s)' %
+ self.user_params['dut'])
+ else:
+ # Default is an fuchsia device
+ self.dut = create_bluetooth_device(self.fuchsia_devices[0])
+
+ self.characteristic_read_not_permitted_uuid = self.user_params.get(
+ "characteristic_read_not_permitted_uuid")
+ self.characteristic_read_not_permitted_handle = self.user_params.get(
+ "characteristic_read_not_permitted_handle")
+ self.characteristic_read_invalid_handle = self.user_params.get(
+ "characteristic_read_invalid_handle")
+ self.characteristic_attribute_not_found_uuid = self.user_params.get(
+ "characteristic_attribute_not_found_uuid")
+ self.write_characteristic_not_permitted_handle = self.user_params.get(
+ "write_characteristic_not_permitted_handle")
+
+ self.pts = self.bluetooth_pts_device[0]
+ # MMI functions commented out until implemented. Added for tracking
+ # purposes.
+ self.pts_action_mapping = {
+ "A2DP": {
+ 1: self.a2dp_mmi_iut_connectable,
+ 1002: self.a2dp_mmi_iut_accept_connect,
+ 1020: self.a2dp_mmi_initiate_open_stream,
+ },
+ "GATT": {
+ 1: self.mmi_make_iut_connectable,
+ 2: self.mmi_iut_initiate_connection,
+ 3: self.mmi_iut_initiate_disconnection,
+ # 4: self.mmi_iut_no_security,
+ # 5: self.mmi_iut_initiate_br_connection,
+ 10: self.mmi_discover_primary_service,
+ # 11: self.mmi_confirm_no_primary_service_small,
+ # 12: self.mmi_iut_mtu_exchange,
+ # 13: self.mmi_discover_all_service_record,
+ # 14: self.mmi_iut_discover_gatt_service_record,
+ 15: self.mmi_iut_find_included_services,
+ # 16: self.mmi_confirm_no_characteristic_uuid_small,
+ 17: self.mmi_confirm_primary_service,
+ # 18: self.mmi_send_primary_service_uuid,
+ # 19: self.mmi_confirm_primary_service_uuid,
+ # 22: self.confirm_primary_service_1801,
+ 24: self.mmi_confirm_include_service,
+ 26: self.mmi_confirm_characteristic_service,
+ # 27: self.perform_read_all_characteristics,
+ 29: self.
+ mmi_discover_service_uuid_range, # AKA: discover service by uuid
+ # 31: self.perform_read_all_descriptors,
+ 48: self.mmi_iut_send_read_characteristic_handle,
+ 58: self.mmi_iut_send_read_descriptor_handle,
+ 70: self.mmi_send_write_command,
+ 74: self.mmi_send_write_request,
+ 76: self.mmi_send_prepare_write,
+ 77: self.mmi_iut_send_prepare_write_greater_offset,
+ 80: self.mmi_iut_send_prepare_write_greater,
+ 110: self.mmi_iut_enter_handle_read_not_permitted,
+ 111: self.mmi_iut_enter_uuid_read_not_permitted,
+ 118: self.mmi_iut_enter_handle_invalid,
+ 119: self.mmi_iut_enter_uuid_attribute_not_found,
+ 120: self.mmi_iut_enter_handle_write_not_permitted,
+ 2000: self.mmi_verify_secure_id, # Enter pairing pin from DUT.
+ },
+ "SDP": {
+ # TODO: Implement MMIs as necessary
+ }
+ }
+ self.pts.bind_to(self.process_next_action)
+
+ def teardown_class(self):
+ self.pts.clean_up()
+
+ def setup_test(self):
+ # Always start the test with RESULT_INCOMP
+ self.pts.pts_test_result = VERDICT_STRINGS['RESULT_INCOMP']
+
+ def teardown_test(self):
+ return True
+
+ @staticmethod
+ def pts_test_wrap(fn):
+ def _safe_wrap_test_case(self, *args, **kwargs):
+ test_id = "{}:{}:{}".format(self.__class__.__name__, fn.__name__,
+ time.time())
+ log_string = "[Test ID] {}".format(test_id)
+ self.log.info(log_string)
+ try:
+ self.dut.log_info("Started " + log_string)
+ result = fn(self, *args, **kwargs)
+ self.dut.log_info("Finished " + log_string)
+ rerun_count = self.user_params.get("pts_auto_rerun_count", 0)
+ for i in range(int(rerun_count)):
+ if result is not True:
+ self.teardown_test()
+ log_string = "[Rerun Test ID] {}. Run #{} run failed... Retrying".format(
+ test_id, i + 1)
+ self.log.info(log_string)
+ self.setup_test()
+ self.dut.log_info("Rerun Started " + log_string)
+ result = fn(self, *args, **kwargs)
+ else:
+ return result
+ return result
+ except TestSignal:
+ raise
+ except Exception as e:
+ self.log.error(traceback.format_exc())
+ self.log.error(str(e))
+ raise
+ return fn(self, *args, **kwargs)
+
+ return _safe_wrap_test_case
+
+ def process_next_action(self, action):
+ func = self.pts_action_mapping.get(
+ self.pts.pts_profile_mmi_request).get(action, "Nothing")
+ if func != 'Nothing':
+ func()
+
+ ### BEGIN A2DP MMI Actions ###
+
+ def a2dp_mmi_iut_connectable(self):
+ self.dut.start_profile_a2dp_sink()
+ self.dut.set_discoverable(True)
+
+ def a2dp_mmi_iut_accept_connect(self):
+ self.dut.start_profile_a2dp_sink()
+ self.dut.set_discoverable(True)
+
+ def a2dp_mmi_initiate_open_stream(self):
+ self.dut.a2dp_initiate_open_stream()
+
+ ### END A2DP MMI Actions ###
+
+ ### BEGIN GATT MMI Actions ###
+
+ def create_write_value_by_size(self, size):
+ write_value = []
+ for i in range(size):
+ write_value.append(i % 256)
+ return write_value
+
+ def mmi_send_write_command(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O with', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ raw_size = re.search('with <= \'(.*)\' byte', description_to_parse)
+ size = int(raw_size.group(1))
+ self.dut.gatt_client_write_characteristic_without_response_by_handle(
+ self.peer_identifier, handle,
+ self.create_write_value_by_size(size))
+
+ def mmi_send_write_request(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O with', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ raw_size = re.search('with <= \'(.*)\' byte', description_to_parse)
+ size = int(raw_size.group(1))
+ offset = 0
+ self.dut.gatt_client_write_characteristic_by_handle(
+ self.peer_identifier, handle, offset,
+ self.create_write_value_by_size(size))
+
+ def mmi_send_prepare_write(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O <=', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ raw_size = re.search('<= \'(.*)\' byte', description_to_parse)
+ size = int(math.floor(int(raw_size.group(1)) / 2))
+ offset = int(size / 2)
+ self.dut.gatt_client_write_characteristic_by_handle(
+ self.peer_identifier, handle, offset,
+ self.create_write_value_by_size(size))
+
+ def mmi_iut_send_prepare_write_greater_offset(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O and', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ raw_offset = re.search('greater than \'(.*)\' byte',
+ description_to_parse)
+ offset = int(raw_offset.group(1))
+ size = 1
+ self.dut.gatt_client_write_characteristic_by_handle(
+ self.peer_identifier, handle, offset,
+ self.create_write_value_by_size(size))
+
+ def mmi_iut_send_prepare_write_greater(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O with', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ raw_size = re.search('greater than \'(.*)\' byte',
+ description_to_parse)
+ size = int(raw_size.group(1))
+ offset = 0
+ self.dut.gatt_client_write_characteristic_by_handle(
+ self.peer_identifier, handle, offset,
+ self.create_write_value_by_size(size))
+
+ def mmi_make_iut_connectable(self):
+ adv_data = {
+ "name": fuchsia_name,
+ "appearance": None,
+ "service_data": None,
+ "tx_power_level": None,
+ "service_uuids": None,
+ "manufacturer_data": None,
+ "uris": None,
+ }
+ scan_response = None
+ connectable = True
+ interval = 1000
+
+ self.dut.start_le_advertisement(adv_data, scan_response, interval,
+ connectable)
+
+ def mmi_iut_enter_uuid_read_not_permitted(self):
+ self.pts.extra_answers.append(
+ self.characteristic_read_not_permitted_uuid)
+
+ def mmi_iut_enter_handle_read_not_permitted(self):
+ self.pts.extra_answers.append(
+ self.characteristic_read_not_permitted_handle)
+
+ def mmi_iut_enter_handle_invalid(self):
+ self.pts.extra_answers.append(self.characteristic_read_invalid_handle)
+
+ def mmi_iut_enter_uuid_attribute_not_found(self):
+ self.pts.extra_answers.append(
+ self.characteristic_attribute_not_found_uuid)
+
+ def mmi_iut_enter_handle_write_not_permitted(self):
+ self.pts.extra_answers.append(
+ self.write_characteristic_not_permitted_handle)
+
+ def mmi_verify_secure_id(self):
+ self.pts.extra_answers.append(self.dut.get_pairing_pin())
+
+ def mmi_discover_service_uuid_range(self, uuid):
+ self.dut.gatt_client_mmi_discover_service_uuid_range(
+ self.peer_identifier, uuid)
+
+ def mmi_iut_initiate_connection(self):
+ autoconnect = False
+ transport = gatt_transport['le']
+ adv_name = "PTS"
+ self.peer_identifier = self.dut.le_scan_with_name_filter(
+ "PTS", self.scan_timeout_seconds)
+ if self.peer_identifier is None:
+ raise signals.TestFailure("Scanner unable to find advertisement.")
+ tries = 3
+ for _ in range(tries):
+ if self.dut.gatt_connect(self.peer_identifier, transport,
+ autoconnect):
+ return
+
+ raise signals.TestFailure("Unable to connect to peripheral.")
+
+ def mmi_iut_initiate_disconnection(self):
+ if not self.dut.gatt_disconnect(self.peer_identifier):
+ raise signals.TestFailure("Failed to disconnect from peer.")
+
+ def mmi_discover_primary_service(self):
+ self.dut.gatt_refresh()
+
+ def mmi_iut_find_included_services(self):
+ self.dut.gatt_refresh()
+
+ test_result = self.pts.execute_test(test_name)
+ return test_result
+
+ def mmi_confirm_primary_service(self):
+ # TODO: Write verifier that 1800 and 1801 exists. For now just pass.
+ return True
+
+ def mmi_confirm_characteristic_service(self):
+ # TODO: Write verifier that no services exist. For now just pass.
+ return True
+
+ def mmi_confirm_include_service(self, uuid_description):
+ # TODO: Write verifier that input services exist. For now just pass.
+ # Note: List comes in the form of a long string to parse:
+ # Attribute Handle = '0002'O Included Service Attribute handle = '0080'O,End Group Handle = '0085'O,Service UUID = 'A00B'O
+ # \n
+ # Attribute Handle = '0021'O Included Service Attribute handle = '0001'O,End Group Handle = '0006'O,Service UUID = 'A00D'O
+ # \n ...
+ return True
+
+ def mmi_iut_send_read_characteristic_handle(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O to', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ self.dut.gatt_client_read_characteristic_by_handle(
+ self.peer_identifier, handle)
+
+ def mmi_iut_send_read_descriptor_handle(self):
+ description_to_parse = self.pts.current_implicit_send_description
+ raw_handle = re.search('handle = \'(.*)\'O to', description_to_parse)
+ handle = int(raw_handle.group(1), 16)
+ self.dut.gatt_client_descriptor_read_by_handle(self.peer_identifier,
+ handle)
+
+ ### END GATT MMI Actions ###
diff --git a/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py b/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py
new file mode 100644
index 0000000..f9aa117
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Bluetooth adapter libraries
+"""
+
+from acts_contrib.test_utils.bt.bt_constants import bt_rfcomm_uuids
+from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
+
+import pprint
+
+
+class RfcommLib():
+ def __init__(self, log, dut, target_mac_addr=None):
+ self.advertisement_list = []
+ self.dut = dut
+ self.log = log
+ self.target_mac_addr = target_mac_addr
+
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
+
+ def connect(self, line):
+ """Perform an RFCOMM connect"""
+ uuid = None
+ if len(line) > 0:
+ uuid = line
+ if uuid:
+ self.dut.droid.bluetoothRfcommBeginConnectThread(
+ self.target_mac_addr, uuid)
+ else:
+ self.dut.droid.bluetoothRfcommBeginConnectThread(
+ self.target_mac_addr)
+
+ def open_rfcomm_socket(self):
+ """Open rfcomm socket"""
+ self.dut.droid.rfcommCreateRfcommSocket(self.target_mac_addr, 1)
+
+ def open_l2cap_socket(self):
+ """Open L2CAP socket"""
+ self.dut.droid.rfcommCreateL2capSocket(self.target_mac_addr, 1)
+
+ def write(self, line):
+ """Write String data over an RFCOMM connection"""
+ self.dut.droid.bluetoothRfcommWrite(line)
+
+ def write_binary(self, line):
+ """Write String data over an RFCOMM connection"""
+ self.dut.droid.bluetoothRfcommWriteBinary(line)
+
+ def end_connect(self):
+ """End RFCOMM connection"""
+ self.dut.droid.bluetoothRfcommEndConnectThread()
+
+ def accept(self, line):
+ """Accept RFCOMM connection"""
+ uuid = None
+ if len(line) > 0:
+ uuid = line
+ if uuid:
+ self.dut.droid.bluetoothRfcommBeginAcceptThread(uuid)
+ else:
+ self.dut.droid.bluetoothRfcommBeginAcceptThread(
+ bt_rfcomm_uuids['base_uuid'])
+
+ def stop(self):
+ """Stop RFCOMM Connection"""
+ self.dut.droid.bluetoothRfcommStop()
+
+ def open_l2cap_socket(self):
+ """Open L2CAP socket"""
+ self.dut.droid.rfcommCreateL2capSocket(self.target_mac_addr, 1)
diff --git a/acts_tests/acts_contrib/test_utils/bt/shell_commands_lib.py b/acts_tests/acts_contrib/test_utils/bt/shell_commands_lib.py
new file mode 100644
index 0000000..0e0f099
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/shell_commands_lib.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Shell command library.
+"""
+
+
+class ShellCommands():
+ def __init__(self, log, dut):
+ self.dut = dut
+ self.log = log
+
+ def set_battery_level(self, level):
+ """Set the battery level via ADB shell
+ Args:
+ level: the percent level to set
+ """
+ self.dut.adb.shell("dumpsys battery set level {}".format(level))
+
+ def disable_ble_scanning(self):
+ """Disable BLE scanning via ADB shell"""
+ self.dut.adb.shell("settings put global ble_scan_always_enabled 0")
+
+ def enable_ble_scanning(self):
+ """Enable BLE scanning via ADB shell"""
+ self.dut.adb.shell("settings put global ble_scan_always_enabled 1")
+
+ def consume_cpu_core(self):
+ """Consume a CPU core on the Android device via ADB shell"""
+ self.dut.adb.shell("echo $$ > /dev/cpuset/top-app/tasks")
+ self.dut.adb.shell("cat /dev/urandom > /dev/null &")
diff --git a/acts_tests/acts_contrib/test_utils/bt/simulated_carkit_device.py b/acts_tests/acts_contrib/test_utils/bt/simulated_carkit_device.py
new file mode 100644
index 0000000..03ccdef
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/simulated_carkit_device.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts import asserts
+
+from acts.controllers import android_device
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+
+# TODO: This class to be deprecated for
+# ../acts/test_utils/abstract_devices/bluetooth_handsfree_abstract_device.py
+
+
+class SimulatedCarkitDevice():
+ def __init__(self, serial):
+ self.ad = android_device.create(serial)[0]
+ if not bluetooth_enabled_check(self.ad):
+ asserts.fail("No able to turn on bluetooth")
+ self.mac_address = self.ad.droid.bluetoothGetLocalAddress()
+ self.ad.droid.bluetoothToggleState(False)
+ self.ad.droid.bluetoothMediaConnectToCarMBS()
+
+ def destroy(self):
+ self.ad.clean_up()
+
+ def accept_call(self):
+ return self.ad.droid.telecomAcceptRingingCall(None)
+
+ def end_call(self):
+ return self.ad.droid.telecomEndCall()
+
+ def enter_pairing_mode(self):
+ self.ad.droid.bluetoothStartPairingHelper(True)
+ return self.ad.droid.bluetoothMakeDiscoverable()
+
+ def next_track(self):
+ return self.ad.droid.bluetoothMediaPassthrough("skipNext")
+
+ def pause(self):
+ return self.ad.droid.bluetoothMediaPassthrough("pause")
+
+ def play(self):
+ return self.ad.droid.bluetoothMediaPassthrough("play")
+
+ def power_off(self):
+ return self.ad.droid.bluetoothToggleState(False)
+
+ def power_on(self):
+ return self.ad.droid.bluetoothToggleState(True)
+
+ def previous_track(self):
+ return self.ad.droid.bluetoothMediaPassthrough("skipPrev")
+
+ def reject_call(self):
+ return self.ad.droid.telecomCallDisconnect(
+ self.ad.droid.telecomCallGetCallIds()[0])
+
+ def volume_down(self):
+ target_step = self.ad.droid.getMediaVolume() - 1
+ target_step = max(target_step, 0)
+ return self.ad.droid.setMediaVolume(target_step)
+
+ def volume_up(self):
+ target_step = self.ad.droid.getMediaVolume() + 1
+ max_step = self.ad.droid.getMaxMediaVolume()
+ target_step = min(target_step, max_step)
+ return self.ad.droid.setMediaVolume(target_step)
diff --git a/acts_tests/acts_contrib/test_utils/car/__init__.py b/acts_tests/acts_contrib/test_utils/car/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/car/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py b/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py
new file mode 100644
index 0000000..a428528
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defines utilities that can be used for making calls indenpendent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+# Make a phone call to the specified URI. It is assumed that we are making the
+# call to the user selected default account.
+#
+# We usually want to make sure that the call has ended up in a good state.
+#
+# NOTE: This util is applicable to only non-conference type calls. It is best
+# suited to test cases where only one call is in action at any point of time.
+
+import queue
+import time
+
+from acts import logger
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.bt.BtEnum import *
+
+
+def set_car_profile_priorities_off(car_droid, ph_droid):
+ """Sets priority of car related profiles to OFF. This avoids
+ autoconnect being triggered randomly. The use of this function
+ is encouraged when you're testing individual profiles in isolation
+
+ Args:
+ log: log object
+ car_droid: Car droid
+ ph_droid: Phone droid
+
+ Returns:
+ True if success, False if fail.
+ """
+ # TODO investigate MCE
+ car_profiles = [BluetoothProfile.A2DP_SINK,
+ BluetoothProfile.HEADSET_CLIENT,
+ BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_MCE]
+ bt_test_utils.set_profile_priority(car_droid, ph_droid, car_profiles,
+ BluetoothPriorityLevel.PRIORITY_OFF)
diff --git a/acts_tests/acts_contrib/test_utils/car/car_media_utils.py b/acts_tests/acts_contrib/test_utils/car/car_media_utils.py
new file mode 100644
index 0000000..84768b8
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/car/car_media_utils.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Utilities that can be used for testing media related usecases.
+
+# Events dispatched from the RPC Server
+EVENT_PLAY_RECEIVED = "playReceived"
+EVENT_PAUSE_RECEIVED = "pauseReceived"
+EVENT_SKIP_NEXT_RECEIVED = "skipNextReceived"
+EVENT_SKIP_PREV_RECEIVED = "skipPrevReceived"
+
+# Passthrough Commands sent to the RPC Server
+CMD_MEDIA_PLAY = "play"
+CMD_MEDIA_PAUSE = "pause"
+CMD_MEDIA_SKIP_NEXT = "skipNext"
+CMD_MEDIA_SKIP_PREV = "skipPrev"
+
+# MediaMetaData keys. Keep them the same as in BluetoothMediaFacade.
+MEDIA_KEY_TITLE = "keyTitle"
+MEDIA_KEY_ALBUM = "keyAlbum"
+MEDIA_KEY_ARTIST = "keyArtist"
+MEDIA_KEY_DURATION = "keyDuration"
+MEDIA_KEY_NUM_TRACKS = "keyNumTracks"
+
+class PlaybackState:
+ PAUSE = 2
+ PLAY = 3
+
+def verifyEventReceived(log, device, event, timeout):
+ """
+ Verify if the event was received from the given device.
+ When a fromDevice talks to a toDevice and expects an event back,
+ this util function can be used to see if the toDevice posted it.
+ Args:
+ log: The logging object
+ device: The device to pop the event from
+ event: The event we are interested in.
+ timeout: The time in seconds before we timeout
+ Returns:
+ True if the event was received
+ False if we timed out waiting for the event
+ """
+ try:
+ device.ed.pop_event(event, timeout)
+ except Exception:
+ log.info(" {} Event Not received".format(event))
+ return False
+ log.info("Event Received : {}".format(event))
+ return True
+
+
+def send_media_passthrough_cmd(log,
+ fromDevice,
+ toDevice,
+ cmd,
+ expctEvent,
+ timeout=1.0):
+ """
+ Send a media passthrough command from one device to another
+ via bluetooth.
+ Args:
+ log: The logging object
+ fromDevice: The device to send the command from
+ toDevice: The device the command is sent to
+ cmd: The passthrough command to send
+ expctEvent: The expected event
+ timeout: The time in seconds before we timeout, deafult = 1sec
+ Returns:
+ True if the event was received
+ False if we timed out waiting for the event
+ """
+ log.info("Sending passthru : {}".format(cmd))
+ fromDevice.droid.bluetoothMediaPassthrough(cmd)
+ return verifyEventReceived(log, toDevice, expctEvent, timeout)
+
+
+def log_metadata(log, metadata):
+ """
+ Log the Metadata to the console.
+ Args:
+ log: The logging object
+ metadata: Dictionary of the song's metadata
+ """
+ title = metadata[MEDIA_KEY_TITLE]
+ album = metadata[MEDIA_KEY_ALBUM]
+ artist = metadata[MEDIA_KEY_ARTIST]
+ duration = metadata[MEDIA_KEY_DURATION]
+ numTracks = metadata[MEDIA_KEY_NUM_TRACKS]
+ log.info("Playing Artist: {}, Album: {}, Title: {}".format(artist, album,
+ title))
+ log.info("Duration: {}, NumTracks: {}".format(duration, numTracks))
+
+
+def compare_metadata(log, metadata1, metadata2):
+ """
+ Compares the Metadata between the two devices
+ Args:
+ log: The logging object
+ metadata1 Media Metadata of device1
+ metadata2 Media Metadata of device2
+ Returns:
+ True if the Metadata matches
+ False if the Metadata do not match
+ """
+ log.info("Device1 metadata:")
+ log_metadata(log, metadata1)
+ log.info("Device2 metadata:")
+ log_metadata(log, metadata2)
+
+ if not (metadata1[MEDIA_KEY_TITLE] == metadata2[MEDIA_KEY_TITLE]):
+ log.info("Song Titles do not match")
+ return False
+
+ if not (metadata1[MEDIA_KEY_ALBUM] == metadata2[MEDIA_KEY_ALBUM]):
+ log.info("Song Albums do not match")
+ return False
+
+ if not (metadata1[MEDIA_KEY_ARTIST] == metadata2[MEDIA_KEY_ARTIST]):
+ log.info("Song Artists do not match")
+ return False
+
+ if not (metadata1[MEDIA_KEY_DURATION] == metadata2[MEDIA_KEY_DURATION]):
+ log.info("Song Duration do not match")
+ return False
+
+ if not (metadata1[MEDIA_KEY_NUM_TRACKS] == metadata2[MEDIA_KEY_NUM_TRACKS]
+ ):
+ log.info("Song Num Tracks do not match")
+ return False
+
+ return True
+
+
+def check_metadata(log, device1, device2):
+ """
+ Gets the now playing metadata from 2 devices and checks if they are the same
+ Args:
+ log: The logging object
+ device1 Device 1
+ device2 Device 2
+ Returns:
+ True if the Metadata matches
+ False if the Metadata do not match
+ """
+ metadata1 = device1.droid.bluetoothMediaGetCurrentMediaMetaData()
+ if metadata1 is None:
+ return False
+
+ metadata2 = device2.droid.bluetoothMediaGetCurrentMediaMetaData()
+ if metadata2 is None:
+ return False
+ return compare_metadata(log, metadata1, metadata2)
+
+
+def isMediaSessionActive(log, device, mediaSession):
+ """
+ Checks if the passed mediaSession is active.
+ Used to see if the device is playing music.
+ Args:
+ log: The logging object
+ device Device to check
+ mediaSession MediaSession to check if it is active
+ Returns:
+ True if the given mediaSession is active
+ False if the given mediaSession is not active
+ """
+ # Get a list of MediaSession tags (String) that is currently active
+ activeSessions = device.droid.bluetoothMediaGetActiveMediaSessions()
+ if len(activeSessions) > 0:
+ for session in activeSessions:
+ log.info(session)
+ if (session == mediaSession):
+ return True
+ log.info("No Media Sessions")
+ return False
diff --git a/acts_tests/acts_contrib/test_utils/car/car_telecom_utils.py b/acts_tests/acts_contrib/test_utils/car/car_telecom_utils.py
new file mode 100644
index 0000000..7dbac40
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/car/car_telecom_utils.py
@@ -0,0 +1,491 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defines utilities that can be used for making calls indenpendent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+# Make a phone call to the specified URI. It is assumed that we are making the
+# call to the user selected default account.
+#
+# We usually want to make sure that the call has ended up in a good state.
+#
+# NOTE: This util is applicable to only non-conference type calls. It is best
+# suited to test cases where only one call is in action at any point of time.
+
+import queue
+import time
+
+from acts import logger
+from acts_contrib.test_utils.tel import tel_defines
+
+def dial_number(log, ad, uri):
+ """Dial a number
+
+ Args:
+ log: log object
+ ad: android device object
+ uri: Tel number to dial
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("Dialing up droid {} call uri {}".format(
+ ad.serial, uri))
+
+ # First check that we are not in call.
+ if ad.droid.telecomIsInCall():
+ log.info("We're still in call {}".format(ad.serial))
+ return False
+
+ # Start tracking updates.
+ ad.droid.telecomStartListeningForCallAdded()
+ #If a phone number is passed in
+ if "tel:" not in uri:
+ uri = "tel:" + uri
+ ad.droid.telecomCallTelUri(uri)
+
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info(
+ "Did not get {} event!".format(tel_defines.EventTelecomCallAdded))
+ # Return failure.
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+
+ call_id = event['data']['CallId']
+ log.info("Call ID: {} dev {}".format(call_id, ad.serial))
+
+ if not call_id:
+ log.info("CallId is empty!")
+ return False
+ if not wait_for_dialing(log, ad):
+ return False
+
+ return call_id
+
+def wait_for_call_state(log, ad, call_id, state):
+ """Wait for the given call id to transition to the given call state.
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: ID of the call that we're waiting for the call state to
+ transition into.
+ state: desired final state.
+
+ Returns:
+ True if success, False if fail.
+ """
+ # Lets track the call now.
+ # NOTE: Disable this everywhere we return.
+ ad.droid.telecomCallStartListeningForEvent(
+ call_id, tel_defines.EVENT_CALL_STATE_CHANGED)
+
+ # We may have missed the update so do a quick check.
+ if ad.droid.telecomCallGetCallState(call_id) == state:
+ log.info("Call ID {} already in {} dev {}!".format(
+ call_id, state, ad.serial))
+ ad.droid.telecomCallStopListeningForEvent(call_id,
+ tel_defines.EventTelecomCallStateChanged)
+ return True
+
+ # If not then we need to poll for the event.
+ # We return if we have found a match or we timeout for further updates of
+ # the call
+ end_time = time.time() + 10
+ while True and time.time() < end_time:
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallStateChanged,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ call_state = event['data']['Event']
+ if call_state == state:
+ ad.droid.telecomCallStopListeningForEvent(call_id,
+ tel_defines.EventTelecomCallStateChanged)
+ return True
+ else:
+ log.info("Droid {} in call {} state {}".format(
+ ad.serial, call_id, call_state))
+ continue
+ except queue.Empty:
+ log.info("Did not get into state {} dev {}".format(
+ state, ad.serial))
+ ad.droid.telecomCallStopListeningForEvent(call_id,
+ tel_defines.EventTelecomCallStateChanged)
+ return False
+ return False
+
+def hangup_call(log, ad, call_id):
+ """Hangup a number
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: Call to hangup.
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("Hanging up droid {} call {}".format(
+ ad.serial, call_id))
+ # First check that we are in call, otherwise fail.
+ if not ad.droid.telecomIsInCall():
+ log.info("We are not in-call {}".format(ad.serial))
+ return False
+
+ # Make sure we are registered with the events.
+ ad.droid.telecomStartListeningForCallRemoved()
+
+ # Disconnect call.
+ ad.droid.telecomCallDisconnect(call_id)
+
+ # Wait for removed event.
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallRemoved,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info("Did not get TelecomCallRemoved event")
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallRemoved()
+
+ log.info("Removed call {}".format(event))
+ if event['data']['CallId'] != call_id:
+ return False
+
+ return True
+
+def hangup_conf(log, ad, conf_id, timeout=10):
+ """Hangup a conference call
+
+ Args:
+ log: log object
+ ad: android device object
+ conf_id: Conf call to hangup.
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("hangup_conf: Hanging up droid {} call {}".format(
+ ad.serial, conf_id))
+
+ # First check that we are in call, otherwise fail.
+ if not ad.droid.telecomIsInCall():
+ log.info("We are not in-call {}".format(ad.serial))
+ return False
+
+ # Disconnect call.
+ ad.droid.telecomCallDisconnect(conf_id)
+
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ call_ids = get_calls_in_states(log, ad, [tel_defines.CALL_STATE_ACTIVE])
+ log.debug("Active calls {}".format(call_ids))
+ if not call_ids:
+ return True
+ time.sleep(1)
+ log.error("Failed to hang up all conference participants")
+ return False
+
+def accept_call(log, ad, call_id):
+ """Accept a number
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: Call to accept.
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("Accepting call at droid {} call {}".format(
+ ad.serial, call_id))
+ # First check we are in call, otherwise fail.
+ if not ad.droid.telecomIsInCall():
+ log.info("We are not in-call {}".format(ad.serial))
+ return False
+
+ # Accept the call and wait for the call to be accepted on AG.
+ ad.droid.telecomCallAnswer(call_id, tel_defines.VT_STATE_AUDIO_ONLY)
+ if not wait_for_call_state(log, ad, call_id, tel_defines.CALL_STATE_ACTIVE):
+ log.error("Call {} on droid {} not active".format(
+ call_id, ad.serial))
+ return False
+
+ return True
+
+def wait_for_not_in_call(log, ad):
+ """Wait for the droid to be OUT OF CALLING state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ ad.droid.telecomStartListeningForCallRemoved()
+
+ calls = ad.droid.telecomCallGetCallIds()
+ if len(calls) > 1:
+ log.info("More than one call {} {}".format(calls, ad.serial))
+ return False
+
+ if len(calls) > 0:
+ log.info("Got calls {} for {}".format(
+ calls, ad.serial))
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallRemoved,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info("wait_for_not_in_call Did not get {} droid {}".format(
+ tel_defines.EventTelecomCallRemoved,
+ ad.serial))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallRemoved()
+
+ # Either we removed the only call or we never had a call previously, either
+ # ways simply check if we are in in call now.
+ return (not ad.droid.telecomIsInCall())
+
+def wait_for_dialing(log, ad):
+ """Wait for the droid to be in dialing state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ # Start listening for events before anything else happens.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ # First check if we re in call, then simply return.
+ if ad.droid.telecomIsInCall():
+ ad.droid.telecomStopListeningForCallAdded()
+ return True
+
+ call_id = None
+ # Now check if we already have calls matching the state.
+ calls_in_state = get_calls_in_states(log, ad,
+ [tel_defines.CALL_STATE_CONNECTING,
+ tel_defines.CALL_STATE_DIALING])
+
+ # If not then we need to poll for the calls themselves.
+ if len(calls_in_state) == 0:
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info("Did not get {}".format(
+ tel_defines.EventTelecomCallAdded))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+ call_id = event['data']['CallId']
+ else:
+ call_id = calls_in_state[0]
+
+ # We may still not be in-call if the call setup is going on.
+ # We wait for the call state to move to dialing.
+ log.info("call id {} droid {}".format(call_id, ad.serial))
+ if not wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_DIALING):
+ return False
+
+ # Finally check the call state.
+ return ad.droid.telecomIsInCall()
+
+def wait_for_ringing(log, ad):
+ """Wait for the droid to be in ringing state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("waiting for ringing {}".format(ad.serial))
+ # Start listening for events before anything else happens.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ # First check if we re in call, then simply return.
+ if ad.droid.telecomIsInCall():
+ log.info("Device already in call {}".format(ad.serial))
+ ad.droid.telecomStopListeningForCallAdded()
+ return True
+
+ call_id = None
+ # Now check if we already have calls matching the state.
+ calls_in_state = ad.droid.telecomCallGetCallIds()
+
+ for c_id in calls_in_state:
+ if ad.droid.telecomCallGetCallState(c_id) == tel_defines.CALL_STATE_RINGING:
+ return True
+
+ event = None
+ call_id = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+ except queue.Empty:
+ log.info("Did not get {} droid {}".format(
+ tel_defines.EventTelecomCallAdded,
+ ad.serial))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+ call_id = event['data']['CallId']
+ log.info("wait_for_ringing call found {} dev {}".format(
+ call_id, ad.serial))
+
+ # If the call already existed then we would have returned above otherwise
+ # we will verify that the newly added call is indeed ringing.
+ if not wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_RINGING):
+ log.info("No ringing call id {} droid {}".format(
+ call_id, ad.serial))
+ return False
+ return True
+
+def wait_for_active(log, ad):
+ """Wait for the droid to be in active call state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("waiting for active {}".format(ad.serial))
+ # Start listening for events before anything else happens.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ call_id = None
+ # Now check if we already have calls matching the state.
+ calls_in_state = ad.droid.telecomCallGetCallIds()
+
+ if len(calls_in_state) == 0:
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+ except queue.Empty:
+ log.info("Did not get {} droid {}".format(
+ tel_defines.EventTelecomCallAdded,
+ ad.serial))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+ call_id = event['data']['CallId']
+ log.info("wait_for_ringing call found {} dev {}".format(
+ call_id, ad.serial))
+ else:
+ call_id = calls_in_state[0]
+
+ # We have found a new call to be added now wait it to transition into
+ # active state.
+ if not wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_ACTIVE):
+ log.info("No active call id {} droid {}".format(
+ call_id, ad.serial))
+ return False
+ return True
+
+def wait_for_conference(log, ad, participants=2, timeout=10):
+ """Wait for the droid to be in a conference with calls specified
+ in conf_calls.
+
+ Args:
+ log: log object
+ ad: android device object
+ participants: conference participant count
+
+ Returns:
+ call_id if success, None if fail.
+ """
+
+ def get_conference_id(callers):
+ for call_id in callers:
+ call_details = ad.droid.telecomCallGetCallById(call_id).get("Details")
+ if set([tel_defines.CALL_PROPERTY_CONFERENCE]).issubset(call_details.get("Properties")):
+ return call_id
+ return None
+
+ log.info("waiting for conference {}".format(ad.serial))
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ participant_callers = get_calls_in_states(log, ad, [tel_defines.CALL_STATE_ACTIVE])
+ if (len(participant_callers) == participants + 1):
+ return get_conference_id(participant_callers)
+ time.sleep(1)
+ return None
+
+def get_call_id_children(log, ad, call_id):
+ """Return the list of calls that are children to call_id
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: Conference call id
+
+ Returns:
+ List containing call_ids.
+ """
+ call = ad.droid.telecomCallGetCallById(call_id)
+ call_chld = set(call['Children'])
+ log.info("get_call_id_children droid {} call {} children {}".format(
+ ad.serial, call, call_chld))
+ return call_chld
+
+def get_calls_in_states(log, ad, call_states):
+ """Return the list of calls that are any of the states passed in call_states
+
+ Args:
+ log: log object
+ ad: android device object
+ call_states: List of desired call states
+
+ Returns:
+ List containing call_ids.
+ """
+ # Get the list of calls.
+ call_ids = ad.droid.telecomCallGetCallIds()
+ call_in_state = []
+ for call_id in call_ids:
+ call = ad.droid.telecomCallGetCallById(call_id)
+ log.info("Call id: {} desc: {}".format(call_id, call))
+ if call['State'] in call_states:
+ log.info("Adding call id {} to result set.".format(call_id))
+ call_in_state.append(call_id)
+ return call_in_state
diff --git a/acts_tests/acts_contrib/test_utils/car/tel_telecom_utils.py b/acts_tests/acts_contrib/test_utils/car/tel_telecom_utils.py
new file mode 100644
index 0000000..fb0fc84
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/car/tel_telecom_utils.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defines utilities that can be used for making calls independent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+import queue
+import time
+
+from acts import logger
+from acts_contrib.test_utils.tel import tel_defines
+
+def dial_number(log, ad, uri):
+ """Dial a Tel: URI
+
+ Args:
+ log: log object
+ ad: android device object
+ uri: Tel uri dial
+
+ Returns:
+ True if success, False if fail.
+ """
+ if "tel:" not in uri:
+ uri = "tel:" + uri
+ log.info("Dialing up droid {} call uri {}".format(
+ ad.serial, uri))
+
+ # Start tracking updates.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ ad.droid.telecomCallTelUri(uri)
+
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info(
+ "Did not get {} event!".format(tel_defines.EventTelecomCallAdded))
+ # Return failure.
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+
+ call_id = event['data']['CallId']
+ log.info("Call ID: {} dev {}".format(call_id, ad.serial))
+
+ if not call_id:
+ log.info("CallId is empty!")
+ return False
+ if not wait_for_dialing(log, ad):
+ return False
+
+ return call_id
+
+def wait_for_call_state(log, ad, call_id, state):
+ """Wait for the given call id to transition to the given call state.
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: ID of the call that we're waiting for the call state to
+ transition into.
+ state: desired final state.
+
+ Returns:
+ True if success, False if fail.
+ """
+ # Lets track the call now.
+ ad.droid.telecomCallStartListeningForEvent(
+ call_id, tel_defines.EVENT_CALL_STATE_CHANGED)
+
+ # We may have missed the update so do a quick check.
+ if ad.droid.telecomCallGetCallState(call_id) == state:
+ log.info("Call ID {} already in {} dev {}!".format(
+ call_id, state, ad.serial))
+ return state
+
+ # If not then we need to poll for the event.
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallStateChanged,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ call_state = event['data']['Event']
+ except queue.Empty:
+ log.info("Did not get into state {} dev {}".format(
+ state, ad.serial))
+ return None
+ finally:
+ ad.droid.telecomCallStopListeningForEvent(call_id,
+ tel_defines.EventTelecomCallStateChanged)
+ return call_state
+
+def hangup_call(log, ad, call_id):
+ """Hangup a number
+
+ Args:
+ log: log object
+ ad: android device object
+ call_id: Call to hangup.
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("Hanging up droid {} call {}".format(
+ ad.serial, call_id))
+ # First check that we are in call, otherwise fail.
+ if not ad.droid.telecomIsInCall():
+ log.info("We are not in-call {}".format(ad.serial))
+ return False
+
+ # Make sure we are registered with the events.
+ ad.droid.telecomStartListeningForCallRemoved()
+
+ # Disconnect call.
+ ad.droid.telecomCallDisconnect(call_id)
+
+ # Wait for removed event.
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallRemoved,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info("Did not get TelecomCallRemoved event")
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallRemoved()
+
+ log.info("Removed call {}".format(event))
+ if event['data']['CallId'] != call_id:
+ return False
+
+ return True
+
+def wait_for_not_in_call(log, ad):
+ """Wait for the droid to be OUT OF CALLING state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ ad.droid.telecomStartListeningForCallRemoved()
+
+ calls = ad.droid.telecomCallGetCallIds()
+
+ timeout = time.time() + tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+ remaining_time = tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+ if len(calls) == 0:
+ log.info("Got calls {} for {}".format(
+ calls, ad.serial))
+ try:
+ while len(calls) > 0:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallRemoved,
+ remaining_time)
+ remaining_time = timeout - time.time()
+ if (remaining_time <= 0
+ or len(ad.droid.telecomCallGetCallIds() == 0)):
+ break
+
+ except queue.Empty:
+ log.info("wait_for_not_in_call Did not get {} droid {}".format(
+ tel_defines.EventTelecomCallRemoved,
+ ad.serial))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallRemoved()
+
+ # Either we removed the only call or we never had a call previously, either
+ # ways simply check if we are in in call now.
+ return (not ad.droid.telecomIsInCall())
+
+def wait_for_dialing(log, ad):
+ """Wait for the droid to be in dialing state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ # Start listening for events before anything else happens.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ call_id = None
+ # Now check if we already have calls matching the state.
+ calls_in_state = get_calls_in_states(log, ad,
+ [tel_defines.CALL_STATE_CONNECTING,
+ tel_defines.CALL_STATE_DIALING])
+
+ # If not then we need to poll for the calls themselves.
+ if len(calls_in_state) == 0:
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+ except queue.Empty:
+ log.info("Did not get {}".format(
+ tel_defines.EventTelecomCallAdded))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+ call_id = event['data']['CallId']
+ else:
+ call_id = calls_in_state[0]
+
+ # We may still not be in-call if the call setup is going on.
+ # We wait for the call state to move to dialing.
+ log.info("call id {} droid {}".format(call_id, ad.serial))
+ curr_state = wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_DIALING)
+ if curr_state == tel_defines.CALL_STATE_CONNECTING:
+ log.info("Got connecting state now waiting for dialing state.")
+ if wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_DIALING) != \
+ tel_defines.CALL_STATE_DIALING:
+ return False
+ else:
+ if curr_state != tel_defines.CALL_STATE_DIALING:
+ log.info("First state is not dialing")
+ return False
+
+ # Finally check the call state.
+ log.info("wait_for_dialing returns " + str(ad.droid.telecomIsInCall()) +
+ " " + str(ad.serial))
+ return ad.droid.telecomIsInCall()
+
+def wait_for_ringing(log, ad):
+ """Wait for the droid to be in ringing state.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ log.info("waiting for ringing {}".format(ad.serial))
+ # Start listening for events before anything else happens.
+ ad.droid.telecomStartListeningForCallAdded()
+
+ # First check if we re in call, then simply return.
+ if ad.droid.telecomIsInCall():
+ log.info("Device already in call {}".format(ad.serial))
+ ad.droid.telecomStopListeningForCallAdded()
+ return True
+
+ call_id = None
+ # Now check if we already have calls matching the state.
+ calls_in_state = ad.droid.telecomCallGetCallIds()
+
+ if len(calls_in_state) == 0:
+ event = None
+ try:
+ event = ad.ed.pop_event(
+ tel_defines.EventTelecomCallAdded,
+ tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+ except queue.Empty:
+ log.info("Did not get {} droid {}".format(
+ tel_defines.EventTelecomCallAdded,
+ ad.serial))
+ return False
+ finally:
+ ad.droid.telecomStopListeningForCallAdded()
+ call_id = event['data']['CallId']
+ log.info("wait_for_ringing call found {} dev {}".format(
+ call_id, ad.serial))
+ else:
+ call_id = calls_in_state[0]
+
+ # A rining call is ringing when it is added so simply wait for ringing
+ # state.
+ if wait_for_call_state(
+ log, ad, call_id, tel_defines.CALL_STATE_RINGING) != \
+ tel_defines.CALL_STATE_RINGING:
+ log.info("No ringing call id {} droid {}".format(
+ call_id, ad.serial))
+ return False
+ return True
+
+def get_calls_in_states(log, ad, call_states):
+ """Return the list of calls that are any of the states passed in call_states
+
+ Args:
+ log: log object
+ ad: android device object
+ call_states: List of desired call states
+
+ Returns:
+ List containing call_ids.
+ """
+ # Get the list of calls.
+ call_ids = ad.droid.telecomCallGetCallIds()
+ call_in_state = []
+ for call_id in call_ids:
+ call = ad.droid.telecomCallGetCallById(call_id)
+ log.info("Call id: {} desc: {}".format(call_id, call))
+ if call['State'] in call_states:
+ log.info("Adding call id {} to result set.".format(call_id))
+ call_in_state.append(call_id)
+ return call_in_state
diff --git a/acts_tests/acts_contrib/test_utils/coex/CoexBaseTest.py b/acts_tests/acts_contrib/test_utils/coex/CoexBaseTest.py
new file mode 100644
index 0000000..e0532d9
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/CoexBaseTest.py
@@ -0,0 +1,305 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import json
+import os
+import threading
+import time
+
+from acts.base_test import BaseTestClass
+from acts.controllers import android_device
+from acts.controllers import relay_device_controller
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.coex.coex_test_utils import A2dpDumpsysParser
+from acts_contrib.test_utils.coex.coex_test_utils import (
+ collect_bluetooth_manager_dumpsys_logs)
+from acts_contrib.test_utils.coex.coex_test_utils import configure_and_start_ap
+from acts_contrib.test_utils.coex.coex_test_utils import iperf_result
+from acts_contrib.test_utils.coex.coex_test_utils import parse_fping_results
+from acts_contrib.test_utils.coex.coex_test_utils import wifi_connection_check
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils import get_iperf_arg_string
+from acts_contrib.test_utils.wifi.wifi_power_test_utils import get_phone_ip
+from acts_contrib.test_utils.wifi.wifi_test_utils import reset_wifi
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_test_device_init
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+
+AVRCP_WAIT_TIME = 3
+
+
+class CoexBaseTest(BaseTestClass):
+
+ class IperfVariables:
+
+ def __init__(self, current_test_name):
+ self.iperf_started = False
+ self.bidirectional_client_path = None
+ self.bidirectional_server_path = None
+ self.iperf_server_path = None
+ self.iperf_client_path = None
+ self.throughput = []
+ self.protocol = current_test_name.split("_")[-2]
+ self.stream = current_test_name.split("_")[-1]
+ self.is_bidirectional = False
+ if self.stream == 'bidirectional':
+ self.is_bidirectional = True
+
+ def setup_class(self):
+ super().setup_class()
+ self.pri_ad = self.android_devices[0]
+ if len(self.android_devices) == 2:
+ self.sec_ad = self.android_devices[1]
+ elif len(self.android_devices) == 3:
+ self.third_ad = self.android_devices[2]
+ self.ssh_config = None
+
+ self.counter = 0
+ self.thread_list = []
+ if not setup_multiple_devices_for_bt_test(self.android_devices):
+ self.log.error('Failed to setup devices for bluetooth test')
+ return False
+ req_params = ['network', 'iperf']
+ opt_params = [
+ 'AccessPoint', 'RetailAccessPoints', 'RelayDevice',
+ 'required_devices'
+ ]
+ self.unpack_userparams(req_params, opt_params)
+
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_client = self.iperf_clients[0]
+
+ if hasattr(self, 'RelayDevice'):
+ self.audio_receiver = self.relay_devices[0]
+ self.audio_receiver.power_on()
+ self.headset_mac_address = self.audio_receiver.mac_address
+ else:
+ self.log.warning('Missing Relay config file.')
+
+ if hasattr(self, 'AccessPoint'):
+ self.ap = self.access_points[0]
+ configure_and_start_ap(self.ap, self.network)
+ elif hasattr(self, 'RetailAccessPoints'):
+ self.retail_access_points = retail_ap.create(
+ self.RetailAccessPoints)
+ self.retail_access_point = self.retail_access_points[0]
+ band = self.retail_access_point.band_lookup_by_channel(
+ self.network['channel'])
+ self.retail_access_point.set_channel(band, self.network['channel'])
+ else:
+ self.log.warning('config file have no access point information')
+
+ wifi_test_device_init(self.pri_ad)
+ wifi_connect(self.pri_ad, self.network, num_of_tries=5)
+
+ def setup_test(self):
+ self.tag = 0
+ self.result = {}
+ self.dev_list = {}
+ self.iperf_variables = self.IperfVariables(self.current_test_name)
+ self.a2dp_dumpsys = A2dpDumpsysParser()
+ self.log_path = os.path.join(self.pri_ad.log_path,
+ self.current_test_name)
+ os.makedirs(self.log_path, exist_ok=True)
+ self.json_file = os.path.join(self.log_path, 'test_results.json')
+ for a in self.android_devices:
+ a.ed.clear_all_events()
+ if not wifi_connection_check(self.pri_ad, self.network['SSID']):
+ self.log.error('Wifi connection does not exist')
+ return False
+ if not enable_bluetooth(self.pri_ad.droid, self.pri_ad.ed):
+ self.log.error('Failed to enable bluetooth')
+ return False
+ if hasattr(self, 'required_devices'):
+ if ('discovery' in self.current_test_name or
+ 'ble' in self.current_test_name):
+ self.create_android_relay_object()
+ else:
+ self.log.warning('required_devices is not given in config file')
+
+ def teardown_test(self):
+ self.parsing_results()
+ with open(self.json_file, 'a') as results_file:
+ json.dump(self.result, results_file, indent=4, sort_keys=True)
+ if not disable_bluetooth(self.pri_ad.droid):
+ self.log.info('Failed to disable bluetooth')
+ return False
+ self.destroy_android_and_relay_object()
+
+ def teardown_class(self):
+ if hasattr(self, 'AccessPoint'):
+ self.ap.close()
+ self.reset_wifi_and_store_results()
+
+ def reset_wifi_and_store_results(self):
+ """Resets wifi and store test results."""
+ reset_wifi(self.pri_ad)
+ wifi_toggle_state(self.pri_ad, False)
+
+ def create_android_relay_object(self):
+ """Creates android device object and relay device object if required
+ devices has android device and relay device."""
+ if 'AndroidDevice' in self.required_devices:
+ self.inquiry_devices = android_device.create(
+ self.required_devices['AndroidDevice'])
+ self.dev_list['AndroidDevice'] = self.inquiry_devices
+ if 'RelayDevice' in self.required_devices:
+ self.relay = relay_device_controller.create(
+ self.required_devices['RelayDevice'])
+ self.dev_list['RelayDevice'] = self.relay
+
+ def destroy_android_and_relay_object(self):
+ """Destroys android device object and relay device object if required
+ devices has android device and relay device."""
+ if hasattr(self, 'required_devices'):
+ if ('discovery' in self.current_test_name or
+ 'ble' in self.current_test_name):
+ if hasattr(self, 'inquiry_devices'):
+ for device in range(len(self.inquiry_devices)):
+ inquiry_device = self.inquiry_devices[device]
+ if not disable_bluetooth(inquiry_device.droid):
+ self.log.info('Failed to disable bluetooth')
+ android_device.destroy(self.inquiry_devices)
+ if hasattr(self, 'relay'):
+ relay_device_controller.destroy(self.relay)
+
+ def parsing_results(self):
+ """Result parser for fping results and a2dp packet drops."""
+ if 'fping' in self.current_test_name:
+ output_path = '{}{}{}'.format(self.pri_ad.log_path, '/Fping/',
+ 'fping_%s.txt' % self.counter)
+ self.result['fping_loss%'] = parse_fping_results(
+ self.fping_params['fping_drop_tolerance'], output_path)
+ self.counter = +1
+ if 'a2dp_streaming' in self.current_test_name:
+ file_path = collect_bluetooth_manager_dumpsys_logs(
+ self.pri_ad, self.current_test_name)
+ self.result['a2dp_packet_drop'] = (
+ self.a2dp_dumpsys.parse(file_path))
+ if self.result['a2dp_packet_drop'] == 0:
+ self.result['a2dp_packet_drop'] = None
+
+ def run_iperf_and_get_result(self):
+ """Frames iperf command based on test and starts iperf client on
+ host machine.
+
+ Returns:
+ throughput: Throughput of the run.
+ """
+ self.iperf_server.start(tag=self.tag)
+ if self.iperf_variables.stream == 'ul':
+ iperf_args = get_iperf_arg_string(
+ duration=self.iperf['duration'],
+ reverse_direction=1,
+ traffic_type=self.iperf_variables.protocol
+ )
+ elif self.iperf_variables.stream == 'dl':
+ iperf_args = get_iperf_arg_string(
+ duration=self.iperf['duration'],
+ reverse_direction=0,
+ traffic_type=self.iperf_variables.protocol
+ )
+ ip = get_phone_ip(self.pri_ad)
+ self.tag = self.tag + 1
+ self.iperf_variables.iperf_client_path = (
+ self.iperf_client.start(ip, iperf_args, self.tag))
+
+ self.iperf_server.stop()
+ if (self.iperf_variables.protocol == 'udp' and
+ self.iperf_variables.stream == 'ul'):
+ throughput = iperf_result(
+ self.log, self.iperf_variables.protocol,
+ self.iperf_variables.iperf_server_path)
+ else:
+ throughput = iperf_result(self.log,
+ self.iperf_variables.protocol,
+ self.iperf_variables.iperf_client_path)
+
+ if not throughput:
+ self.log.error('Iperf failed/stopped')
+ self.iperf_variables.throughput.append(0)
+ else:
+ self.iperf_variables.throughput.append(
+ str(round(throughput, 2)) + "Mb/s")
+ self.log.info("Throughput: {} Mb/s".format(throughput))
+ self.result["throughput"] = self.iperf_variables.throughput
+ return throughput
+
+ def on_fail(self, test_name, begin_time):
+ """A function that is executed upon a test case failure.
+
+ Args:
+ test_name: Name of the test that triggered this function.
+ begin_time: Logline format timestamp taken when the test started.
+ """
+ self.log.info('Test {} failed, Fetching Btsnoop logs and bugreport'.
+ format(test_name))
+ take_btsnoop_logs(self.android_devices, self, test_name)
+ self._take_bug_report(test_name, begin_time)
+
+ def run_thread(self, kwargs):
+ """Convenience function to start thread.
+
+ Args:
+ kwargs: Function object to start in thread.
+ """
+ for function in kwargs:
+ self.thread = threading.Thread(target=function)
+ self.thread_list.append(self.thread)
+ self.thread.start()
+
+ def teardown_thread(self):
+ """Convenience function to join thread."""
+ for thread_id in self.thread_list:
+ if thread_id.is_alive():
+ thread_id.join()
+
+ def get_call_volume(self):
+ """Function to get call volume when bluetooth headset connected.
+
+ Returns:
+ Call volume.
+ """
+ return self.pri_ad.adb.shell(
+ 'settings list system|grep volume_bluetooth_sco_bt_sco_hs')
+
+ def change_volume(self):
+ """Changes volume with HFP call.
+
+ Returns: True if successful, otherwise False.
+ """
+ if 'Volume_up' and 'Volume_down' in (
+ self.relay_devices[0].relays.keys()):
+ current_volume = self.get_call_volume()
+ self.audio_receiver.press_volume_down()
+ time.sleep(AVRCP_WAIT_TIME) # wait till volume_changes
+ if current_volume == self.get_call_volume():
+ self.log.error('Decrease volume failed')
+ return False
+ time.sleep(AVRCP_WAIT_TIME)
+ current_volume = self.get_call_volume()
+ self.audio_receiver.press_volume_up()
+ time.sleep(AVRCP_WAIT_TIME) # wait till volume_changes
+ if current_volume == self.get_call_volume():
+ self.log.error('Increase volume failed')
+ return False
+ else:
+ self.log.warning(
+ 'No volume control pins specified in relay config.')
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/coex/CoexPerformanceBaseTest.py b/acts_tests/acts_contrib/test_utils/coex/CoexPerformanceBaseTest.py
new file mode 100644
index 0000000..3180660
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/CoexPerformanceBaseTest.py
@@ -0,0 +1,508 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import json
+import os
+import time
+from collections import defaultdict
+
+from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.coex.audio_test_utils import AudioCaptureResult
+from acts_contrib.test_utils.coex.audio_test_utils import get_audio_capture_device
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import bokeh_chart_plot
+from acts_contrib.test_utils.coex.coex_test_utils import collect_bluetooth_manager_dumpsys_logs
+from acts_contrib.test_utils.coex.coex_test_utils import multithread_func
+from acts_contrib.test_utils.coex.coex_test_utils import wifi_connection_check
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_test_device_init
+from acts.utils import get_current_epoch_time
+
+RSSI_POLL_RESULTS = 'Monitoring , Handle: 0x0003, POLL'
+RSSI_RESULTS = 'Monitoring , Handle: 0x0003, '
+
+
+def get_atten_range(start, stop, step):
+ """Function to derive attenuation range for tests.
+
+ Args:
+ start: Start attenuation value.
+ stop: Stop attenuation value.
+ step: Step attenuation value.
+ """
+ temp = start
+ while temp < stop:
+ yield temp
+ temp += step
+
+
+class CoexPerformanceBaseTest(CoexBaseTest):
+ """Base test class for performance tests.
+
+ Attributes:
+ rvr : Dict to save attenuation, throughput, fixed_attenuation.
+ a2dp_streaming : Used to denote a2dp test cases.
+ """
+
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.a2dp_streaming = False
+ self.rvr = {}
+ self.bt_range_metric = BlackboxMetricLogger.for_test_case(
+ metric_name='bt_range')
+ self.wifi_max_atten_metric = BlackboxMetricLogger.for_test_case(
+ metric_name='wifi_max_atten')
+ self.wifi_min_atten_metric = BlackboxMetricLogger.for_test_case(
+ metric_name='wifi_min_atten')
+ self.wifi_range_metric = BlackboxMetricLogger.for_test_case(
+ metric_name='wifi_range_metric')
+
+ def setup_class(self):
+ req_params = ['test_params', 'Attenuator']
+ opt_params = ['audio_params']
+ self.unpack_userparams(req_params, opt_params)
+ if hasattr(self, 'Attenuator'):
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ else:
+ self.log.error('Attenuator should be connected to run tests.')
+ return False
+ for i in range(self.num_atten):
+ self.attenuators[i].set_atten(0)
+ super().setup_class()
+ self.performance_files_list = []
+ if "performance_result_path" in self.user_params["test_params"]:
+ self.performance_files_list = [
+ os.path.join(self.test_params["performance_result_path"],
+ files) for files in os.listdir(
+ self.test_params["performance_result_path"])
+ ]
+ self.bt_atten_range = list(get_atten_range(
+ self.test_params["bt_atten_start"],
+ self.test_params["bt_atten_stop"],
+ self.test_params["bt_atten_step"]))
+ self.wifi_atten_range = list(get_atten_range(
+ self.test_params["attenuation_start"],
+ self.test_params["attenuation_stop"],
+ self.test_params["attenuation_step"]))
+
+ def setup_test(self):
+ if ('a2dp_streaming' in self.current_test_name and
+ hasattr(self, 'audio_params')):
+ self.audio = get_audio_capture_device(self.sec_ad, self.audio_params)
+ self.a2dp_streaming = True
+ for i in range(self.num_atten):
+ self.attenuators[i].set_atten(0)
+ if not wifi_connection_check(self.pri_ad, self.network["SSID"]):
+ wifi_connect(self.pri_ad, self.network, num_of_tries=5)
+ super().setup_test()
+
+ def teardown_test(self):
+ self.performance_baseline_check()
+ for i in range(self.num_atten):
+ self.attenuators[i].set_atten(0)
+ current_atten = int(self.attenuators[i].get_atten())
+ self.log.debug(
+ "Setting attenuation to zero : Current atten {} : {}".format(
+ self.attenuators[i], current_atten))
+ self.a2dp_streaming = False
+ if not disable_bluetooth(self.pri_ad.droid):
+ self.log.info("Failed to disable bluetooth")
+ return False
+ self.destroy_android_and_relay_object()
+ self.rvr = {}
+
+ def teardown_class(self):
+ self.reset_wifi_and_store_results()
+
+ def set_attenuation_and_run_iperf(self, called_func=None):
+ """Sets attenuation and runs iperf for Attenuation max value.
+
+ Args:
+ called_func : Function object to run.
+
+ Returns:
+ True if Pass
+ False if Fail
+ """
+ self.attenuators[self.num_atten - 1].set_atten(0)
+ self.rvr["bt_attenuation"] = []
+ self.rvr["test_name"] = self.current_test_name
+ self.rvr["bt_gap_analysis"] = {}
+ self.rvr["bt_range"] = []
+ status_flag = True
+ for bt_atten in self.bt_atten_range:
+ self.rvr[bt_atten] = {}
+ self.rvr[bt_atten]["fixed_attenuation"] = (
+ self.test_params["fixed_attenuation"][str(
+ self.network["channel"])])
+ self.log.info('Setting bt attenuation to: {} dB'.format(bt_atten))
+ self.attenuators[self.num_atten - 1].set_atten(bt_atten)
+ for i in range(self.num_atten - 1):
+ self.attenuators[i].set_atten(0)
+ if not wifi_connection_check(self.pri_ad, self.network["SSID"]):
+ wifi_test_device_init(self.pri_ad)
+ wifi_connect(self.pri_ad, self.network, num_of_tries=5)
+ adb_rssi_results = self.pri_ad.search_logcat(RSSI_RESULTS)
+ if adb_rssi_results:
+ self.log.debug(adb_rssi_results[-1])
+ self.log.info('Android device: {}'.format(
+ (adb_rssi_results[-1]['log_message']).split(',')[5]))
+ (self.rvr[bt_atten]["throughput_received"],
+ self.rvr[bt_atten]["a2dp_packet_drop"],
+ status_flag) = self.rvr_throughput(bt_atten, called_func)
+ self.wifi_max_atten_metric.metric_value = max(self.rvr[bt_atten]
+ ["attenuation"])
+ self.wifi_min_atten_metric.metric_value = min(self.rvr[bt_atten]
+ ["attenuation"])
+
+ if self.rvr[bt_atten]["throughput_received"]:
+ for i, atten in enumerate(self.rvr[bt_atten]["attenuation"]):
+ if self.rvr[bt_atten]["throughput_received"][i] < 1.0:
+ self.wifi_range_metric.metric_value = (
+ self.rvr[bt_atten]["attenuation"][i-1])
+ break
+ else:
+ self.wifi_range_metric.metric_value = max(
+ self.rvr[bt_atten]["attenuation"])
+ else:
+ self.wifi_range_metric.metric_value = max(
+ self.rvr[bt_atten]["attenuation"])
+ if self.a2dp_streaming:
+ if not any(x > 0 for x in self.a2dp_dropped_list):
+ self.rvr[bt_atten]["a2dp_packet_drop"] = []
+ if not self.rvr["bt_range"]:
+ self.rvr["bt_range"].append(0)
+ return status_flag
+
+ def rvr_throughput(self, bt_atten, called_func=None):
+ """Sets attenuation and runs the function passed.
+
+ Args:
+ bt_atten: Bluetooth attenuation.
+ called_func: Functions object to run parallely.
+
+ Returns:
+ Throughput, a2dp_drops and True/False.
+ """
+ self.iperf_received = []
+ self.iperf_variables.received = []
+ self.a2dp_dropped_list = []
+ self.rvr["bt_attenuation"].append(bt_atten)
+ self.rvr[bt_atten]["audio_artifacts"] = {}
+ self.rvr[bt_atten]["attenuation"] = []
+ self.rvr["bt_gap_analysis"][bt_atten] = {}
+ for atten in self.wifi_atten_range:
+ tag = '{}_{}'.format(bt_atten, atten)
+ self.rvr[bt_atten]["attenuation"].append(
+ atten + self.rvr[bt_atten]["fixed_attenuation"])
+ self.log.info('Setting wifi attenuation to: {} dB'.format(atten))
+ for i in range(self.num_atten - 1):
+ self.attenuators[i].set_atten(atten)
+ if not wifi_connection_check(self.pri_ad, self.network["SSID"]):
+ self.iperf_received.append(0)
+ return self.iperf_received, self.a2dp_dropped_list, False
+ time.sleep(5) # Time for attenuation to set.
+ begin_time = get_current_epoch_time()
+ if self.a2dp_streaming:
+ self.audio.start()
+ if called_func:
+ if not multithread_func(self.log, called_func):
+ self.iperf_received.append(float(str(
+ self.iperf_variables.received[-1]).strip("Mb/s")))
+ return self.iperf_received, self.a2dp_dropped_list, False
+ else:
+ self.run_iperf_and_get_result()
+
+ adb_rssi_poll_results = self.pri_ad.search_logcat(
+ RSSI_POLL_RESULTS, begin_time)
+ adb_rssi_results = self.pri_ad.search_logcat(
+ RSSI_RESULTS, begin_time)
+ if adb_rssi_results:
+ self.log.debug(adb_rssi_poll_results)
+ self.log.debug(adb_rssi_results[-1])
+ self.log.info('Android device: {}'.format((
+ adb_rssi_results[-1]['log_message']).split(',')[5]))
+ if self.a2dp_streaming:
+ self.path = self.audio.stop()
+ analysis_path = AudioCaptureResult(
+ self.path).audio_quality_analysis(self.audio_params)
+ with open(analysis_path) as f:
+ self.rvr[bt_atten]["audio_artifacts"][atten] = f.readline()
+ content = json.loads(self.rvr[bt_atten]["audio_artifacts"][atten])
+ self.rvr["bt_gap_analysis"][bt_atten][atten] = {}
+ for idx, data in enumerate(content["quality_result"]):
+ if data['artifacts']['delay_during_playback']:
+ self.rvr["bt_gap_analysis"][bt_atten][atten][idx] = (
+ data['artifacts']['delay_during_playback'])
+ self.rvr["bt_range"].append(bt_atten)
+ else:
+ self.rvr["bt_gap_analysis"][bt_atten][atten][idx] = 0
+ file_path = collect_bluetooth_manager_dumpsys_logs(
+ self.pri_ad, self.current_test_name)
+ self.a2dp_dropped_list.append(
+ self.a2dp_dumpsys.parse(file_path))
+ self.iperf_received.append(
+ float(str(self.iperf_variables.throughput[-1]).strip("Mb/s")))
+ for i in range(self.num_atten - 1):
+ self.attenuators[i].set_atten(0)
+ return self.iperf_received, self.a2dp_dropped_list, True
+
+ def performance_baseline_check(self):
+ """Checks for performance_result_path in config. If present, plots
+ comparision chart else plot chart for that particular test run.
+
+ Returns:
+ True if success, False otherwise.
+ """
+ if self.rvr:
+ with open(self.json_file, 'a') as results_file:
+ json.dump({str(k): v for k, v in self.rvr.items()},
+ results_file, indent=4, sort_keys=True)
+ self.bt_range_metric.metric_value = self.rvr["bt_range"][0]
+ self.log.info('First occurrence of audio gap in bt '
+ 'range: {}'.format(self.bt_range_metric.metric_value))
+ self.log.info('Bluetooth min range: '
+ '{} dB'.format(min(self.rvr['bt_attenuation'])))
+ self.log.info('Bluetooth max range: '
+ '{} dB'.format(max(self.rvr['bt_attenuation'])))
+ self.plot_graph_for_attenuation()
+ if not self.performance_files_list:
+ self.log.warning('Performance file list is empty. Could not '
+ 'calculate throughput limits')
+ return
+ self.throughput_pass_fail_check()
+ else:
+ self.log.error("Throughput dict empty!")
+ return False
+ return True
+
+ def plot_graph_for_attenuation(self):
+ """Plots graph and add as JSON formatted results for attenuation with
+ respect to its iperf values.
+ """
+ data_sets = defaultdict(dict)
+ legends = defaultdict(list)
+
+ x_label = 'WIFI Attenuation (dB)'
+ y_label = []
+
+ fig_property = {
+ "title": self.current_test_name,
+ "x_label": x_label,
+ "linewidth": 3,
+ "markersize": 10
+ }
+
+ for bt_atten in self.rvr["bt_attenuation"]:
+ y_label.insert(0, 'Throughput (Mbps)')
+ legends[bt_atten].insert(
+ 0, str("BT Attenuation @ %sdB" % bt_atten))
+ data_sets[bt_atten]["attenuation"] = (
+ self.rvr[bt_atten]["attenuation"])
+ data_sets[bt_atten]["throughput_received"] = (
+ self.rvr[bt_atten]["throughput_received"])
+
+ if self.a2dp_streaming:
+ for bt_atten in self.bt_atten_range:
+ legends[bt_atten].insert(
+ 0, ('Packet drops(in %) @ {}dB'.format(bt_atten)))
+ data_sets[bt_atten]["a2dp_attenuation"] = (
+ self.rvr[bt_atten]["attenuation"])
+ data_sets[bt_atten]["a2dp_packet_drops"] = (
+ self.rvr[bt_atten]["a2dp_packet_drop"])
+ y_label.insert(0, "Packets Dropped")
+ fig_property["y_label"] = y_label
+ shaded_region = None
+
+ if "performance_result_path" in self.user_params["test_params"]:
+ shaded_region = self.comparision_results_calculation(data_sets, legends)
+
+ output_file_path = os.path.join(self.pri_ad.log_path,
+ self.current_test_name,
+ "attenuation_plot.html")
+ bokeh_chart_plot(list(self.rvr["bt_attenuation"]),
+ data_sets,
+ legends,
+ fig_property,
+ shaded_region=shaded_region,
+ output_file_path=output_file_path)
+
+ def comparision_results_calculation(self, data_sets, legends):
+ """Compares rvr results with baseline values by calculating throughput
+ limits.
+
+ Args:
+ data_sets: including lists of x_data and lists of y_data.
+ ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]]
+ legends: list of legend for each curve.
+
+ Returns:
+ None if test_file is not found, otherwise shaded_region
+ will be returned.
+ """
+ try:
+ attenuation_path = next(
+ file_name for file_name in self.performance_files_list
+ if self.current_test_name in file_name
+ )
+ except StopIteration:
+ self.log.warning("Test_file not found. "
+ "No comparision values to calculate")
+ return
+ with open(attenuation_path, 'r') as throughput_file:
+ throughput_results = json.load(throughput_file)
+ for bt_atten in self.bt_atten_range:
+ throughput_received = []
+ user_attenuation = []
+ legends[bt_atten].insert(
+ 0, ('Performance Results @ {}dB'.format(bt_atten)))
+ for att in self.rvr[bt_atten]["attenuation"]:
+ attenuation = att - self.rvr[bt_atten]["fixed_attenuation"]
+ throughput_received.append(throughput_results[str(bt_atten)]
+ ["throughput_received"][attenuation])
+ user_attenuation.append(att)
+ data_sets[bt_atten][
+ "user_attenuation"] = user_attenuation
+ data_sets[bt_atten]["user_throughput"] = throughput_received
+ throughput_limits = self.get_throughput_limits(attenuation_path)
+ shaded_region = defaultdict(dict)
+ for bt_atten in self.bt_atten_range:
+ shaded_region[bt_atten] = {
+ "x_vector": throughput_limits[bt_atten]["attenuation"],
+ "lower_limit":
+ throughput_limits[bt_atten]["lower_limit"],
+ "upper_limit":
+ throughput_limits[bt_atten]["upper_limit"]
+ }
+ return shaded_region
+
+ def total_attenuation(self, performance_dict):
+ """Calculates attenuation with adding fixed attenuation.
+
+ Args:
+ performance_dict: dict containing attenuation and fixed attenuation.
+
+ Returns:
+ Total attenuation is returned.
+ """
+ if "fixed_attenuation" in self.test_params:
+ total_atten = [
+ att + performance_dict["fixed_attenuation"]
+ for att in performance_dict["attenuation"]
+ ]
+ return total_atten
+
+ def throughput_pass_fail_check(self):
+ """Check the test result and decide if it passed or failed
+ by comparing with throughput limits.The pass/fail tolerances are
+ provided in the config file.
+
+ Returns:
+ None if test_file is not found, True if successful,
+ False otherwise.
+ """
+ try:
+ performance_path = next(
+ file_name for file_name in self.performance_files_list
+ if self.current_test_name in file_name
+ )
+ except StopIteration:
+ self.log.warning("Test_file not found. Couldn't "
+ "calculate throughput limits")
+ return
+ throughput_limits = self.get_throughput_limits(performance_path)
+
+ failure_count = 0
+ for bt_atten in self.bt_atten_range:
+ for idx, current_throughput in enumerate(
+ self.rvr[bt_atten]["throughput_received"]):
+ current_att = self.rvr[bt_atten]["attenuation"][idx]
+ if (current_throughput <
+ (throughput_limits[bt_atten]["lower_limit"][idx]) or
+ current_throughput >
+ (throughput_limits[bt_atten]["upper_limit"][idx])):
+ failure_count = failure_count + 1
+ self.log.info(
+ "Throughput at {} dB attenuation is beyond limits. "
+ "Throughput is {} Mbps. Expected within [{}, {}] Mbps.".
+ format(
+ current_att, current_throughput,
+ throughput_limits[bt_atten]["lower_limit"][idx],
+ throughput_limits[bt_atten]["upper_limit"][
+ idx]))
+ if failure_count >= self.test_params["failure_count_tolerance"]:
+ self.log.error(
+ "Test failed. Found {} points outside throughput limits.".
+ format(failure_count))
+ return False
+ self.log.info(
+ "Test passed. Found {} points outside throughput limits.".
+ format(failure_count))
+ return True
+
+ def get_throughput_limits(self, performance_path):
+ """Compute throughput limits for current test.
+
+ Checks the RvR test result and compares to a throughput limits for
+ the same configuration. The pass/fail tolerances are provided in the
+ config file.
+
+ Args:
+ performance_path: path to baseline file used to generate limits
+
+ Returns:
+ throughput_limits: dict containing attenuation and throughput
+ limit data
+ """
+ with open(performance_path, 'r') as performance_file:
+ performance_results = json.load(performance_file)
+ throughput_limits = defaultdict(dict)
+ for bt_atten in self.bt_atten_range:
+ performance_attenuation = (self.total_attenuation(
+ performance_results[str(bt_atten)]))
+ attenuation = []
+ lower_limit = []
+ upper_limit = []
+ for idx, current_throughput in enumerate(
+ self.rvr[bt_atten]["throughput_received"]):
+ current_att = self.rvr[bt_atten]["attenuation"][idx]
+ att_distances = [
+ abs(current_att - performance_att)
+ for performance_att in performance_attenuation
+ ]
+ sorted_distances = sorted(
+ enumerate(att_distances), key=lambda x: x[1])
+ closest_indeces = [dist[0] for dist in sorted_distances[0:3]]
+ closest_throughputs = [
+ performance_results[str(bt_atten)]["throughput_received"][
+ index] for index in closest_indeces
+ ]
+ closest_throughputs.sort()
+ attenuation.append(current_att)
+ lower_limit.append(
+ max(closest_throughputs[0] -
+ max(self.test_params["abs_tolerance"],
+ closest_throughputs[0] *
+ self.test_params["pct_tolerance"] / 100), 0))
+ upper_limit.append(closest_throughputs[-1] + max(
+ self.test_params["abs_tolerance"], closest_throughputs[-1] *
+ self.test_params["pct_tolerance"] / 100))
+ throughput_limits[bt_atten]["attenuation"] = attenuation
+ throughput_limits[bt_atten]["lower_limit"] = lower_limit
+ throughput_limits[bt_atten]["upper_limit"] = upper_limit
+ return throughput_limits
+
diff --git a/acts_tests/acts_contrib/test_utils/coex/__init__.py b/acts_tests/acts_contrib/test_utils/coex/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/coex/audio_capture_device.py b/acts_tests/acts_contrib/test_utils/coex/audio_capture_device.py
new file mode 100644
index 0000000..e76d054
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/audio_capture_device.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import pyaudio
+import wave
+
+from acts import context
+
+
+WAVE_FILE_TEMPLATE = 'recorded_audio_%s.wav'
+ADB_PATH = 'sdcard/Music/'
+ADB_FILE = 'rec.pcm'
+
+
+class AudioCaptureBase(object):
+ """Base class for Audio capture."""
+
+ def __init__(self):
+
+ self.wave_file = os.path.join(self.log_path, WAVE_FILE_TEMPLATE)
+ self.file_dir = self.log_path
+
+ @property
+ def log_path(self):
+ """Returns current log path."""
+ current_context = context.get_current_context()
+ full_out_dir = os.path.join(current_context.get_full_output_path(),
+ 'AudioCapture')
+
+ os.makedirs(full_out_dir, exist_ok=True)
+ return full_out_dir
+
+ @property
+ def next_fileno(self):
+ counter = 0
+ while os.path.exists(self.wave_file % counter):
+ counter += 1
+ return counter
+
+ @property
+ def last_fileno(self):
+ return self.next_fileno - 1
+
+ @property
+ def get_last_record_duration_millis(self):
+ """Get duration of most recently recorded file.
+
+ Returns:
+ duration (float): duration of recorded file in milliseconds.
+ """
+ latest_file_path = self.wave_file % self.last_fileno
+ print (latest_file_path)
+ with wave.open(latest_file_path, 'r') as f:
+ frames = f.getnframes()
+ rate = f.getframerate()
+ duration = (frames / float(rate)) * 1000
+ return duration
+
+ def write_record_file(self, audio_params, frames):
+ """Writes the recorded audio into the file.
+
+ Args:
+ audio_params: A dict with audio configuration.
+ frames: Recorded audio frames.
+
+ Returns:
+ file_name: wave file name.
+ """
+ file_name = self.wave_file % self.next_fileno
+ logging.debug('writing to %s' % file_name)
+ wf = wave.open(file_name, 'wb')
+ wf.setnchannels(audio_params['channel'])
+ wf.setsampwidth(audio_params['sample_width'])
+ wf.setframerate(audio_params['sample_rate'])
+ wf.writeframes(frames)
+ wf.close()
+ return file_name
+
+
+class CaptureAudioOverAdb(AudioCaptureBase):
+ """Class to capture audio over android device which acts as the
+ a2dp sink or hfp client. This captures the digital audio and converts
+ to analog audio for post processing.
+ """
+
+ def __init__(self, ad, audio_params):
+ """Initializes CaptureAudioOverAdb.
+
+ Args:
+ ad: An android device object.
+ audio_params: Dict containing audio record settings.
+ """
+ super().__init__()
+ self._ad = ad
+ self.audio_params = audio_params
+ self.adb_path = None
+
+ def start(self):
+ """Start the audio capture over adb."""
+ self.adb_path = os.path.join(ADB_PATH, ADB_FILE)
+ cmd = 'ap2f --usage 1 --start --duration {} --target {}'.format(
+ self.audio_params['duration'], self.adb_path,
+ )
+ self._ad.adb.shell_nb(cmd)
+
+ def stop(self):
+ """Stops the audio capture and stores it in wave file.
+
+ Returns:
+ File name of the recorded file.
+ """
+ cmd = '{} {}'.format(self.adb_path, self.file_dir)
+ self._ad.adb.pull(cmd)
+ self._ad.adb.shell('rm {}'.format(self.adb_path))
+ return self._convert_pcm_to_wav()
+
+ def _convert_pcm_to_wav(self):
+ """Converts raw pcm data into wave file.
+
+ Returns:
+ file_path: Returns the file path of the converted file
+ (digital to analog).
+ """
+ file_to_read = os.path.join(self.file_dir, ADB_FILE)
+ with open(file_to_read, 'rb') as pcm_file:
+ frames = pcm_file.read()
+ file_path = self.write_record_file(self.audio_params, frames)
+ return file_path
+
+
+class CaptureAudioOverLocal(AudioCaptureBase):
+ """Class to capture audio on local server using the audio input devices
+ such as iMic/AudioBox. This class mandates input deivce to be connected to
+ the machine.
+ """
+ def __init__(self, audio_params):
+ """Initializes CaptureAudioOverLocal.
+
+ Args:
+ audio_params: Dict containing audio record settings.
+ """
+ super().__init__()
+ self.audio_params = audio_params
+ self.channels = self.audio_params['channel']
+ self.chunk = self.audio_params['chunk']
+ self.sample_rate = self.audio_params['sample_rate']
+ self.__input_device = None
+ self.audio = None
+ self.frames = []
+
+ @property
+ def name(self):
+ return self.__input_device["name"]
+
+ def __get_input_device(self):
+ """Checks for the audio capture device."""
+ if self.__input_device is None:
+ for i in range(self.audio.get_device_count()):
+ device_info = self.audio.get_device_info_by_index(i)
+ logging.debug('Device Information: {}'.format(device_info))
+ if self.audio_params['input_device'] in device_info['name']:
+ self.__input_device = device_info
+ break
+ else:
+ raise DeviceNotFound(
+ 'Audio Capture device {} not found.'.format(
+ self.audio_params['input_device']))
+ return self.__input_device
+
+ def start(self, trim_beginning=0, trim_end=0):
+ """Starts audio recording on host machine.
+
+ Args:
+ trim_beginning: how many seconds to trim from the beginning
+ trim_end: how many seconds to trim from the end
+ """
+ self.audio = pyaudio.PyAudio()
+ self.__input_device = self.__get_input_device()
+ stream = self.audio.open(
+ format=pyaudio.paInt16,
+ channels=self.channels,
+ rate=self.sample_rate,
+ input=True,
+ frames_per_buffer=self.chunk,
+ input_device_index=self.__input_device['index'])
+ b_chunks = trim_beginning * (self.sample_rate // self.chunk)
+ e_chunks = trim_end * (self.sample_rate // self.chunk)
+ total_chunks = self.sample_rate // self.chunk * self.audio_params[
+ 'duration']
+ for i in range(total_chunks):
+ try:
+ data = stream.read(self.chunk, exception_on_overflow=False)
+ except IOError as ex:
+ logging.error('Cannot record audio: {}'.format(ex))
+ return False
+ if b_chunks <= i < total_chunks - e_chunks:
+ self.frames.append(data)
+
+ stream.stop_stream()
+ stream.close()
+
+ def stop(self):
+ """Terminates the pulse audio instance.
+
+ Returns:
+ File name of the recorded audio file.
+ """
+ self.audio.terminate()
+ frames = b''.join(self.frames)
+ return self.write_record_file(self.audio_params, frames)
+
+
+class DeviceNotFound(Exception):
+ """Raises exception if audio capture device is not found."""
diff --git a/acts_tests/acts_contrib/test_utils/coex/audio_test_utils.py b/acts_tests/acts_contrib/test_utils/coex/audio_test_utils.py
new file mode 100644
index 0000000..39543a3
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/audio_test_utils.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import logging
+import numpy
+import os
+import scipy.io.wavfile as sciwav
+
+from acts_contrib.test_utils.coex.audio_capture_device import AudioCaptureBase
+from acts_contrib.test_utils.coex.audio_capture_device import CaptureAudioOverAdb
+from acts_contrib.test_utils.coex.audio_capture_device import CaptureAudioOverLocal
+from acts_contrib.test_utils.audio_analysis_lib import audio_analysis
+from acts_contrib.test_utils.audio_analysis_lib.check_quality import quality_analysis
+
+ANOMALY_DETECTION_BLOCK_SIZE = audio_analysis.ANOMALY_DETECTION_BLOCK_SIZE
+ANOMALY_GROUPING_TOLERANCE = audio_analysis.ANOMALY_GROUPING_TOLERANCE
+PATTERN_MATCHING_THRESHOLD = audio_analysis.PATTERN_MATCHING_THRESHOLD
+ANALYSIS_FILE_TEMPLATE = "audio_analysis_%s.txt"
+bits_per_sample = 32
+
+
+def get_audio_capture_device(ad, audio_params):
+ """Gets the device object of the audio capture device connected to server.
+
+ The audio capture device returned is specified by the audio_params
+ within user_params. audio_params must specify a "type" field, that
+ is either "AndroidDevice" or "Local"
+
+ Args:
+ ad: Android Device object.
+ audio_params: object containing variables to record audio.
+
+ Returns:
+ Object of the audio capture device.
+
+ Raises:
+ ValueError if audio_params['type'] is not "AndroidDevice" or
+ "Local".
+ """
+
+ if audio_params['type'] == 'AndroidDevice':
+ return CaptureAudioOverAdb(ad, audio_params)
+
+ elif audio_params['type'] == 'Local':
+ return CaptureAudioOverLocal(audio_params)
+
+ else:
+ raise ValueError('Unrecognized audio capture device '
+ '%s' % audio_params['type'])
+
+
+class FileNotFound(Exception):
+ """Raises Exception if file is not present"""
+
+
+class AudioCaptureResult(AudioCaptureBase):
+ def __init__(self, path, audio_params=None):
+ """Initializes Audio Capture Result class.
+
+ Args:
+ path: Path of audio capture result.
+ """
+ super().__init__()
+ self.path = path
+ self.audio_params = audio_params
+ self.analysis_path = os.path.join(self.log_path,
+ ANALYSIS_FILE_TEMPLATE)
+ if self.audio_params:
+ self._trim_wave_file()
+
+ def THDN(self, win_size=None, step_size=None, q=1, freq=None):
+ """Calculate THD+N value for most recently recorded file.
+
+ Args:
+ win_size: analysis window size (must be less than length of
+ signal). Used with step size to analyze signal piece by
+ piece. If not specified, entire signal will be analyzed.
+ step_size: number of samples to move window per-analysis. If not
+ specified, entire signal will be analyzed.
+ q: quality factor for the notch filter used to remove fundamental
+ frequency from signal to isolate noise.
+ freq: the fundamental frequency to remove from the signal. If none,
+ the fundamental frequency will be determined using FFT.
+ Returns:
+ channel_results (list): THD+N value for each channel's signal.
+ List index corresponds to channel index.
+ """
+ if not (win_size and step_size):
+ return audio_analysis.get_file_THDN(filename=self.path,
+ q=q,
+ freq=freq)
+ else:
+ return audio_analysis.get_file_max_THDN(filename=self.path,
+ step_size=step_size,
+ window_size=win_size,
+ q=q,
+ freq=freq)
+
+ def detect_anomalies(self,
+ freq=None,
+ block_size=ANOMALY_DETECTION_BLOCK_SIZE,
+ threshold=PATTERN_MATCHING_THRESHOLD,
+ tolerance=ANOMALY_GROUPING_TOLERANCE):
+ """Detect anomalies in most recently recorded file.
+
+ An anomaly is defined as a sample in a recorded sine wave that differs
+ by at least the value defined by the threshold parameter from a golden
+ generated sine wave of the same amplitude, sample rate, and frequency.
+
+ Args:
+ freq (int|float): fundamental frequency of the signal. All other
+ frequencies are noise. If None, will be calculated with FFT.
+ block_size (int): the number of samples to analyze at a time in the
+ anomaly detection algorithm.
+ threshold (float): the threshold of the correlation index to
+ determine if two sample values match.
+ tolerance (float): the sample tolerance for anomaly time values
+ to be grouped as the same anomaly
+ Returns:
+ channel_results (list): anomaly durations for each channel's signal.
+ List index corresponds to channel index.
+ """
+ return audio_analysis.get_file_anomaly_durations(filename=self.path,
+ freq=freq,
+ block_size=block_size,
+ threshold=threshold,
+ tolerance=tolerance)
+
+ @property
+ def analysis_fileno(self):
+ """Returns the file number to dump audio analysis results."""
+ counter = 0
+ while os.path.exists(self.analysis_path % counter):
+ counter += 1
+ return counter
+
+ def audio_quality_analysis(self):
+ """Measures audio quality based on the audio file given as input.
+
+ Returns:
+ analysis_path on success.
+ """
+ analysis_path = self.analysis_path % self.analysis_fileno
+ if not os.path.exists(self.path):
+ raise FileNotFound("Recorded file not found")
+ try:
+ quality_analysis(filename=self.path,
+ output_file=analysis_path,
+ bit_width=bits_per_sample,
+ rate=self.audio_params["sample_rate"],
+ channel=self.audio_params["channel"],
+ spectral_only=False)
+ except Exception as err:
+ logging.exception("Failed to analyze raw audio: %s" % err)
+ return analysis_path
+
+ def _trim_wave_file(self):
+ """Trim wave files.
+
+ """
+ original_record_file_name = 'original_' + os.path.basename(self.path)
+ original_record_file_path = os.path.join(os.path.dirname(self.path),
+ original_record_file_name)
+ os.rename(self.path, original_record_file_path)
+ fs, data = sciwav.read(original_record_file_path)
+ trim_start = self.audio_params['trim_start']
+ trim_end = self.audio_params['trim_end']
+ trim = numpy.array([[trim_start, trim_end]])
+ trim = trim * fs
+ new_wave_file_list = []
+ for elem in trim:
+ # To check start and end doesn't exceed raw data dimension
+ start_read = min(elem[0], data.shape[0] - 1)
+ end_read = min(elem[1], data.shape[0] - 1)
+ new_wave_file_list.extend(data[start_read:end_read])
+ new_wave_file = numpy.array(new_wave_file_list)
+
+ sciwav.write(self.path, fs, new_wave_file)
diff --git a/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py b/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py
new file mode 100644
index 0000000..62e12af
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py
@@ -0,0 +1,1331 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License
+
+import json
+import logging
+import math
+import os
+import re
+import time
+
+from acts import asserts
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib import hostapd_security
+from acts.controllers.utils_lib.ssh import connection
+from acts.controllers.utils_lib.ssh import settings
+from acts.controllers.iperf_server import IPerfResult
+from acts.libs.proc import job
+from acts_contrib.test_utils.bt.bt_constants import (
+ bluetooth_profile_connection_state_changed)
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_constants
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_states
+from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import is_a2dp_src_device_connected
+from acts_contrib.test_utils.bt.bt_test_utils import is_a2dp_snk_device_connected
+from acts_contrib.test_utils.bt.bt_test_utils import is_hfp_client_device_connected
+from acts_contrib.test_utils.bt.bt_test_utils import is_map_mce_device_connected
+from acts_contrib.test_utils.bt.bt_test_utils import is_map_mse_device_connected
+from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
+from acts_contrib.test_utils.car.car_telecom_utils import wait_for_active
+from acts_contrib.test_utils.car.car_telecom_utils import wait_for_dialing
+from acts_contrib.test_utils.car.car_telecom_utils import wait_for_not_in_call
+from acts_contrib.test_utils.car.car_telecom_utils import wait_for_ringing
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.wifi.wifi_power_test_utils import get_phone_ip
+from acts_contrib.test_utils.wifi.wifi_test_utils import reset_wifi
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_test_device_init
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts.utils import exe_cmd
+from bokeh.layouts import column
+from bokeh.models import tools as bokeh_tools
+from bokeh.plotting import figure, output_file, save
+
+THROUGHPUT_THRESHOLD = 100
+AP_START_TIME = 10
+DISCOVERY_TIME = 10
+BLUETOOTH_WAIT_TIME = 2
+AVRCP_WAIT_TIME = 3
+
+
+def avrcp_actions(pri_ad, bt_device):
+ """Performs avrcp controls like volume up, volume down, skip next and
+ skip previous.
+
+ Args:
+ pri_ad: Android device.
+ bt_device: bt device instance.
+
+ Returns:
+ True if successful, otherwise False.
+ """
+ current_volume = pri_ad.droid.getMediaVolume()
+ for _ in range(5):
+ bt_device.volume_up()
+ time.sleep(AVRCP_WAIT_TIME)
+ if current_volume == pri_ad.droid.getMediaVolume():
+ pri_ad.log.error("Increase volume failed")
+ return False
+ time.sleep(AVRCP_WAIT_TIME)
+ current_volume = pri_ad.droid.getMediaVolume()
+ for _ in range(5):
+ bt_device.volume_down()
+ time.sleep(AVRCP_WAIT_TIME)
+ if current_volume == pri_ad.droid.getMediaVolume():
+ pri_ad.log.error("Decrease volume failed")
+ return False
+
+ #TODO: (sairamganesh) validate next and previous calls.
+ bt_device.next()
+ time.sleep(AVRCP_WAIT_TIME)
+ bt_device.previous()
+ time.sleep(AVRCP_WAIT_TIME)
+ return True
+
+
+def connect_ble(pri_ad, sec_ad):
+ """Connect BLE device from DUT.
+
+ Args:
+ pri_ad: An android device object.
+ sec_ad: An android device object.
+
+ Returns:
+ True if successful, otherwise False.
+ """
+ adv_instances = []
+ gatt_server_list = []
+ bluetooth_gatt_list = []
+ pri_ad.droid.bluetoothEnableBLE()
+ sec_ad.droid.bluetoothEnableBLE()
+ gatt_server_cb = sec_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = sec_ad.droid.gattServerOpenGattServer(gatt_server_cb)
+ gatt_server_list.append(gatt_server)
+ try:
+ bluetooth_gatt, gatt_callback, adv_callback = (
+ orchestrate_gatt_connection(pri_ad, sec_ad))
+ bluetooth_gatt_list.append(bluetooth_gatt)
+ except GattTestUtilsError as err:
+ pri_ad.log.error(err)
+ return False
+ adv_instances.append(adv_callback)
+ connected_devices = sec_ad.droid.gattServerGetConnectedDevices(gatt_server)
+ pri_ad.log.debug("Connected device = {}".format(connected_devices))
+ return True
+
+
+def collect_bluetooth_manager_dumpsys_logs(pri_ad, test_name):
+ """Collect "adb shell dumpsys bluetooth_manager" logs.
+
+ Args:
+ pri_ad: An android device.
+ test_name: Current test case name.
+
+ Returns:
+ Dumpsys file path.
+ """
+ dump_counter = 0
+ dumpsys_path = os.path.join(pri_ad.log_path, test_name, "BluetoothDumpsys")
+ os.makedirs(dumpsys_path, exist_ok=True)
+ while os.path.exists(
+ os.path.join(dumpsys_path,
+ "bluetooth_dumpsys_%s.txt" % dump_counter)):
+ dump_counter += 1
+ out_file = "bluetooth_dumpsys_%s.txt" % dump_counter
+ cmd = "adb -s {} shell dumpsys bluetooth_manager > {}/{}".format(
+ pri_ad.serial, dumpsys_path, out_file)
+ exe_cmd(cmd)
+ file_path = os.path.join(dumpsys_path, out_file)
+ return file_path
+
+
+def configure_and_start_ap(ap, network):
+ """Configure hostapd parameters and starts access point.
+
+ Args:
+ ap: An access point object.
+ network: A dictionary with wifi network details.
+ """
+ hostapd_sec = None
+ if network["security"] == "wpa2":
+ hostapd_sec = hostapd_security.Security(
+ security_mode=network["security"], password=network["password"])
+
+ config = hostapd_config.HostapdConfig(
+ n_capabilities=[hostapd_constants.N_CAPABILITY_HT40_MINUS],
+ mode=hostapd_constants.MODE_11N_PURE,
+ channel=network["channel"],
+ ssid=network["SSID"],
+ security=hostapd_sec)
+ ap.start_ap(config)
+ time.sleep(AP_START_TIME)
+
+
+def connect_dev_to_headset(pri_droid, dev_to_connect, profiles_set):
+ """Connects primary android device to headset.
+
+ Args:
+ pri_droid: Android device initiating connection.
+ dev_to_connect: Third party headset mac address.
+ profiles_set: Profiles to be connected.
+
+ Returns:
+ True if Pass
+ False if Fail
+ """
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_set:
+ if profile not in supported_profiles:
+ pri_droid.log.info("Profile {} is not supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ paired = False
+ for paired_device in pri_droid.droid.bluetoothGetBondedDevices():
+ if paired_device['address'] == dev_to_connect:
+ paired = True
+ break
+
+ if not paired:
+ pri_droid.log.info("{} not paired to {}".format(pri_droid.serial,
+ dev_to_connect))
+ return False
+
+ end_time = time.time() + 10
+ profile_connected = set()
+ sec_addr = dev_to_connect
+ pri_droid.log.info("Profiles to be connected {}".format(profiles_set))
+
+ while (time.time() < end_time and
+ not profile_connected.issuperset(profiles_set)):
+ if (bt_profile_constants['headset_client'] not in profile_connected and
+ bt_profile_constants['headset_client'] in profiles_set):
+ if is_hfp_client_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['headset_client'])
+ if (bt_profile_constants['headset'] not in profile_connected and
+ bt_profile_constants['headset'] in profiles_set):
+ profile_connected.add(bt_profile_constants['headset'])
+ if (bt_profile_constants['a2dp'] not in profile_connected and
+ bt_profile_constants['a2dp'] in profiles_set):
+ if is_a2dp_src_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp'])
+ if (bt_profile_constants['a2dp_sink'] not in profile_connected and
+ bt_profile_constants['a2dp_sink'] in profiles_set):
+ if is_a2dp_snk_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp_sink'])
+ if (bt_profile_constants['map_mce'] not in profile_connected and
+ bt_profile_constants['map_mce'] in profiles_set):
+ if is_map_mce_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['map_mce'])
+ if (bt_profile_constants['map'] not in profile_connected and
+ bt_profile_constants['map'] in profiles_set):
+ if is_map_mse_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['map'])
+ time.sleep(0.1)
+
+ while not profile_connected.issuperset(profiles_set):
+ try:
+ time.sleep(10)
+ profile_event = pri_droid.ed.pop_event(
+ bluetooth_profile_connection_state_changed,
+ bt_default_timeout + 10)
+ pri_droid.log.info("Got event {}".format(profile_event))
+ except Exception:
+ pri_droid.log.error("Did not get {} profiles left {}".format(
+ bluetooth_profile_connection_state_changed, profile_connected))
+ return False
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+ if state == bt_profile_states['connected'] and (
+ device_addr == dev_to_connect):
+ profile_connected.add(profile)
+ pri_droid.log.info(
+ "Profiles connected until now {}".format(profile_connected))
+ return True
+
+
+def device_discoverable(pri_ad, sec_ad):
+ """Verifies whether the device is discoverable or not.
+
+ Args:
+ pri_ad: An primary android device object.
+ sec_ad: An secondary android device object.
+
+ Returns:
+ True if the device found, False otherwise.
+ """
+ pri_ad.droid.bluetoothMakeDiscoverable()
+ scan_mode = pri_ad.droid.bluetoothGetScanMode()
+ if scan_mode == bt_scan_mode_types['connectable_discoverable']:
+ pri_ad.log.info("Primary device scan mode is "
+ "SCAN_MODE_CONNECTABLE_DISCOVERABLE.")
+ else:
+ pri_ad.log.info("Primary device scan mode is not "
+ "SCAN_MODE_CONNECTABLE_DISCOVERABLE.")
+ return False
+ if sec_ad.droid.bluetoothStartDiscovery():
+ time.sleep(DISCOVERY_TIME)
+ droid_name = pri_ad.droid.bluetoothGetLocalName()
+ droid_address = pri_ad.droid.bluetoothGetLocalAddress()
+ get_discovered_devices = sec_ad.droid.bluetoothGetDiscoveredDevices()
+ find_flag = False
+
+ if get_discovered_devices:
+ for device in get_discovered_devices:
+ if 'name' in device and device['name'] == droid_name or (
+ 'address' in device and
+ device["address"] == droid_address):
+ pri_ad.log.info("Primary device is in the discovery "
+ "list of secondary device.")
+ find_flag = True
+ break
+ else:
+ pri_ad.log.info("Secondary device get all the discovered devices "
+ "list is empty")
+ return False
+ else:
+ pri_ad.log.info("Secondary device start discovery process error.")
+ return False
+ if not find_flag:
+ return False
+ return True
+
+
+def device_discoverability(required_devices):
+ """Wrapper function to keep required_devices in discoverable mode.
+
+ Args:
+ required_devices: List of devices to be discovered.
+
+ Returns:
+ discovered_devices: List of BD_ADDR of devices in discoverable mode.
+ """
+ discovered_devices = []
+ if "AndroidDevice" in required_devices:
+ discovered_devices.extend(
+ android_device_discoverability(required_devices["AndroidDevice"]))
+ if "RelayDevice" in required_devices:
+ discovered_devices.extend(
+ relay_device_discoverability(required_devices["RelayDevice"]))
+ return discovered_devices
+
+
+def android_device_discoverability(droid_dev):
+ """To keep android devices in discoverable mode.
+
+ Args:
+ droid_dev: Android device object.
+
+ Returns:
+ device_list: List of device discovered.
+ """
+ device_list = []
+ for device in range(len(droid_dev)):
+ inquiry_device = droid_dev[device]
+ if enable_bluetooth(inquiry_device.droid, inquiry_device.ed):
+ if set_bt_scan_mode(inquiry_device,
+ bt_scan_mode_types['connectable_discoverable']):
+ device_list.append(
+ inquiry_device.droid.bluetoothGetLocalAddress())
+ else:
+ droid_dev.log.error(
+ "Device {} scan mode is not in"
+ "SCAN_MODE_CONNECTABLE_DISCOVERABLE.".format(
+ inquiry_device.droid.bluetoothGetLocalAddress()))
+ return device_list
+
+
+def relay_device_discoverability(relay_devices):
+ """To keep relay controlled devices in discoverable mode.
+
+ Args:
+ relay_devices: Relay object.
+
+ Returns:
+ mac_address: Mac address of relay controlled device.
+ """
+ relay_device = relay_devices[0]
+ relay_device.power_on()
+ relay_device.enter_pairing_mode()
+ return relay_device.mac_address
+
+
+def disconnect_headset_from_dev(pri_ad, sec_ad, profiles_list):
+ """Disconnect primary from secondary on a specific set of profiles
+
+ Args:
+ pri_ad: Primary android_device initiating disconnection
+ sec_ad: Secondary android droid (sl4a interface to keep the
+ method signature the same connect_pri_to_sec above)
+ profiles_list: List of profiles we want to disconnect from
+
+ Returns:
+ True on Success
+ False on Failure
+ """
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_list:
+ if profile not in supported_profiles:
+ pri_ad.log.info("Profile {} is not in supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
+
+ try:
+ pri_ad.droid.bluetoothDisconnectConnectedProfile(sec_ad, profiles_list)
+ except Exception as err:
+ pri_ad.log.error(
+ "Exception while trying to disconnect profile(s) {}: {}".format(
+ profiles_list, err))
+ return False
+
+ profile_disconnected = set()
+ pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
+
+ while not profile_disconnected.issuperset(profiles_list):
+ try:
+ profile_event = pri_ad.ed.pop_event(
+ bluetooth_profile_connection_state_changed, bt_default_timeout)
+ pri_ad.log.info("Got event {}".format(profile_event))
+ except Exception:
+ pri_ad.log.warning("Did not disconnect from Profiles")
+ return True
+
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+
+ if state == bt_profile_states['disconnected'] and (
+ device_addr == sec_ad):
+ profile_disconnected.add(profile)
+ pri_ad.log.info(
+ "Profiles disconnected so far {}".format(profile_disconnected))
+
+ return True
+
+
+def initiate_disconnect_from_hf(audio_receiver, pri_ad, sec_ad, duration):
+ """Initiates call and disconnect call on primary device.
+
+ Steps:
+ 1. Initiate call from HF.
+ 2. Wait for dialing state at DUT and wait for ringing at secondary device.
+ 3. Accepts call from secondary device.
+ 4. Wait for call active state at primary and secondary device.
+ 5. Sleeps until given duration.
+ 6. Disconnect call from primary device.
+ 7. Wait for call is not present state.
+
+ Args:
+ audio_receiver: An relay device object.
+ pri_ad: An android device to disconnect call.
+ sec_ad: An android device accepting call.
+ duration: Duration of call in seconds.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ audio_receiver.press_initiate_call()
+ time.sleep(2)
+ flag = True
+ flag &= wait_for_dialing(logging, pri_ad)
+ flag &= wait_for_ringing(logging, sec_ad)
+ if not flag:
+ pri_ad.log.error("Outgoing call did not get established")
+ return False
+
+ if not wait_and_answer_call(logging, sec_ad):
+ pri_ad.log.error("Failed to answer call in second device.")
+ return False
+ if not wait_for_active(logging, pri_ad):
+ pri_ad.log.error("AG not in Active state.")
+ return False
+ if not wait_for_active(logging, sec_ad):
+ pri_ad.log.error("RE not in Active state.")
+ return False
+ time.sleep(duration)
+ if not hangup_call(logging, pri_ad):
+ pri_ad.log.error("Failed to hangup call.")
+ return False
+ flag = True
+ flag &= wait_for_not_in_call(logging, pri_ad)
+ flag &= wait_for_not_in_call(logging, sec_ad)
+ return flag
+
+
+def initiate_disconnect_call_dut(pri_ad, sec_ad, duration, callee_number):
+ """Initiates call and disconnect call on primary device.
+
+ Steps:
+ 1. Initiate call from DUT.
+ 2. Wait for dialing state at DUT and wait for ringing at secondary device.
+ 3. Accepts call from secondary device.
+ 4. Wait for call active state at primary and secondary device.
+ 5. Sleeps until given duration.
+ 6. Disconnect call from primary device.
+ 7. Wait for call is not present state.
+
+ Args:
+ pri_ad: An android device to disconnect call.
+ sec_ad: An android device accepting call.
+ duration: Duration of call in seconds.
+ callee_number: Secondary device's phone number.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_call(logging, pri_ad, callee_number):
+ pri_ad.log.error("Failed to initiate call")
+ return False
+ time.sleep(2)
+
+ flag = True
+ flag &= wait_for_dialing(logging, pri_ad)
+ flag &= wait_for_ringing(logging, sec_ad)
+ if not flag:
+ pri_ad.log.error("Outgoing call did not get established")
+ return False
+
+ if not wait_and_answer_call(logging, sec_ad):
+ pri_ad.log.error("Failed to answer call in second device.")
+ return False
+ # Wait for AG, RE to go into an Active state.
+ if not wait_for_active(logging, pri_ad):
+ pri_ad.log.error("AG not in Active state.")
+ return False
+ if not wait_for_active(logging, sec_ad):
+ pri_ad.log.error("RE not in Active state.")
+ return False
+ time.sleep(duration)
+ if not hangup_call(logging, pri_ad):
+ pri_ad.log.error("Failed to hangup call.")
+ return False
+ flag = True
+ flag &= wait_for_not_in_call(logging, pri_ad)
+ flag &= wait_for_not_in_call(logging, sec_ad)
+
+ return flag
+
+
+def check_wifi_status(pri_ad, network, ssh_config=None):
+ """Function to check existence of wifi connection.
+
+ Args:
+ pri_ad: An android device.
+ network: network ssid.
+ ssh_config: ssh config for iperf client.
+ """
+ time.sleep(5)
+ proc = job.run("pgrep -f 'iperf3 -c'")
+ pid_list = proc.stdout.split()
+
+ while True:
+ iperf_proc = job.run(["pgrep", "-f", "iperf3"])
+ process_list = iperf_proc.stdout.split()
+ if not wifi_connection_check(pri_ad, network["SSID"]):
+ pri_ad.adb.shell("killall iperf3")
+ if ssh_config:
+ time.sleep(5)
+ ssh_settings = settings.from_config(ssh_config)
+ ssh_session = connection.SshConnection(ssh_settings)
+ result = ssh_session.run("pgrep iperf3")
+ res = result.stdout.split("\n")
+ for pid in res:
+ try:
+ ssh_session.run("kill -9 %s" % pid)
+ except Exception as e:
+ logging.warning("No such process: %s" % e)
+ for pid in pid_list[:-1]:
+ job.run(["kill", " -9", " %s" % pid], ignore_status=True)
+ else:
+ job.run(["killall", " iperf3"], ignore_status=True)
+ break
+ elif pid_list[0] not in process_list:
+ break
+
+
+def iperf_result(log, protocol, result):
+ """Accepts the iperf result in json format and parse the output to
+ get throughput value.
+
+ Args:
+ log: Logger object.
+ protocol : TCP or UDP protocol.
+ result: iperf result's filepath.
+
+ Returns:
+ rx_rate: Data received from client.
+ """
+ if os.path.exists(result):
+ ip_cl = IPerfResult(result)
+
+ if protocol == "udp":
+ rx_rate = (math.fsum(ip_cl.instantaneous_rates) /
+ len(ip_cl.instantaneous_rates))*8
+ else:
+ rx_rate = ip_cl.avg_receive_rate * 8
+ return rx_rate
+ else:
+ log.error("IPerf file not found")
+ return False
+
+
+def is_a2dp_connected(pri_ad, headset_mac_address):
+ """Convenience Function to see if the 2 devices are connected on A2DP.
+
+ Args:
+ pri_ad : An android device.
+ headset_mac_address : Mac address of headset.
+
+ Returns:
+ True:If A2DP connection exists, False otherwise.
+ """
+ devices = pri_ad.droid.bluetoothA2dpGetConnectedDevices()
+ for device in devices:
+ pri_ad.log.debug("A2dp Connected device {}".format(device["name"]))
+ if device["address"] == headset_mac_address:
+ return True
+ return False
+
+
+def media_stream_check(pri_ad, duration, headset_mac_address):
+ """Checks whether A2DP connecion is active or not for given duration of
+ time.
+
+ Args:
+ pri_ad : An android device.
+ duration : No of seconds to check if a2dp streaming is alive.
+ headset_mac_address : Headset mac address.
+
+ Returns:
+ True: If A2dp connection is active for entire duration.
+ False: If A2dp connection is not active.
+ """
+ while time.time() < duration:
+ if not is_a2dp_connected(pri_ad, headset_mac_address):
+ pri_ad.log.error('A2dp connection not active at %s', pri_ad.serial)
+ return False
+ time.sleep(1)
+ return True
+
+
+def multithread_func(log, tasks):
+ """Multi-thread function wrapper.
+
+ Args:
+ log: log object.
+ tasks: tasks to be executed in parallel.
+
+ Returns:
+ List of results of tasks
+ """
+ results = run_multithread_func(log, tasks)
+ for res in results:
+ if not res:
+ return False
+ return True
+
+
+def music_play_and_check(pri_ad, headset_mac_address, music_to_play, duration):
+ """Starts playing media and checks if media plays for n seconds.
+
+ Steps:
+ 1. Starts media player on android device.
+ 2. Checks if music streaming is ongoing for n seconds.
+ 3. Stops media player.
+ 4. Collect dumpsys logs.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+ music_to_play: Indicates the music file to play.
+ duration: Time in secs to indicate music time to play.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ pri_ad.droid.setMediaVolume(pri_ad.droid.getMaxMediaVolume() - 1)
+ pri_ad.log.info("current volume = {}".format(pri_ad.droid.getMediaVolume()))
+ pri_ad.log.debug("In music play and check")
+ if not start_media_play(pri_ad, music_to_play):
+ pri_ad.log.error("Start media play failed.")
+ return False
+ stream_time = time.time() + duration
+ if not media_stream_check(pri_ad, stream_time, headset_mac_address):
+ pri_ad.log.error("A2DP Connection check failed.")
+ pri_ad.droid.mediaPlayStop()
+ return False
+ pri_ad.droid.mediaPlayStop()
+ return True
+
+
+def music_play_and_check_via_app(pri_ad, headset_mac_address, duration=5):
+ """Starts google music player and check for A2DP connection.
+
+ Steps:
+ 1. Starts Google music player on android device.
+ 2. Checks for A2DP connection.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+ duration: Total time of music streaming.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ pri_ad.adb.shell("am start com.google.android.music")
+ time.sleep(3)
+ pri_ad.adb.shell("input keyevent 85")
+ stream_time = time.time() + duration
+ try:
+ if not media_stream_check(pri_ad, stream_time, headset_mac_address):
+ pri_ad.log.error("A2dp connection not active at %s", pri_ad.serial)
+ return False
+ finally:
+ pri_ad.adb.shell("am force-stop com.google.android.music")
+ return True
+
+
+def pair_dev_to_headset(pri_ad, dev_to_pair):
+ """Pairs primary android device with headset.
+
+ Args:
+ pri_ad: Android device initiating connection
+ dev_to_pair: Third party headset mac address.
+
+ Returns:
+ True if Pass
+ False if Fail
+ """
+ bonded_devices = pri_ad.droid.bluetoothGetBondedDevices()
+ for d in bonded_devices:
+ if d['address'] == dev_to_pair:
+ pri_ad.log.info("Successfully bonded to device {}".format(
+ dev_to_pair))
+ return True
+ pri_ad.droid.bluetoothStartDiscovery()
+ time.sleep(10) # Wait until device gets discovered
+ pri_ad.droid.bluetoothCancelDiscovery()
+ pri_ad.log.debug("Discovered bluetooth devices: {}".format(
+ pri_ad.droid.bluetoothGetDiscoveredDevices()))
+ for device in pri_ad.droid.bluetoothGetDiscoveredDevices():
+ if device['address'] == dev_to_pair:
+
+ result = pri_ad.droid.bluetoothDiscoverAndBond(dev_to_pair)
+ pri_ad.log.info(result)
+ end_time = time.time() + bt_default_timeout
+ pri_ad.log.info("Verifying if device bonded with {}".format(
+ dev_to_pair))
+ time.sleep(5) # Wait time until device gets paired.
+ while time.time() < end_time:
+ bonded_devices = pri_ad.droid.bluetoothGetBondedDevices()
+ for d in bonded_devices:
+ if d['address'] == dev_to_pair:
+ pri_ad.log.info(
+ "Successfully bonded to device {}".format(
+ dev_to_pair))
+ return True
+ pri_ad.log.error("Failed to bond with {}".format(dev_to_pair))
+ return False
+
+
+def pair_and_connect_headset(pri_ad, headset_mac_address, profile_to_connect, retry=5):
+ """Pair and connect android device with third party headset.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+ profile_to_connect: Profile to be connected with headset.
+ retry: Number of times pair and connection should happen.
+
+ Returns:
+ True if pair and connect to headset successful, or raises exception
+ on failure.
+ """
+
+ paired = False
+ for i in range(1, retry):
+ if pair_dev_to_headset(pri_ad, headset_mac_address):
+ paired = True
+ break
+ else:
+ pri_ad.log.error("Attempt {} out of {}, Failed to pair, "
+ "Retrying.".format(i, retry))
+
+ if paired:
+ for i in range(1, retry):
+ if connect_dev_to_headset(pri_ad, headset_mac_address,
+ profile_to_connect):
+ return True
+ else:
+ pri_ad.log.error("Attempt {} out of {}, Failed to connect, "
+ "Retrying.".format(i, retry))
+ else:
+ asserts.fail("Failed to pair and connect with {}".format(
+ headset_mac_address))
+
+
+def perform_classic_discovery(pri_ad, duration, file_name, dev_list=None):
+ """Convenience function to start and stop device discovery.
+
+ Args:
+ pri_ad: An android device.
+ duration: iperf duration of the test.
+ file_name: Json file to which result is dumped
+ dev_list: List of devices to be discoverable mode.
+
+ Returns:
+ True start and stop discovery is successful, False otherwise.
+ """
+ if dev_list:
+ devices_required = device_discoverability(dev_list)
+ else:
+ devices_required = None
+ iteration = 0
+ result = {}
+ result["discovered_devices"] = {}
+ discover_result = []
+ start_time = time.time()
+ while time.time() < start_time + duration:
+ if not pri_ad.droid.bluetoothStartDiscovery():
+ pri_ad.log.error("Failed to start discovery")
+ return False
+ time.sleep(DISCOVERY_TIME)
+ if not pri_ad.droid.bluetoothCancelDiscovery():
+ pri_ad.log.error("Failed to cancel discovery")
+ return False
+ pri_ad.log.info("Discovered device list {}".format(
+ pri_ad.droid.bluetoothGetDiscoveredDevices()))
+ if devices_required is not None:
+ result["discovered_devices"][iteration] = []
+ devices_name = {
+ element.get('name', element['address'])
+ for element in pri_ad.droid.bluetoothGetDiscoveredDevices()
+ if element["address"] in devices_required
+ }
+ result["discovered_devices"][iteration] = list(devices_name)
+ discover_result.extend([len(devices_name) == len(devices_required)])
+ iteration += 1
+ with open(file_name, 'a') as results_file:
+ json.dump(result, results_file, indent=4)
+ if False in discover_result:
+ return False
+ else:
+ pri_ad.log.warning("No devices are kept in discoverable mode")
+ return True
+
+
+def connect_wlan_profile(pri_ad, network):
+ """Disconnect and Connect to AP.
+
+ Args:
+ pri_ad: An android device.
+ network: Network to which AP to be connected.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ reset_wifi(pri_ad)
+ wifi_toggle_state(pri_ad, False)
+ wifi_test_device_init(pri_ad)
+ wifi_connect(pri_ad, network)
+ if not wifi_connection_check(pri_ad, network["SSID"]):
+ pri_ad.log.error("Wifi connection does not exist.")
+ return False
+ return True
+
+
+def toggle_bluetooth(pri_ad, duration):
+ """Toggles bluetooth on/off for N iterations.
+
+ Args:
+ pri_ad: An android device object.
+ duration: Iperf duration of the test.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ start_time = time.time()
+ while time.time() < start_time + duration:
+ if not enable_bluetooth(pri_ad.droid, pri_ad.ed):
+ pri_ad.log.error("Failed to enable bluetooth")
+ return False
+ time.sleep(BLUETOOTH_WAIT_TIME)
+ if not disable_bluetooth(pri_ad.droid):
+ pri_ad.log.error("Failed to turn off bluetooth")
+ return False
+ time.sleep(BLUETOOTH_WAIT_TIME)
+ return True
+
+
+def toggle_screen_state(pri_ad, duration):
+ """Toggles the screen state to on or off..
+
+ Args:
+ pri_ad: Android device.
+ duration: Iperf duration of the test.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ start_time = time.time()
+ while time.time() < start_time + duration:
+ if not pri_ad.ensure_screen_on():
+ pri_ad.log.error("User window cannot come up")
+ return False
+ if not pri_ad.go_to_sleep():
+ pri_ad.log.info("Screen off")
+ return True
+
+
+def setup_tel_config(pri_ad, sec_ad, sim_conf_file):
+ """Sets tel properties for primary device and secondary devices
+
+ Args:
+ pri_ad: An android device object.
+ sec_ad: An android device object.
+ sim_conf_file: Sim card map.
+
+ Returns:
+ pri_ad_num: Phone number of primary device.
+ sec_ad_num: Phone number of secondary device.
+ """
+ setup_droid_properties(logging, pri_ad, sim_conf_file)
+ pri_ad_num = get_phone_number(logging, pri_ad)
+ setup_droid_properties(logging, sec_ad, sim_conf_file)
+ sec_ad_num = get_phone_number(logging, sec_ad)
+ return pri_ad_num, sec_ad_num
+
+
+def start_fping(pri_ad, duration, fping_params):
+ """Starts fping to ping for DUT's ip address.
+
+ Steps:
+ 1. Run fping command to check DUT's IP is alive or not.
+
+ Args:
+ pri_ad: An android device object.
+ duration: Duration of fping in seconds.
+ fping_params: List of parameters for fping to run.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ counter = 0
+ fping_path = ''.join((pri_ad.log_path, "/Fping"))
+ os.makedirs(fping_path, exist_ok=True)
+ while os.path.isfile(fping_path + "/fping_%s.txt" % counter):
+ counter += 1
+ out_file_name = "{}".format("fping_%s.txt" % counter)
+
+ full_out_path = os.path.join(fping_path, out_file_name)
+ cmd = "fping {} -D -c {}".format(get_phone_ip(pri_ad), duration)
+ if fping_params["ssh_config"]:
+ ssh_settings = settings.from_config(fping_params["ssh_config"])
+ ssh_session = connection.SshConnection(ssh_settings)
+ try:
+ with open(full_out_path, 'w') as outfile:
+ job_result = ssh_session.run(cmd)
+ outfile.write(job_result.stdout)
+ outfile.write("\n")
+ outfile.writelines(job_result.stderr)
+ except Exception as err:
+ pri_ad.log.error("Fping run has been failed. = {}".format(err))
+ return False
+ else:
+ cmd = cmd.split()
+ with open(full_out_path, "w") as f:
+ job.run(cmd)
+ result = parse_fping_results(fping_params["fping_drop_tolerance"],
+ full_out_path)
+ return bool(result)
+
+
+def parse_fping_results(failure_rate, full_out_path):
+ """Calculates fping results.
+
+ Steps:
+ 1. Read the file and calculate the results.
+
+ Args:
+ failure_rate: Fping packet drop tolerance value.
+ full_out_path: path where the fping results has been stored.
+
+ Returns:
+ loss_percent: loss percentage of fping packet.
+ """
+ try:
+ result_file = open(full_out_path, "r")
+ lines = result_file.readlines()
+ res_line = lines[-1]
+ # Ex: res_line = "192.168.186.224 : xmt/rcv/%loss = 10/10/0%,
+ # min/avg/max = 36.7/251/1272"
+ loss_percent = re.search("[0-9]+%", res_line)
+ if int(loss_percent.group().strip("%")) > failure_rate:
+ logging.error("Packet drop observed")
+ return False
+ return loss_percent.group()
+ except Exception as e:
+ logging.error("Error in parsing fping results : %s" %(e))
+ return False
+
+
+def start_media_play(pri_ad, music_file_to_play):
+ """Starts media player on device.
+
+ Args:
+ pri_ad : An android device.
+ music_file_to_play : An audio file to play.
+
+ Returns:
+ True:If media player start music, False otherwise.
+ """
+ if not pri_ad.droid.mediaPlayOpen(
+ "file:///sdcard/Music/{}".format(music_file_to_play)):
+ pri_ad.log.error("Failed to play music")
+ return False
+
+ pri_ad.droid.mediaPlaySetLooping(True)
+ pri_ad.log.info("Music is now playing on device {}".format(pri_ad.serial))
+ return True
+
+
+def wifi_connection_check(pri_ad, ssid):
+ """Function to check existence of wifi connection.
+
+ Args:
+ pri_ad : An android device.
+ ssid : wifi ssid to check.
+
+ Returns:
+ True if wifi connection exists, False otherwise.
+ """
+ wifi_info = pri_ad.droid.wifiGetConnectionInfo()
+ if (wifi_info["SSID"] == ssid and
+ wifi_info["supplicant_state"] == "completed"):
+ return True
+ pri_ad.log.error("Wifi Connection check failed : {}".format(wifi_info))
+ return False
+
+
+def push_music_to_android_device(ad, audio_params):
+ """Add music to Android device as specified by the test config
+
+ Args:
+ ad: Android device
+ audio_params: Music file to push.
+
+ Returns:
+ True on success, False on failure
+ """
+ ad.log.info("Pushing music to the Android device")
+ android_music_path = "/sdcard/Music/"
+ music_path = audio_params["music_file"]
+ if type(music_path) is list:
+ ad.log.info("Media ready to push as is.")
+ for item in music_path:
+ music_file_to_play = item
+ ad.adb.push(item, android_music_path)
+ return music_file_to_play
+ else:
+ music_file_to_play = audio_params["music_file"]
+ ad.adb.push("{} {}".format(music_file_to_play, android_music_path))
+ return (os.path.basename(music_file_to_play))
+
+def bokeh_plot(data_sets,
+ legends,
+ fig_property,
+ shaded_region=None,
+ output_file_path=None):
+ """Plot bokeh figs.
+ Args:
+ data_sets: data sets including lists of x_data and lists of y_data
+ ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]]
+ legends: list of legend for each curve
+ fig_property: dict containing the plot property, including title,
+ labels, linewidth, circle size, etc.
+ shaded_region: optional dict containing data for plot shading
+ output_file_path: optional path at which to save figure
+ Returns:
+ plot: bokeh plot figure object
+ """
+ tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save'
+ plot = figure(plot_width=1300,
+ plot_height=700,
+ title=fig_property['title'],
+ tools=tools,
+ output_backend="webgl")
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width"))
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height"))
+ colors = [
+ 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy',
+ 'yellow', 'darkred', 'goldenrod'
+ ]
+ if shaded_region:
+ band_x = shaded_region["x_vector"]
+ band_x.extend(shaded_region["x_vector"][::-1])
+ band_y = shaded_region["lower_limit"]
+ band_y.extend(shaded_region["upper_limit"][::-1])
+ plot.patch(band_x,
+ band_y,
+ color='#7570B3',
+ line_alpha=0.1,
+ fill_alpha=0.1)
+
+ for x_data, y_data, legend in zip(data_sets[0], data_sets[1], legends):
+ index_now = legends.index(legend)
+ color = colors[index_now % len(colors)]
+ plot.line(x_data,
+ y_data,
+ legend=str(legend),
+ line_width=fig_property['linewidth'],
+ color=color)
+ plot.circle(x_data,
+ y_data,
+ size=fig_property['markersize'],
+ legend=str(legend),
+ fill_color=color)
+
+ # Plot properties
+ plot.xaxis.axis_label = fig_property['x_label']
+ plot.yaxis.axis_label = fig_property['y_label']
+ plot.legend.location = "top_right"
+ plot.legend.click_policy = "hide"
+ plot.title.text_font_size = {'value': '15pt'}
+ if output_file_path is not None:
+ output_file(output_file_path)
+ save(plot)
+ return plot
+
+def bokeh_chart_plot(bt_attenuation_range,
+ data_sets,
+ legends,
+ fig_property,
+ shaded_region=None,
+ output_file_path=None):
+ """Plot bokeh figs.
+
+ Args:
+ bt_attenuation_range: range of BT attenuation.
+ data_sets: data sets including lists of x_data and lists of y_data
+ ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]]
+ legends: list of legend for each curve
+ fig_property: dict containing the plot property, including title,
+ labels, linewidth, circle size, etc.
+ shaded_region: optional dict containing data for plot shading
+ output_file_path: optional path at which to save figure
+
+ Returns:
+ plot: bokeh plot figure object
+ """
+ TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save')
+ colors = [
+ 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy',
+ 'yellow', 'darkred', 'goldenrod'
+ ]
+ plot = []
+ data = [[], []]
+ legend = []
+ for i in bt_attenuation_range:
+ if "Packet drop" in legends[i][0]:
+ plot_info = {0: "A2dp_packet_drop_plot", 1: "throughput_plot"}
+ else:
+ plot_info = {0: "throughput_plot"}
+ for j in plot_info:
+ if "Packet drops" in legends[i][j]:
+ if data_sets[i]["a2dp_packet_drops"]:
+ plot_i_j = figure(
+ plot_width=1000,
+ plot_height=500,
+ title=fig_property['title'],
+ tools=TOOLS)
+
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="width"))
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="height"))
+ plot_i_j.xaxis.axis_label = fig_property['x_label']
+ plot_i_j.yaxis.axis_label = fig_property['y_label'][j]
+ plot_i_j.legend.location = "top_right"
+ plot_i_j.legend.click_policy = "hide"
+ plot_i_j.title.text_font_size = {'value': '15pt'}
+
+ plot_i_j.line(
+ data_sets[i]["a2dp_attenuation"],
+ data_sets[i]["a2dp_packet_drops"],
+ legend=legends[i][j],
+ line_width=3,
+ color=colors[j])
+ plot_i_j.circle(
+ data_sets[i]["a2dp_attenuation"],
+ data_sets[i]["a2dp_packet_drops"],
+ legend=str(legends[i][j]),
+ fill_color=colors[j])
+ plot.append(plot_i_j)
+ elif "Performance Results" in legends[i][j]:
+ plot_i_j = figure(
+ plot_width=1000,
+ plot_height=500,
+ title=fig_property['title'],
+ tools=TOOLS)
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="width"))
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="height"))
+ plot_i_j.xaxis.axis_label = fig_property['x_label']
+ plot_i_j.yaxis.axis_label = fig_property['y_label'][j]
+ plot_i_j.legend.location = "top_right"
+ plot_i_j.legend.click_policy = "hide"
+ plot_i_j.title.text_font_size = {'value': '15pt'}
+ data[0].insert(0, data_sets[i]["attenuation"])
+ data[1].insert(0, data_sets[i]["throughput_received"])
+ legend.insert(0, legends[i][j + 1])
+ plot_i_j.line(
+ data_sets[i]["user_attenuation"],
+ data_sets[i]["user_throughput"],
+ legend=legends[i][j],
+ line_width=3,
+ color=colors[j])
+ plot_i_j.circle(
+ data_sets[i]["user_attenuation"],
+ data_sets[i]["user_throughput"],
+ legend=str(legends[i][j]),
+ fill_color=colors[j])
+ plot_i_j.line(
+ data_sets[i]["attenuation"],
+ data_sets[i]["throughput_received"],
+ legend=legends[i][j + 1],
+ line_width=3,
+ color=colors[j])
+ plot_i_j.circle(
+ data_sets[i]["attenuation"],
+ data_sets[i]["throughput_received"],
+ legend=str(legends[i][j + 1]),
+ fill_color=colors[j])
+ if shaded_region:
+ band_x = shaded_region[i]["x_vector"]
+ band_x.extend(shaded_region[i]["x_vector"][::-1])
+ band_y = shaded_region[i]["lower_limit"]
+ band_y.extend(shaded_region[i]["upper_limit"][::-1])
+ plot_i_j.patch(
+ band_x,
+ band_y,
+ color='#7570B3',
+ line_alpha=0.1,
+ fill_alpha=0.1)
+ plot.append(plot_i_j)
+ else:
+ plot_i_j = figure(
+ plot_width=1000,
+ plot_height=500,
+ title=fig_property['title'],
+ tools=TOOLS)
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="width"))
+ plot_i_j.add_tools(
+ bokeh_tools.WheelZoomTool(dimensions="height"))
+ plot_i_j.xaxis.axis_label = fig_property['x_label']
+ plot_i_j.yaxis.axis_label = fig_property['y_label'][j]
+ plot_i_j.legend.location = "top_right"
+ plot_i_j.legend.click_policy = "hide"
+ plot_i_j.title.text_font_size = {'value': '15pt'}
+ data[0].insert(0, data_sets[i]["attenuation"])
+ data[1].insert(0, data_sets[i]["throughput_received"])
+ legend.insert(0, legends[i][j])
+ plot_i_j.line(
+ data_sets[i]["attenuation"],
+ data_sets[i]["throughput_received"],
+ legend=legends[i][j],
+ line_width=3,
+ color=colors[j])
+ plot_i_j.circle(
+ data_sets[i]["attenuation"],
+ data_sets[i]["throughput_received"],
+ legend=str(legends[i][j]),
+ fill_color=colors[j])
+ plot.append(plot_i_j)
+ fig_property['y_label'] = "Throughput (Mbps)"
+ all_plot = bokeh_plot(data, legend, fig_property, shaded_region=None,
+ output_file_path=None)
+ plot.insert(0, all_plot)
+ if output_file_path is not None:
+ output_file(output_file_path)
+ save(column(plot))
+ return plot
+
+
+class A2dpDumpsysParser():
+
+ def __init__(self):
+ self.count_list = []
+ self.frame_list = []
+ self.dropped_count = None
+
+ def parse(self, file_path):
+ """Convenience function to parse a2dp dumpsys logs.
+
+ Args:
+ file_path: Path of dumpsys logs.
+
+ Returns:
+ dropped_list containing packet drop count for every iteration.
+ drop containing list of all packets dropped for test suite.
+ """
+ a2dp_dumpsys_info = []
+ with open(file_path) as dumpsys_file:
+ for line in dumpsys_file:
+ if "A2DP State:" in line:
+ a2dp_dumpsys_info.append(line)
+ elif "Counts (max dropped)" not in line and len(
+ a2dp_dumpsys_info) > 0:
+ a2dp_dumpsys_info.append(line)
+ elif "Counts (max dropped)" in line:
+ a2dp_dumpsys_info = ''.join(a2dp_dumpsys_info)
+ a2dp_info = a2dp_dumpsys_info.split("\n")
+ # Ex: Frames per packet (total/max/ave) : 5034 / 1 / 0
+ frames = int(re.split("[':/()]", str(a2dp_info[-3]))[-3])
+ self.frame_list.append(frames)
+ # Ex : Counts (flushed/dropped/dropouts) : 0 / 4 / 0
+ count = int(re.split("[':/()]", str(a2dp_info[-2]))[-2])
+ if count > 0:
+ for i in range(len(self.count_list)):
+ count = count - self.count_list[i]
+ self.count_list.append(count)
+ if len(self.frame_list) > 1:
+ last_frame = self.frame_list[-1] - self.frame_list[
+ -2]
+ self.dropped_count = (count / last_frame) * 100
+ else:
+ self.dropped_count = (
+ count / self.frame_list[-1]) * 100
+ else:
+ self.dropped_count = count
+ logging.info(a2dp_dumpsys_info)
+ return self.dropped_count
diff --git a/acts_tests/acts_contrib/test_utils/coex/hotspot_utils.py b/acts_tests/acts_contrib/test_utils/coex/hotspot_utils.py
new file mode 100644
index 0000000..a6109bd
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/coex/hotspot_utils.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# WiFi Frequency and channel map.
+wifi_channel_map = {
+ 2412: 1,
+ 2417: 2,
+ 2422: 3,
+ 2427: 4,
+ 2432: 5,
+ 2437: 6,
+ 2442: 7,
+ 2447: 8,
+ 2452: 9,
+ 2457: 10,
+ 2462: 11,
+ 2467: 12,
+ 2472: 13,
+ 2484: 14,
+ 5170: 34,
+ 5180: 36,
+ 5190: 38,
+ 5200: 40,
+ 5210: 42,
+ 5220: 44,
+ 5230: 46,
+ 5240: 48,
+ 5260: 52,
+ 5280: 56,
+ 5300: 60,
+ 5320: 64,
+ 5500: 100,
+ 5520: 104,
+ 5540: 108,
+ 5560: 112,
+ 5580: 116,
+ 5600: 120,
+ 5620: 124,
+ 5640: 128,
+ 5660: 132,
+ 5680: 136,
+ 5700: 140,
+ 5720: 144,
+ 5745: 149,
+ 5755: 151,
+ 5765: 153,
+ 5775: 155,
+ 5795: 159,
+ 5785: 157,
+ 5805: 161,
+ 5825: 165
+}
+
+# Supported lte band.
+# TODO:(@sairamganesh) Make a common function to support different SKU's.
+
+supported_lte_bands = ['OB1', 'OB2', 'OB3', 'OB4', 'OB5', 'OB7', 'OB8',
+ 'OB12', 'OB13', 'OB14', 'OB17', 'OB18', 'OB19',
+ 'OB20', 'OB25', 'OB26', 'OB28', 'OB30', 'OB38',
+ 'OB39', 'OB40', 'OB41', 'OB46', 'OB48', 'OB66',
+ 'OB71'
+ ]
+
+# list of TDD Bands supported.
+tdd_band_list = ['OB33', 'OB34', 'OB35', 'OB36', 'OB37', 'OB38', 'OB39', 'OB40',
+ 'OB41', 'OB42', 'OB43', 'OB44']
+
+# lte band channel map.
+# For every band three channels are chosen(Low, Mid and High)
+band_channel_map = {
+ 'OB1': [25, 300, 575],
+ 'OB2': [625, 900, 1175],
+ 'OB3': [1225, 1575, 1925],
+ 'OB4': [1975, 2175, 2375],
+ 'OB5': [2425, 2525, 2625],
+ 'OB7': [3100],
+ 'OB8': [3475, 3625, 3775],
+ 'OB12': [5035, 5095, 5155],
+ 'OB13': [5205, 5230, 5255],
+ 'OB14': [5310, 5330, 5355],
+ 'OB17': [5755, 5790, 5825],
+ 'OB18': [5875, 5925, 5975],
+ 'OB19': [6025, 6075, 6125],
+ 'OB20': [6180, 6300, 6425],
+ 'OB25': [8065, 8365, 8665],
+ 'OB26': [8715, 8865, 9010],
+ 'OB28': [9235, 9435, 9635],
+ 'OB30': [9795, 9820, 9840],
+ 'OB38': [37750, 38000, 38245],
+ 'OB39': [38250, 38450, 38645],
+ 'OB40': [38650, 39150, 39645],
+ 'OB41': [39650, 40620, 41585],
+ 'OB46': [46790, 50665, 54535],
+ 'OB48': [55240, 55990, 56735],
+ 'OB66': [66461, 66886, 67331],
+ 'OB71': [68611, 68761, 68906]
+}
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/__init__.py b/acts_tests/acts_contrib/test_utils/fuchsia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py b/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py
new file mode 100644
index 0000000..c5a9250
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import time
+
+
+def le_scan_for_device_by_name(fd,
+ log,
+ search_name,
+ timeout,
+ partial_match=False):
+ """Scan for and returns the first BLE advertisement with the device name.
+
+ Args:
+ fd: The Fuchsia device to start LE scanning on.
+ name: The name to find.
+ timeout: How long to scan for.
+ partial_match: Only do a partial match for the LE advertising name.
+ This will return the first result that had a partial match.
+
+ Returns:
+ The dictionary of device information.
+ """
+ scan_filter = {"name_substring": search_name}
+ fd.gattc_lib.bleStartBleScan(scan_filter)
+ end_time = time.time() + timeout
+ found_device = None
+ while time.time() < end_time and not found_device:
+ time.sleep(1)
+ scan_res = fd.gattc_lib.bleGetDiscoveredDevices()['result']
+ for device in scan_res:
+ name, did, connectable = device["name"], device["id"], device[
+ "connectable"]
+ if name == search_name or (partial_match and search_name in name):
+ log.info("Successfully found advertisement! name, id: {}, {}".
+ format(name, did))
+ found_device = device
+ fd.gattc_lib.bleStopBleScan()
+ if not found_device:
+ log.error("Failed to find device with name {}.".format(search_name))
+ return found_device
+ return found_device
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/sdp_records.py b/acts_tests/acts_contrib/test_utils/fuchsia/sdp_records.py
new file mode 100644
index 0000000..1da2e62
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/sdp_records.py
@@ -0,0 +1,491 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from acts_contrib.test_utils.bt.bt_constants import bt_attribute_values
+from acts_contrib.test_utils.bt.bt_constants import sig_uuid_constants
+
+BASE_UUID = sig_uuid_constants['BASE_UUID']
+
+# A list of pre-defined SDP definitions
+sdp_pts_record_list = [
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['AudioSink'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['AVDTP'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVDTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['AdvancedAudioDistribution'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors': [{
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [
+ {
+ 'data': int(sig_uuid_constants['AVDTP'], 16),
+ },
+ {
+ 'data': int(sig_uuid_constants['AVCTP'], 16),
+ },
+ {
+ 'data': int(sig_uuid_constants['GenericAudio'], 16),
+ },
+ ]
+ }],
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_SERVICE_AVAILABILITY'],
+ 'element': {
+ 'data': 0xff # Indicate all available
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [
+ BASE_UUID.format(sig_uuid_constants['A/V_RemoteControlTarget']),
+ BASE_UUID.format(sig_uuid_constants['A/V_RemoteControl']),
+ BASE_UUID.format(sig_uuid_constants['A/V_RemoteControlController'])
+ ],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['AVCTP'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['PANU'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['NAP'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['SerialPort'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data':
+ int(sig_uuid_constants['SerialPort'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['DialupNetworking'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data':
+ int(sig_uuid_constants['DialupNetworking'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['OBEXObjectPush'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data':
+ int(sig_uuid_constants['OBEXObjectPush'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['OBEXFileTransfer'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data':
+ int(sig_uuid_constants['OBEXFileTransfer'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['Headset'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['Headset'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['HandsfreeAudioGateway'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol':
+ int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data':
+ int(sig_uuid_constants['HandsfreeAudioGateway'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['Handsfree'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['Handsfree'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ },
+ {
+ 'service_class_uuids': [BASE_UUID.format(sig_uuid_constants['SIM_Access'])],
+ 'protocol_descriptors': [
+ {
+ 'protocol': int(sig_uuid_constants['L2CAP'], 16),
+ 'params': [{
+ 'data': int(sig_uuid_constants['SIM_Access'], 16),
+ }]
+ },
+ {
+ 'protocol': int(sig_uuid_constants['AVCTP'], 16),
+ 'params': [{
+ 'data': 0x103 # to indicate 1.3
+ }]
+ },
+ ],
+ 'profile_descriptors': [{
+ 'profile_id':
+ int(sig_uuid_constants['A/V_RemoteControl'], 16),
+ 'major_version':
+ 1,
+ 'minor_version':
+ 2,
+ }],
+ 'additional_protocol_descriptors':
+ None,
+ 'information': [{
+ 'language': "en",
+ 'name': "A2DP",
+ 'description': "Advanced Audio Distribution Profile",
+ 'provider': "Fuchsia"
+ }],
+ 'additional_attributes': [{
+ 'id':
+ bt_attribute_values['ATTR_A2DP_SUPPORTED_FEATURES'],
+ 'element': {
+ 'data': 0x0011
+ }
+ }]
+ }
+]
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/utils.py b/acts_tests/acts_contrib/test_utils/fuchsia/utils.py
new file mode 100644
index 0000000..8014220
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/utils.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from acts.libs.proc.job import Error
+
+
+def http_file_download_by_curl(fd,
+ url,
+ out_path='/tmp/',
+ curl_loc='/bin/curl',
+ remove_file_after_check=True,
+ timeout=3600,
+ limit_rate=None,
+ additional_args=None,
+ retry=3):
+ """Download http file by ssh curl.
+
+ Args:
+ fd: Fuchsia Device Object.
+ url: The url that file to be downloaded from.
+ out_path: Optional. Where to download file to.
+ out_path is /tmp by default.
+ curl_loc: Location of curl binary on fd.
+ remove_file_after_check: Whether to remove the downloaded file after
+ check.
+ timeout: timeout for file download to complete.
+ limit_rate: download rate in bps. None, if do not apply rate limit.
+ additional_args: Any additional args for curl.
+ retry: the retry request times provided in curl command.
+ """
+ file_directory, file_name = _generate_file_directory_and_file_name(
+ url, out_path)
+ file_path = os.path.join(file_directory, file_name)
+ curl_cmd = curl_loc
+ if limit_rate:
+ curl_cmd += ' --limit-rate %s' % limit_rate
+ if retry:
+ curl_cmd += ' --retry %s' % retry
+ if additional_args:
+ curl_cmd += ' %s' % additional_args
+ curl_cmd += ' --url %s > %s' % (url, file_path)
+ try:
+ fd.log.info(
+ 'Download %s to %s by ssh command %s' % (url, file_path, curl_cmd))
+
+ status = fd.send_command_ssh(curl_cmd, timeout=timeout)
+ if isinstance(status, Error):
+ status = status.result
+ if not status.stderr:
+ if int(status.exit_status) != 0:
+ fd.log.warning('Curl command: "%s" failed with error %s' %
+ (curl_cmd, status.exit_status))
+ return False
+
+ if _check_file_existence(fd, file_path):
+ fd.log.info(
+ '%s is downloaded to %s successfully' % (url, file_path))
+ return True
+ else:
+ fd.log.warning('Fail to download %s' % url)
+ return False
+ except Exception as e:
+ fd.log.warning('Download %s failed with exception %s' % (url, e))
+ return False
+ finally:
+ if remove_file_after_check:
+ fd.log.info('Remove the downloaded file %s' % file_path)
+ fd.send_command_ssh('rm %s' % file_path)
+
+
+def _generate_file_directory_and_file_name(url, out_path):
+ """Splits the file from the url and specifies the appropriate location of
+ where to store the downloaded file.
+
+ Args:
+ url: A url to the file that is going to be downloaded.
+ out_path: The location of where to store the file that is downloaded.
+
+ Returns:
+ file_directory: The directory of where to store the downloaded file.
+ file_name: The name of the file that is being downloaded.
+ """
+ file_name = url.split('/')[-1]
+ if not out_path:
+ file_directory = '/tmp/'
+ elif not out_path.endswith('/'):
+ file_directory, file_name = os.path.split(out_path)
+ else:
+ file_directory = out_path
+ return file_directory, file_name
+
+
+def _check_file_existence(fd, file_path):
+ """Check file existence by file_path. If expected_file_size
+ is provided, then also check if the file meet the file size requirement.
+
+ Args:
+ fd: A fuchsia device
+ file_path: Where to store the file on the fuchsia device.
+ """
+ out = fd.send_command_ssh('ls -al "%s"' % file_path)
+ if isinstance(out, Error):
+ out = out.result
+ if 'No such file or directory' in out.stdout:
+ fd.log.debug('File %s does not exist.' % file_path)
+ return False
+ else:
+ fd.log.debug('File %s exists.' % file_path)
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/gnss/__init__.py b/acts_tests/acts_contrib/test_utils/gnss/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/gnss/dut_log_test_utils.py b/acts_tests/acts_contrib/test_utils/gnss/dut_log_test_utils.py
new file mode 100644
index 0000000..6dfa77d
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/dut_log_test_utils.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import time
+import errno
+
+DEVICE_CFG_FOLDER = "/data/vendor/radio/diag_logs/cfg/"
+DEVICE_DIAGMDLOG_FOLDER = "/data/vendor/radio/diag_logs/logs/"
+MDLOG_SETTLING_TIME = 2
+MDLOG_PROCESS_KILL_TIME = 3
+NOHUP_CMD = "nohup diag_mdlog -f {} -o {} -s 100 -c &> /dev/null &"
+
+
+def find_device_qxdm_log_mask(ad, maskfile):
+ """Finds device's diagmd mask file
+
+ Args:
+ ad: the target android device, AndroidDevice object
+ maskfile: Device's mask file name
+
+ Return:
+ exists, if cfg file is present
+
+ Raises:
+ FileNotFoundError if maskfile is not present
+ """
+
+ if ".cfg" not in maskfile:
+ # errno.ENOENT - No such file or directory
+ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
+ maskfile)
+ else:
+ cfg_path = os.path.join(DEVICE_CFG_FOLDER, maskfile)
+ device_mask_file = ad.adb.shell('test -e %s && echo exists' % cfg_path)
+ return device_mask_file
+
+
+def set_diagmdlog_command(ad, maskfile):
+ """Sets diagmdlog command to run in background
+
+ Args:
+ ad: the target android device, AndroidDevice object
+ maskfile: mask file name
+
+ """
+ cfg_path = os.path.join(DEVICE_CFG_FOLDER, maskfile)
+ ad.adb.shell(NOHUP_CMD.format(cfg_path, DEVICE_DIAGMDLOG_FOLDER))
+ ad.log.info("Running diag_mdlog in the background")
+ time.sleep(MDLOG_SETTLING_TIME)
+
+
+def verify_diagmd_folder_exists(ad):
+ """Verify diagmd folder existence in device
+
+ Args:
+ ad: the target android device, AndroidDevice object
+
+ """
+ mask_folder_exists = ad.adb.shell(
+ 'test -d %s && echo exists' % DEVICE_CFG_FOLDER)
+ diag_folder_exists = ad.adb.shell(
+ 'test -d %s && echo exists' % DEVICE_DIAGMDLOG_FOLDER)
+ if not mask_folder_exists and diag_folder_exists:
+ ad.adb.shell("mkdir " + DEVICE_CFG_FOLDER)
+ ad.adb.shell("mkdir " + DEVICE_DIAGMDLOG_FOLDER)
+
+
+def start_diagmdlog_background(ad, maskfile="default.cfg", is_local=True):
+ """Runs diagmd_log in background
+
+ Args:
+ ad: the target android device, AndroidDevice object
+ maskfile: Local Mask file path or Device's mask file name
+ is_local: False, take cfgfile from config.
+ True, find cfgfile in device and run diagmdlog
+
+ Raises:
+ FileNotFoundError if maskfile is not present
+ ProcessLookupError if diagmdlog process not present
+ """
+ if is_local:
+ find_device_qxdm_log_mask(ad, maskfile)
+ set_diagmdlog_command(ad, maskfile)
+ else:
+ if not os.path.isfile(maskfile):
+ # errno.ENOENT - No such file or directory
+ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
+ maskfile)
+ else:
+ cfgfilename = os.path.basename(maskfile)
+ verify_diagmd_folder_exists(ad)
+ ad.adb.push("{} {}".format(maskfile, DEVICE_CFG_FOLDER))
+ set_diagmdlog_command(ad, cfgfilename)
+ output = ad.adb.shell("pgrep diag_mdlog")
+ ad.log.info("Checking diag_mdlog in process")
+ if not output:
+ # errno.ESRCH - No such process
+ raise ProcessLookupError(errno.ESRCH, os.strerror(errno.ESRCH),
+ "diag_mdlog")
+
+
+def stop_background_diagmdlog(ad, local_logpath, keep_logs=True):
+ """Stop diagmdlog and pulls diag_mdlog from android device
+
+ Args:
+ ad: the target android device, AndroidDevice object
+ local_logpath: Local file path to pull the diag_mdlog logs
+ keep_logs: False, delete log files from the diag_mdlog path
+
+ Raises:
+ ProcessLookupError if diagmdlog process not present
+ """
+ ps_output = ad.adb.shell("pgrep diag_mdlog")
+ ad.log.info("Checking diag_mdlog in process")
+ if ps_output:
+ output = ad.adb.shell("diag_mdlog -k")
+ time.sleep(MDLOG_PROCESS_KILL_TIME)
+ if "stopping" in output:
+ ad.log.debug("Stopping diag_mdlog")
+ ad.adb.pull("{} {}".format(DEVICE_DIAGMDLOG_FOLDER, local_logpath))
+ ad.log.debug("Pulling diag_logs from the device to local")
+ if not keep_logs:
+ ad.adb.shell("rm -rf " + DEVICE_DIAGMDLOG_FOLDER + "*.*")
+ ad.log.debug("diagmd logs are deleted from device")
+ else:
+ ad.log.debug("diagmd logs are not deleted from device")
+ else:
+ output = ad.adb.shell("pidof diag_mdlog")
+ if output:
+ ad.adb.shell("kill -9 {}".format(output))
+ ad.log.debug("Kill the existing qxdm process")
+ ad.adb.pull("{} {}".format(DEVICE_DIAGMDLOG_FOLDER,
+ local_logpath))
+ ad.log.debug("Pulling diag_logs from the device to local")
+ else:
+ # errno.ESRCH - No such process
+ raise ProcessLookupError(errno.ESRCH, os.strerror(errno.ESRCH),
+ "diag_mdlog")
+ else:
+ # errno.ESRCH - No such process
+ raise ProcessLookupError(errno.ESRCH, os.strerror(errno.ESRCH),
+ "diag_mdlog")
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py
new file mode 100644
index 0000000..0c94b49
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py
@@ -0,0 +1,1475 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import re
+import os
+import math
+import shutil
+import fnmatch
+import posixpath
+import tempfile
+from collections import namedtuple
+
+from acts import utils
+from acts import signals
+from acts.libs.proc import job
+from acts.controllers.android_device import list_adb_devices
+from acts.controllers.android_device import list_fastboot_devices
+from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
+from acts.controllers.android_device import SL4A_APK_NAME
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_test_utils as tutils
+from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder
+from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder
+from acts.utils import get_current_epoch_time
+from acts.utils import epoch_to_human_time
+
+WifiEnums = wutils.WifiEnums
+PULL_TIMEOUT = 300
+GNSSSTATUS_LOG_PATH = (
+ "/storage/emulated/0/Android/data/com.android.gpstool/files/")
+QXDM_MASKS = ["GPS.cfg", "GPS-general.cfg", "default.cfg"]
+TTFF_REPORT = namedtuple(
+ "TTFF_REPORT", "utc_time ttff_loop ttff_sec ttff_pe ttff_ant_cn "
+ "ttff_base_cn")
+TRACK_REPORT = namedtuple(
+ "TRACK_REPORT", "l5flag pe ant_top4cn ant_cn base_top4cn base_cn")
+LOCAL_PROP_FILE_CONTENTS = """\
+log.tag.LocationManagerService=VERBOSE
+log.tag.GnssLocationProvider=VERBOSE
+log.tag.GnssMeasurementsProvider=VERBOSE
+log.tag.GpsNetInitiatedHandler=VERBOSE
+log.tag.GnssNetInitiatedHandler=VERBOSE
+log.tag.GnssNetworkConnectivityHandler=VERBOSE
+log.tag.ConnectivityService=VERBOSE
+log.tag.ConnectivityManager=VERBOSE
+log.tag.GnssVisibilityControl=VERBOSE
+log.tag.NtpTimeHelper=VERBOSE
+log.tag.NtpTrustedTime=VERBOSE
+log.tag.GnssPsdsDownloader=VERBOSE
+log.tag.Gnss=VERBOSE
+log.tag.GnssConfiguration=VERBOSE"""
+TEST_PACKAGE_NAME = "com.google.android.apps.maps"
+LOCATION_PERMISSIONS = [
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.ACCESS_COARSE_LOCATION"
+]
+GNSSTOOL_PACKAGE_NAME = "com.android.gpstool"
+GNSSTOOL_PERMISSIONS = [
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.READ_EXTERNAL_STORAGE",
+ "android.permission.ACCESS_COARSE_LOCATION",
+ "android.permission.CALL_PHONE",
+ "android.permission.WRITE_CONTACTS",
+ "android.permission.CAMERA",
+ "android.permission.WRITE_EXTERNAL_STORAGE",
+ "android.permission.READ_CONTACTS",
+ "android.permission.ACCESS_BACKGROUND_LOCATION"
+]
+
+
+class GnssTestUtilsError(Exception):
+ pass
+
+
+def remount_device(ad):
+ """Remount device file system to read and write.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ for retries in range(5):
+ ad.root_adb()
+ if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
+ ad.adb.disable_verity()
+ reboot(ad)
+ remount_result = ad.adb.remount()
+ ad.log.info("Attempt %d - %s" % (retries + 1, remount_result))
+ if "remount succeeded" in remount_result:
+ break
+
+
+def reboot(ad):
+ """Reboot device and check if mobile data is available.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.log.info("Reboot device to make changes take effect.")
+ ad.reboot()
+ ad.unlock_screen(password=None)
+ if not int(ad.adb.shell("settings get global mobile_data")) == 1:
+ set_mobile_data(ad, True)
+ utils.sync_device_time(ad)
+
+
+def enable_gnss_verbose_logging(ad):
+ """Enable GNSS VERBOSE Logging and persistent logcat.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ ad.log.info("Enable GNSS VERBOSE Logging and persistent logcat.")
+ ad.adb.shell("echo -e '\nDEBUG_LEVEL = 5' >> /vendor/etc/gps.conf")
+ ad.adb.shell("echo %r >> /data/local.prop" % LOCAL_PROP_FILE_CONTENTS)
+ ad.adb.shell("chmod 644 /data/local.prop")
+ ad.adb.shell("setprop persist.logd.logpersistd.size 20000")
+ ad.adb.shell("setprop persist.logd.size 16777216")
+ ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
+ ad.adb.shell("setprop persist.logd.logpersistd logcatd")
+ ad.adb.shell("setprop log.tag.copresGcore VERBOSE")
+ ad.adb.shell("sync")
+
+
+def get_am_flags(value):
+ """Returns the (value, type) flags for a given python value."""
+ if type(value) is bool:
+ return str(value).lower(), 'boolean'
+ elif type(value) is str:
+ return value, 'string'
+ raise ValueError("%s should be either 'boolean' or 'string'" % value)
+
+
+def enable_compact_and_particle_fusion_log(ad):
+ """Enable CompactLog, FLP particle fusion log and disable gms
+ location-based quake monitoring.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.root_adb()
+ ad.log.info("Enable FLP flags and Disable GMS location-based quake "
+ "monitoring.")
+ overrides = {
+ 'compact_log_enabled': True,
+ 'flp_use_particle_fusion': True,
+ 'flp_particle_fusion_extended_bug_report': True,
+ 'flp_event_log_size': '86400',
+ 'proks_config': '28',
+ 'flp_particle_fusion_bug_report_window_sec': '86400',
+ 'flp_particle_fusion_bug_report_max_buffer_size': '86400',
+ 'seismic_data_collection': False,
+ 'Ealert__enable': False,
+ }
+ for flag, python_value in overrides.items():
+ value, type = get_am_flags(python_value)
+ cmd = ("am broadcast -a com.google.android.gms.phenotype.FLAG_OVERRIDE "
+ "--es package com.google.android.location --es user \* "
+ "--esa flags %s --esa values %s --esa types %s "
+ "com.google.android.gms" % (flag, value, type))
+ ad.adb.shell(cmd)
+ ad.adb.shell("am force-stop com.google.android.gms")
+ ad.adb.shell("am broadcast -a com.google.android.gms.INITIALIZE")
+
+
+def disable_xtra_throttle(ad):
+ """Disable XTRA throttle will have no limit to download XTRA data.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ ad.log.info("Disable XTRA Throttle.")
+ ad.adb.shell("echo -e '\nXTRA_TEST_ENABLED=1' >> /vendor/etc/gps.conf")
+ ad.adb.shell("echo -e '\nXTRA_THROTTLE_ENABLED=0' >> /vendor/etc/gps.conf")
+
+
+def enable_supl_mode(ad):
+ """Enable SUPL back on for next test item.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ ad.log.info("Enable SUPL mode.")
+ ad.adb.shell("echo -e '\nSUPL_MODE=1' >> /etc/gps_debug.conf")
+
+
+def disable_supl_mode(ad):
+ """Kill SUPL to test XTRA only test item.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ ad.log.info("Disable SUPL mode.")
+ ad.adb.shell("echo -e '\nSUPL_MODE=0' >> /etc/gps_debug.conf")
+ reboot(ad)
+
+
+def kill_xtra_daemon(ad):
+ """Kill XTRA daemon to test SUPL only test item.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.root_adb()
+ ad.log.info("Disable XTRA-daemon until next reboot.")
+ ad.adb.shell("killall xtra-daemon", ignore_status=True)
+
+
+def disable_private_dns_mode(ad):
+ """Due to b/118365122, it's better to disable private DNS mode while
+ testing. 8.8.8.8 private dns sever is unstable now, sometimes server
+ will not response dns query suddenly.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ tutils.get_operator_name(ad.log, ad, subId=None)
+ if ad.adb.shell("settings get global private_dns_mode") != "off":
+ ad.log.info("Disable Private DNS mode.")
+ ad.adb.shell("settings put global private_dns_mode off")
+
+
+def _init_device(ad):
+ """Init GNSS test devices.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ enable_gnss_verbose_logging(ad)
+ enable_compact_and_particle_fusion_log(ad)
+ disable_xtra_throttle(ad)
+ enable_supl_mode(ad)
+ ad.adb.shell("settings put system screen_off_timeout 1800000")
+ wutils.wifi_toggle_state(ad, False)
+ ad.log.info("Setting Bluetooth state to False")
+ ad.droid.bluetoothToggleState(False)
+ set_gnss_qxdm_mask(ad, QXDM_MASKS)
+ check_location_service(ad)
+ set_wifi_and_bt_scanning(ad, True)
+ disable_private_dns_mode(ad)
+ reboot(ad)
+ init_gtw_gpstool(ad)
+
+
+def connect_to_wifi_network(ad, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ ad: An AndroidDevice object.
+ network: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ ad.ed.clear_all_events()
+ wutils.reset_wifi(ad)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(ad, SSID)
+ wutils.wifi_connect(ad, network, num_of_tries=5)
+
+
+def set_wifi_and_bt_scanning(ad, state=True):
+ """Set Wi-Fi and Bluetooth scanning on/off in Settings -> Location
+
+ Args:
+ ad: An AndroidDevice object.
+ state: True to turn on "Wi-Fi and Bluetooth scanning".
+ False to turn off "Wi-Fi and Bluetooth scanning".
+ """
+ ad.root_adb()
+ if state:
+ ad.adb.shell("settings put global wifi_scan_always_enabled 1")
+ ad.adb.shell("settings put global ble_scan_always_enabled 1")
+ ad.log.info("Wi-Fi and Bluetooth scanning are enabled")
+ else:
+ ad.adb.shell("settings put global wifi_scan_always_enabled 0")
+ ad.adb.shell("settings put global ble_scan_always_enabled 0")
+ ad.log.info("Wi-Fi and Bluetooth scanning are disabled")
+
+
+def check_location_service(ad):
+ """Set location service on.
+ Verify if location service is available.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ utils.set_location_service(ad, True)
+ location_mode = int(ad.adb.shell("settings get secure location_mode"))
+ ad.log.info("Current Location Mode >> %d" % location_mode)
+ if location_mode != 3:
+ raise signals.TestError("Failed to turn Location on")
+
+
+def clear_logd_gnss_qxdm_log(ad):
+ """Clear /data/misc/logd,
+ /storage/emulated/0/Android/data/com.android.gpstool/files and
+ /data/vendor/radio/diag_logs/logs from previous test item then reboot.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ ad.log.info("Clear Logd, GNSS and QXDM Log from previous test item.")
+ ad.adb.shell("rm -rf /data/misc/logd", ignore_status=True)
+ ad.adb.shell(
+ 'find %s -name "*.txt" -type f -delete' % GNSSSTATUS_LOG_PATH,
+ ignore_status=True)
+ output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs")
+ ad.adb.shell("rm -rf %s" % output_path, ignore_status=True)
+ reboot(ad)
+
+
+def get_gnss_qxdm_log(ad, qdb_path):
+ """Get /storage/emulated/0/Android/data/com.android.gpstool/files and
+ /data/vendor/radio/diag_logs/logs for test item.
+
+ Args:
+ ad: An AndroidDevice object.
+ qdb_path: The path of qdsp6m.qdb on different projects.
+ """
+ log_path = ad.device_log_path
+ os.makedirs(log_path, exist_ok=True)
+ gnss_log_name = "gnssstatus_log_%s_%s" % (ad.model, ad.serial)
+ gnss_log_path = posixpath.join(log_path, gnss_log_name)
+ os.makedirs(gnss_log_path, exist_ok=True)
+ ad.log.info("Pull GnssStatus Log to %s" % gnss_log_path)
+ ad.adb.pull("%s %s" % (GNSSSTATUS_LOG_PATH+".", gnss_log_path),
+ timeout=PULL_TIMEOUT, ignore_status=True)
+ shutil.make_archive(gnss_log_path, "zip", gnss_log_path)
+ shutil.rmtree(gnss_log_path)
+ output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs/.")
+ file_count = ad.adb.shell(
+ "find %s -type f -iname *.qmdl | wc -l" % output_path)
+ if not int(file_count) == 0:
+ qxdm_log_name = "QXDM_%s_%s" % (ad.model, ad.serial)
+ qxdm_log_path = posixpath.join(log_path, qxdm_log_name)
+ os.makedirs(qxdm_log_path, exist_ok=True)
+ ad.log.info("Pull QXDM Log %s to %s" % (output_path, qxdm_log_path))
+ ad.adb.pull("%s %s" % (output_path, qxdm_log_path),
+ timeout=PULL_TIMEOUT, ignore_status=True)
+ for path in qdb_path:
+ output = ad.adb.pull("%s %s" % (path, qxdm_log_path),
+ timeout=PULL_TIMEOUT, ignore_status=True)
+ if "No such file or directory" in output:
+ continue
+ break
+ shutil.make_archive(qxdm_log_path, "zip", qxdm_log_path)
+ shutil.rmtree(qxdm_log_path)
+ else:
+ ad.log.error("QXDM file count is %d. There is no QXDM log on device."
+ % int(file_count))
+
+
+def set_mobile_data(ad, state):
+ """Set mobile data on or off and check mobile data state.
+
+ Args:
+ ad: An AndroidDevice object.
+ state: True to enable mobile data. False to disable mobile data.
+ """
+ ad.root_adb()
+ if state:
+ ad.log.info("Enable mobile data.")
+ ad.adb.shell("svc data enable")
+ else:
+ ad.log.info("Disable mobile data.")
+ ad.adb.shell("svc data disable")
+ time.sleep(5)
+ out = int(ad.adb.shell("settings get global mobile_data"))
+ if state and out == 1:
+ ad.log.info("Mobile data is enabled and set to %d" % out)
+ elif not state and out == 0:
+ ad.log.info("Mobile data is disabled and set to %d" % out)
+ else:
+ ad.log.error("Mobile data is at unknown state and set to %d" % out)
+
+
+def gnss_trigger_modem_ssr_by_adb(ad, dwelltime=60):
+ """Trigger modem SSR crash by adb and verify if modem crash and recover
+ successfully.
+
+ Args:
+ ad: An AndroidDevice object.
+ dwelltime: Waiting time for modem reset. Default is 60 seconds.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ begin_time = get_current_epoch_time()
+ ad.root_adb()
+ cmds = ("echo restart > /sys/kernel/debug/msm_subsys/modem",
+ r"echo 'at+cfun=1,1\r' > /dev/at_mdm0")
+ for cmd in cmds:
+ ad.log.info("Triggering modem SSR crash by %s" % cmd)
+ output = ad.adb.shell(cmd, ignore_status=True)
+ if "No such file or directory" in output:
+ continue
+ break
+ time.sleep(dwelltime)
+ ad.send_keycode("HOME")
+ logcat_results = ad.search_logcat("SSRObserver", begin_time)
+ if logcat_results:
+ for ssr in logcat_results:
+ if "mSubsystem='modem', mCrashReason" in ssr["log_message"]:
+ ad.log.debug(ssr["log_message"])
+ ad.log.info("Triggering modem SSR crash successfully.")
+ return True
+ raise signals.TestError("Failed to trigger modem SSR crash")
+ raise signals.TestError("No SSRObserver found in logcat")
+
+
+def gnss_trigger_modem_ssr_by_mds(ad, dwelltime=60):
+ """Trigger modem SSR crash by mds tool and verify if modem crash and recover
+ successfully.
+
+ Args:
+ ad: An AndroidDevice object.
+ dwelltime: Waiting time for modem reset. Default is 60 seconds.
+ """
+ mds_check = ad.adb.shell("pm path com.google.mdstest")
+ if not mds_check:
+ raise signals.TestError("MDS Tool is not properly installed.")
+ ad.root_adb()
+ cmd = ('am instrument -w -e request "4b 25 03 00" '
+ '"com.google.mdstest/com.google.mdstest.instrument'
+ '.ModemCommandInstrumentation"')
+ ad.log.info("Triggering modem SSR crash by MDS")
+ output = ad.adb.shell(cmd, ignore_status=True)
+ ad.log.debug(output)
+ time.sleep(dwelltime)
+ ad.send_keycode("HOME")
+ if "SUCCESS" in output:
+ ad.log.info("Triggering modem SSR crash by MDS successfully.")
+ else:
+ raise signals.TestError(
+ "Failed to trigger modem SSR crash by MDS. \n%s" % output)
+
+
+def check_xtra_download(ad, begin_time):
+ """Verify XTRA download success log message in logcat.
+
+ Args:
+ ad: An AndroidDevice object.
+ begin_time: test begin time
+
+ Returns:
+ True: xtra_download if XTRA downloaded and injected successfully
+ otherwise return False.
+ """
+ ad.send_keycode("HOME")
+ logcat_results = ad.search_logcat("XTRA download success. "
+ "inject data into modem", begin_time)
+ if logcat_results:
+ ad.log.debug("%s" % logcat_results[-1]["log_message"])
+ ad.log.info("XTRA downloaded and injected successfully.")
+ return True
+ ad.log.error("XTRA downloaded FAIL.")
+ return False
+
+
+def pull_package_apk(ad, package_name):
+ """Pull apk of given package_name from device.
+
+ Args:
+ ad: An AndroidDevice object.
+ package_name: Package name of apk to pull.
+
+ Returns:
+ The temp path of pulled apk.
+ """
+ apk_path = None
+ out = ad.adb.shell("pm path %s" % package_name)
+ result = re.search(r"package:(.*)", out)
+ if not result:
+ tutils.abort_all_tests(ad.log, "Couldn't find apk of %s" % package_name)
+ else:
+ apk_source = result.group(1)
+ ad.log.info("Get apk of %s from %s" % (package_name, apk_source))
+ apk_path = tempfile.mkdtemp()
+ ad.pull_files([apk_source], apk_path)
+ return apk_path
+
+
+def reinstall_package_apk(ad, package_name, apk_path):
+ """Reinstall apk of given package_name.
+
+ Args:
+ ad: An AndroidDevice object.
+ package_name: Package name of apk.
+ apk_path: The temp path of pulled apk.
+ """
+ for path_key in os.listdir(apk_path):
+ if fnmatch.fnmatch(path_key, "*.apk"):
+ apk_path = os.path.join(apk_path, path_key)
+ break
+ else:
+ raise signals.TestError("No apk is found in %s" % apk_path)
+ ad.log.info("Re-install %s with path: %s" % (package_name, apk_path))
+ ad.adb.shell("settings put global verifier_verify_adb_installs 0")
+ ad.adb.install("-r -d -g --user 0 %s" % apk_path)
+ package_check = ad.adb.shell("pm path %s" % package_name)
+ if not package_check:
+ tutils.abort_all_tests(
+ ad.log, "%s is not properly re-installed." % package_name)
+ ad.log.info("%s is re-installed successfully." % package_name)
+
+
+def init_gtw_gpstool(ad):
+ """Init GTW_GPSTool apk.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ remount_device(ad)
+ gpstool_path = pull_package_apk(ad, "com.android.gpstool")
+ reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
+
+
+def fastboot_factory_reset(ad):
+ """Factory reset the device in fastboot mode.
+ Pull sl4a apk from device. Terminate all sl4a sessions,
+ Reboot the device to bootloader,
+ factory reset the device by fastboot.
+ Reboot the device. wait for device to complete booting
+ Re-install and start an sl4a session.
+
+ Args:
+ ad: An AndroidDevice object.
+
+ Returns:
+ True if factory reset process complete.
+ """
+ status = True
+ skip_setup_wizard = True
+ sl4a_path = pull_package_apk(ad, SL4A_APK_NAME)
+ gpstool_path = pull_package_apk(ad, "com.android.gpstool")
+ mds_path = pull_package_apk(ad, "com.google.mdstest")
+ tutils.stop_qxdm_logger(ad)
+ ad.stop_services()
+ attempts = 3
+ for i in range(1, attempts + 1):
+ try:
+ if ad.serial in list_adb_devices():
+ ad.log.info("Reboot to bootloader")
+ ad.adb.reboot("bootloader", ignore_status=True)
+ time.sleep(10)
+ if ad.serial in list_fastboot_devices():
+ ad.log.info("Factory reset in fastboot")
+ ad.fastboot._w(timeout=300, ignore_status=True)
+ time.sleep(30)
+ ad.log.info("Reboot in fastboot")
+ ad.fastboot.reboot()
+ ad.wait_for_boot_completion()
+ ad.root_adb()
+ if ad.skip_sl4a:
+ break
+ if ad.is_sl4a_installed():
+ break
+ reinstall_package_apk(ad, SL4A_APK_NAME, sl4a_path)
+ reinstall_package_apk(ad, "com.android.gpstool", gpstool_path)
+ reinstall_package_apk(ad, "com.google.mdstest", mds_path)
+ time.sleep(10)
+ break
+ except Exception as e:
+ ad.log.error(e)
+ if i == attempts:
+ tutils.abort_all_tests(ad.log, str(e))
+ time.sleep(5)
+ try:
+ ad.start_adb_logcat()
+ except Exception as e:
+ ad.log.error(e)
+ if skip_setup_wizard:
+ ad.exit_setup_wizard()
+ if ad.skip_sl4a:
+ return status
+ tutils.bring_up_sl4a(ad)
+ return status
+
+
+def clear_aiding_data_by_gtw_gpstool(ad):
+ """Launch GTW GPSTool and Clear all GNSS aiding data.
+ Wait 5 seconds for GTW GPStool to clear all GNSS aiding
+ data properly.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.log.info("Launch GTW GPSTool and Clear all GNSS aiding data")
+ ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode clear")
+ time.sleep(10)
+
+
+def start_gnss_by_gtw_gpstool(ad,
+ state,
+ type="gnss",
+ bgdisplay=False,
+ freq=0,
+ lowpower=False,
+ meas=False):
+ """Start or stop GNSS on GTW_GPSTool.
+
+ Args:
+ ad: An AndroidDevice object.
+ state: True to start GNSS. False to Stop GNSS.
+ type: Different API for location fix. Use gnss/flp/nmea
+ bgdisplay: true to run GTW when Display off. false to not run GTW when
+ Display off.
+ freq: An integer to set location update frequency.
+ meas: A Boolean to set GNSS measurement registration.
+ lowpower: A boolean to set GNSS LowPowerMode.
+ """
+ cmd = "am start -S -n com.android.gpstool/.GPSTool --es mode gps"
+ if not state:
+ ad.log.info("Stop %s on GTW_GPSTool." % type)
+ cmd = "am broadcast -a com.android.gpstool.stop_gps_action"
+ else:
+ options = ("--es type {} --ei freq {} --ez BG {} --ez meas {} --ez "
+ "lowpower {}").format(type, freq, bgdisplay, meas, lowpower)
+ cmd = cmd + " " + options
+ ad.adb.shell(cmd)
+ time.sleep(3)
+
+
+def process_gnss_by_gtw_gpstool(ad,
+ criteria,
+ type="gnss",
+ clear_data=True,
+ meas_flag=False):
+ """Launch GTW GPSTool and Clear all GNSS aiding data
+ Start GNSS tracking on GTW_GPSTool.
+
+ Args:
+ ad: An AndroidDevice object.
+ criteria: Criteria for current test item.
+ type: Different API for location fix. Use gnss/flp/nmea
+ clear_data: True to clear GNSS aiding data. False is not to. Default
+ set to True.
+ meas_flag: True to enable GnssMeasurement. False is not to. Default
+ set to False.
+
+ Returns:
+ True: First fix TTFF are within criteria.
+ False: First fix TTFF exceed criteria.
+ """
+ retries = 3
+ for i in range(retries):
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+ check_adblog_functionality(ad)
+ check_location_runtime_permissions(
+ ad, GNSSTOOL_PACKAGE_NAME, GNSSTOOL_PERMISSIONS)
+ begin_time = get_current_epoch_time()
+ if clear_data:
+ clear_aiding_data_by_gtw_gpstool(ad)
+ ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (type.upper(),
+ i+1))
+ start_gnss_by_gtw_gpstool(ad, state=True, type=type, meas=meas_flag)
+ for _ in range(10 + criteria):
+ logcat_results = ad.search_logcat("First fixed", begin_time)
+ if logcat_results:
+ ad.log.debug(logcat_results[-1]["log_message"])
+ first_fixed = int(logcat_results[-1]["log_message"].split()[-1])
+ ad.log.info("%s First fixed = %.3f seconds" %
+ (type.upper(), first_fixed/1000))
+ if (first_fixed/1000) <= criteria:
+ return True
+ start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+ raise signals.TestFailure("Fail to get %s location fixed "
+ "within %d seconds criteria."
+ % (type.upper(), criteria))
+ time.sleep(1)
+ check_current_focus_app(ad)
+ start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+ raise signals.TestFailure("Fail to get %s location fixed within %d "
+ "attempts." % (type.upper(), retries))
+
+def start_ttff_by_gtw_gpstool(ad, ttff_mode, iteration, aid_data=False):
+ """Identify which TTFF mode for different test items.
+
+ Args:
+ ad: An AndroidDevice object.
+ ttff_mode: TTFF Test mode for current test item.
+ iteration: Iteration of TTFF cycles.
+ aid_data: Boolean for identify aid_data existed or not
+ """
+ begin_time = get_current_epoch_time()
+ if (ttff_mode == "hs" or ttff_mode == "ws") and not aid_data:
+ ad.log.info("Wait 5 minutes to start TTFF %s..." % ttff_mode.upper())
+ time.sleep(300)
+ if ttff_mode == "cs":
+ ad.log.info("Start TTFF Cold Start...")
+ time.sleep(3)
+ for i in range(1, 4):
+ ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
+ "--es ttff %s --es cycle %d" % (ttff_mode, iteration))
+ time.sleep(1)
+ if ad.search_logcat("act=com.android.gpstool.start_test_action",
+ begin_time):
+ ad.log.info("Send TTFF start_test_action successfully.")
+ break
+ else:
+ check_current_focus_app(ad)
+ raise signals.TestError("Fail to send TTFF start_test_action.")
+
+
+def gnss_tracking_via_gtw_gpstool(ad,
+ criteria,
+ type="gnss",
+ testtime=60,
+ meas_flag=False):
+ """Start GNSS/FLP tracking tests for input testtime on GTW_GPSTool.
+
+ Args:
+ ad: An AndroidDevice object.
+ criteria: Criteria for current TTFF.
+ type: Different API for location fix. Use gnss/flp/nmea
+ testtime: Tracking test time for minutes. Default set to 60 minutes.
+ meas_flag: True to enable GnssMeasurement. False is not to. Default
+ set to False.
+ """
+ process_gnss_by_gtw_gpstool(
+ ad, criteria=criteria, type=type, meas_flag=meas_flag)
+ ad.log.info("Start %s tracking test for %d minutes" % (type.upper(),
+ testtime))
+ begin_time = get_current_epoch_time()
+ while get_current_epoch_time() - begin_time < testtime * 60 * 1000:
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+ crash_result = ad.search_logcat("Force finishing activity "
+ "com.android.gpstool/.GPSTool",
+ begin_time)
+ if crash_result:
+ raise signals.TestError("GPSTool crashed. Abort test.")
+ ad.log.info("Successfully tested for %d minutes" % testtime)
+ start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+
+
+def parse_gtw_gpstool_log(ad, true_position, type="gnss"):
+ """Process GNSS/FLP API logs from GTW GPSTool and output track_data to
+ test_run_info for ACTS plugin to parse and display on MobileHarness as
+ Property.
+
+ Args:
+ ad: An AndroidDevice object.
+ true_position: Coordinate as [latitude, longitude] to calculate
+ position error.
+ type: Different API for location fix. Use gnss/flp/nmea
+ """
+ test_logfile = {}
+ track_data = {}
+ ant_top4_cn = 0
+ ant_cn = 0
+ base_top4_cn = 0
+ base_cn = 0
+ track_lat = 0
+ track_long = 0
+ l5flag = "false"
+ file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
+ % GNSSSTATUS_LOG_PATH))
+ if file_count != 1:
+ ad.log.error("%d API logs exist." % file_count)
+ dir_file = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
+ for path_key in dir_file:
+ if fnmatch.fnmatch(path_key, "*.txt"):
+ logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
+ out = ad.adb.shell("wc -c %s" % logpath)
+ file_size = int(out.split(" ")[0])
+ if file_size < 2000:
+ ad.log.info("Skip log %s due to log size %d bytes" %
+ (path_key, file_size))
+ continue
+ test_logfile = logpath
+ if not test_logfile:
+ raise signals.TestError("Failed to get test log file in device.")
+ lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
+ for line in lines:
+ if "Antenna_History Avg Top4" in line:
+ ant_top4_cn = float(line.split(":")[-1].strip())
+ elif "Antenna_History Avg" in line:
+ ant_cn = float(line.split(":")[-1].strip())
+ elif "Baseband_History Avg Top4" in line:
+ base_top4_cn = float(line.split(":")[-1].strip())
+ elif "Baseband_History Avg" in line:
+ base_cn = float(line.split(":")[-1].strip())
+ elif "L5 used in fix" in line:
+ l5flag = line.split(":")[-1].strip()
+ elif "Latitude" in line:
+ track_lat = float(line.split(":")[-1].strip())
+ elif "Longitude" in line:
+ track_long = float(line.split(":")[-1].strip())
+ elif "Time" in line:
+ track_utc = line.split("Time:")[-1].strip()
+ if track_utc in track_data.keys():
+ continue
+ pe = calculate_position_error(track_lat, track_long, true_position)
+ track_data[track_utc] = TRACK_REPORT(l5flag=l5flag,
+ pe=pe,
+ ant_top4cn=ant_top4_cn,
+ ant_cn=ant_cn,
+ base_top4cn=base_top4_cn,
+ base_cn=base_cn)
+ ad.log.debug(track_data)
+ prop_basename = "TestResult %s_tracking_" % type.upper()
+ time_list = sorted(track_data.keys())
+ l5flag_list = [track_data[key].l5flag for key in time_list]
+ pe_list = [float(track_data[key].pe) for key in time_list]
+ ant_top4cn_list = [float(track_data[key].ant_top4cn) for key in time_list]
+ ant_cn_list = [float(track_data[key].ant_cn) for key in time_list]
+ base_top4cn_list = [float(track_data[key].base_top4cn) for key in time_list]
+ base_cn_list = [float(track_data[key].base_cn) for key in time_list]
+ ad.log.info(prop_basename+"StartTime %s" % time_list[0].replace(" ", "-"))
+ ad.log.info(prop_basename+"EndTime %s" % time_list[-1].replace(" ", "-"))
+ ad.log.info(prop_basename+"TotalFixPoints %d" % len(time_list))
+ ad.log.info(prop_basename+"L5FixRate "+'{percent:.2%}'.format(
+ percent=l5flag_list.count("true")/len(l5flag_list)))
+ ad.log.info(prop_basename+"AvgDis %.1f" % (sum(pe_list)/len(pe_list)))
+ ad.log.info(prop_basename+"MaxDis %.1f" % max(pe_list))
+ ad.log.info(prop_basename+"Ant_AvgTop4Signal %.1f" % ant_top4cn_list[-1])
+ ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_cn_list[-1])
+ ad.log.info(prop_basename+"Base_AvgTop4Signal %.1f" % base_top4cn_list[-1])
+ ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_cn_list[-1])
+
+
+def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"):
+ """Process TTFF and record results in ttff_data.
+
+ Args:
+ ad: An AndroidDevice object.
+ begin_time: test begin time.
+ true_position: Coordinate as [latitude, longitude] to calculate
+ position error.
+ type: Different API for location fix. Use gnss/flp/nmea
+
+ Returns:
+ ttff_data: A dict of all TTFF data.
+ """
+ ttff_lat = 0
+ ttff_lon = 0
+ utc_time = epoch_to_human_time(get_current_epoch_time())
+ ttff_data = {}
+ ttff_loop_time = get_current_epoch_time()
+ while True:
+ if get_current_epoch_time() - ttff_loop_time >= 120000:
+ raise signals.TestError("Fail to search specific GPSService "
+ "message in logcat. Abort test.")
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+ logcat_results = ad.search_logcat("write TTFF log", ttff_loop_time)
+ if logcat_results:
+ ttff_loop_time = get_current_epoch_time()
+ ttff_log = logcat_results[-1]["log_message"].split()
+ ttff_loop = int(ttff_log[8].split(":")[-1])
+ ttff_sec = float(ttff_log[11])
+ if ttff_sec != 0.0:
+ ttff_ant_cn = float(ttff_log[18].strip("]"))
+ ttff_base_cn = float(ttff_log[25].strip("]"))
+ if type == "gnss":
+ gnss_results = ad.search_logcat("GPSService: Check item",
+ begin_time)
+ if gnss_results:
+ ad.log.debug(gnss_results[-1]["log_message"])
+ gnss_location_log = \
+ gnss_results[-1]["log_message"].split()
+ ttff_lat = float(
+ gnss_location_log[8].split("=")[-1].strip(","))
+ ttff_lon = float(
+ gnss_location_log[9].split("=")[-1].strip(","))
+ loc_time = int(
+ gnss_location_log[10].split("=")[-1].strip(","))
+ utc_time = epoch_to_human_time(loc_time)
+ elif type == "flp":
+ flp_results = ad.search_logcat("GPSService: FLP Location",
+ begin_time)
+ if flp_results:
+ ad.log.debug(flp_results[-1]["log_message"])
+ flp_location_log = flp_results[-1][
+ "log_message"].split()
+ ttff_lat = float(flp_location_log[8].split(",")[0])
+ ttff_lon = float(flp_location_log[8].split(",")[1])
+ utc_time = epoch_to_human_time(get_current_epoch_time())
+ else:
+ ttff_ant_cn = float(ttff_log[19].strip("]"))
+ ttff_base_cn = float(ttff_log[26].strip("]"))
+ ttff_lat = 0
+ ttff_lon = 0
+ utc_time = epoch_to_human_time(get_current_epoch_time())
+ ad.log.debug("TTFF Loop %d - (Lat, Lon) = (%s, %s)" % (ttff_loop,
+ ttff_lat,
+ ttff_lon))
+ ttff_pe = calculate_position_error(
+ ttff_lat, ttff_lon, true_position)
+ ttff_data[ttff_loop] = TTFF_REPORT(utc_time=utc_time,
+ ttff_loop=ttff_loop,
+ ttff_sec=ttff_sec,
+ ttff_pe=ttff_pe,
+ ttff_ant_cn=ttff_ant_cn,
+ ttff_base_cn=ttff_base_cn)
+ ad.log.info("UTC Time = %s, Loop %d = %.1f seconds, "
+ "Position Error = %.1f meters, "
+ "Antenna Average Signal = %.1f dbHz, "
+ "Baseband Average Signal = %.1f dbHz" % (utc_time,
+ ttff_loop,
+ ttff_sec,
+ ttff_pe,
+ ttff_ant_cn,
+ ttff_base_cn))
+ stop_gps_results = ad.search_logcat("stop gps test", begin_time)
+ if stop_gps_results:
+ ad.send_keycode("HOME")
+ break
+ crash_result = ad.search_logcat("Force finishing activity "
+ "com.android.gpstool/.GPSTool",
+ begin_time)
+ if crash_result:
+ raise signals.TestError("GPSTool crashed. Abort test.")
+ # wait 10 seconds to avoid logs not writing into logcat yet
+ time.sleep(10)
+ return ttff_data
+
+
+def check_ttff_data(ad, ttff_data, ttff_mode, criteria):
+ """Verify all TTFF results from ttff_data.
+
+ Args:
+ ad: An AndroidDevice object.
+ ttff_data: TTFF data of secs, position error and signal strength.
+ ttff_mode: TTFF Test mode for current test item.
+ criteria: Criteria for current test item.
+
+ Returns:
+ True: All TTFF results are within criteria.
+ False: One or more TTFF results exceed criteria or Timeout.
+ """
+ ad.log.info("%d iterations of TTFF %s tests finished."
+ % (len(ttff_data.keys()), ttff_mode))
+ ad.log.info("%s PASS criteria is %d seconds" % (ttff_mode, criteria))
+ ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
+ ttff_property_key_and_value(ad, ttff_data, ttff_mode)
+ if len(ttff_data.keys()) == 0:
+ ad.log.error("GTW_GPSTool didn't process TTFF properly.")
+ return False
+ elif any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
+ ad.log.error("One or more TTFF %s Timeout" % ttff_mode)
+ return False
+ elif any(float(ttff_data[key].ttff_sec) >= criteria for key in
+ ttff_data.keys()):
+ ad.log.error("One or more TTFF %s are over test criteria %d seconds"
+ % (ttff_mode, criteria))
+ return False
+ ad.log.info("All TTFF %s are within test criteria %d seconds."
+ % (ttff_mode, criteria))
+ return True
+
+
+def ttff_property_key_and_value(ad, ttff_data, ttff_mode):
+ """Output ttff_data to test_run_info for ACTS plugin to parse and display
+ on MobileHarness as Property.
+
+ Args:
+ ad: An AndroidDevice object.
+ ttff_data: TTFF data of secs, position error and signal strength.
+ ttff_mode: TTFF Test mode for current test item.
+ """
+ prop_basename = "TestResult "+ttff_mode.replace(" ", "_")+"_TTFF_"
+ sec_list = [float(ttff_data[key].ttff_sec) for key in ttff_data.keys()]
+ pe_list = [float(ttff_data[key].ttff_pe) for key in ttff_data.keys()]
+ ant_cn_list = [float(ttff_data[key].ttff_ant_cn) for key in
+ ttff_data.keys()]
+ base_cn_list = [float(ttff_data[key].ttff_base_cn) for key in
+ ttff_data.keys()]
+ timeoutcount = sec_list.count(0.0)
+ if len(sec_list) == timeoutcount:
+ avgttff = 9527
+ else:
+ avgttff = sum(sec_list)/(len(sec_list) - timeoutcount)
+ if timeoutcount != 0:
+ maxttff = 9527
+ else:
+ maxttff = max(sec_list)
+ avgdis = sum(pe_list)/len(pe_list)
+ maxdis = max(pe_list)
+ ant_avgcn = sum(ant_cn_list)/len(ant_cn_list)
+ base_avgcn = sum(base_cn_list)/len(base_cn_list)
+ ad.log.info(prop_basename+"AvgTime %.1f" % avgttff)
+ ad.log.info(prop_basename+"MaxTime %.1f" % maxttff)
+ ad.log.info(prop_basename+"TimeoutCount %d" % timeoutcount)
+ ad.log.info(prop_basename+"AvgDis %.1f" % avgdis)
+ ad.log.info(prop_basename+"MaxDis %.1f" % maxdis)
+ ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_avgcn)
+ ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_avgcn)
+
+
+def calculate_position_error(latitude, longitude, true_position):
+ """Use haversine formula to calculate position error base on true location
+ coordinate.
+
+ Args:
+ latitude: latitude of location fixed in the present.
+ longitude: longitude of location fixed in the present.
+ true_position: [latitude, longitude] of true location coordinate.
+
+ Returns:
+ position_error of location fixed in the present.
+ """
+ radius = 6371009
+ dlat = math.radians(latitude - true_position[0])
+ dlon = math.radians(longitude - true_position[1])
+ a = math.sin(dlat/2) * math.sin(dlat/2) + \
+ math.cos(math.radians(true_position[0])) * \
+ math.cos(math.radians(latitude)) * math.sin(dlon/2) * math.sin(dlon/2)
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ return radius * c
+
+
+def launch_google_map(ad):
+ """Launch Google Map via intent.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.log.info("Launch Google Map.")
+ try:
+ ad.adb.shell("am start -S -n com.google.android.apps.maps/"
+ "com.google.android.maps.MapsActivity")
+ ad.send_keycode("BACK")
+ ad.force_stop_apk("com.google.android.apps.maps")
+ ad.adb.shell("am start -S -n com.google.android.apps.maps/"
+ "com.google.android.maps.MapsActivity")
+ except Exception as e:
+ ad.log.error(e)
+ raise signals.TestError("Failed to launch google map.")
+ check_current_focus_app(ad)
+
+
+def check_current_focus_app(ad):
+ """Check to see current focused window and app.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ time.sleep(1)
+ current = ad.adb.shell(
+ "dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'")
+ ad.log.debug("\n"+current)
+
+
+def check_location_api(ad, retries):
+ """Verify if GnssLocationProvider API reports location.
+
+ Args:
+ ad: An AndroidDevice object.
+ retries: Retry time.
+
+ Returns:
+ True: GnssLocationProvider API reports location.
+ otherwise return False.
+ """
+ for i in range(retries):
+ begin_time = get_current_epoch_time()
+ ad.log.info("Try to get location report from GnssLocationProvider API "
+ "- attempt %d" % (i+1))
+ while get_current_epoch_time() - begin_time <= 30000:
+ logcat_results = ad.search_logcat("REPORT_LOCATION", begin_time)
+ if logcat_results:
+ ad.log.info("%s" % logcat_results[-1]["log_message"])
+ ad.log.info("GnssLocationProvider reports location "
+ "successfully.")
+ return True
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+ ad.log.error("GnssLocationProvider is unable to report location.")
+ return False
+
+def check_network_location(ad, retries, location_type, criteria=30):
+ """Verify if NLP reports location after requesting via GPSTool.
+
+ Args:
+ ad: An AndroidDevice object.
+ retries: Retry time.
+ location_type: cell or wifi.
+ criteria: expected nlp return time, default 30 seconds
+
+ Returns:
+ True: NLP reports location.
+ otherwise return False.
+ """
+ criteria = criteria * 1000
+ search_pattern = ("GPSTool : networkLocationType = %s" % location_type)
+ for i in range(retries):
+ begin_time = get_current_epoch_time()
+ ad.log.info("Try to get NLP status - attempt %d" % (i+1))
+ ad.adb.shell(
+ "am start -S -n com.android.gpstool/.GPSTool --es mode nlp")
+ while get_current_epoch_time() - begin_time <= criteria:
+ # Search pattern in 1 second interval
+ time.sleep(1)
+ result = ad.search_logcat(search_pattern, begin_time)
+ if result:
+ ad.log.info("Pattern Found: %s." % result[-1]["log_message"])
+ ad.send_keycode("BACK")
+ return True
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+ ad.send_keycode("BACK")
+ ad.log.error("Unable to report network location \"%s\"." % location_type)
+ return False
+
+
+def set_attenuator_gnss_signal(ad, attenuator, atten_value):
+ """Set attenuation value for different GNSS signal.
+
+ Args:
+ ad: An AndroidDevice object.
+ attenuator: The attenuator object.
+ atten_value: attenuation value
+ """
+ try:
+ ad.log.info(
+ "Set attenuation value to \"%d\" for GNSS signal." % atten_value)
+ attenuator[0].set_atten(atten_value)
+ except Exception as e:
+ ad.log.error(e)
+
+
+def set_battery_saver_mode(ad, state):
+ """Enable or disable battery saver mode via adb.
+
+ Args:
+ ad: An AndroidDevice object.
+ state: True is enable Battery Saver mode. False is disable.
+ """
+ ad.root_adb()
+ if state:
+ ad.log.info("Enable Battery Saver mode.")
+ ad.adb.shell("cmd battery unplug")
+ ad.adb.shell("settings put global low_power 1")
+ else:
+ ad.log.info("Disable Battery Saver mode.")
+ ad.adb.shell("settings put global low_power 0")
+ ad.adb.shell("cmd battery reset")
+
+
+def set_gnss_qxdm_mask(ad, masks):
+ """Find defined gnss qxdm mask and set as default logging mask.
+
+ Args:
+ ad: An AndroidDevice object.
+ masks: Defined gnss qxdm mask.
+ """
+ try:
+ for mask in masks:
+ if not tutils.find_qxdm_log_mask(ad, mask):
+ continue
+ tutils.set_qxdm_logger_command(ad, mask)
+ break
+ except Exception as e:
+ ad.log.error(e)
+ raise signals.TestError("Failed to set any QXDM masks.")
+
+
+def start_youtube_video(ad, url=None, retries=0):
+ """Start youtube video and verify if audio is in music state.
+
+ Args:
+ ad: An AndroidDevice object.
+ url: Youtube video url.
+ retries: Retry times if audio is not in music state.
+
+ Returns:
+ True if youtube video is playing normally.
+ False if youtube video is not playing properly.
+ """
+ for i in range(retries):
+ ad.log.info("Open an youtube video - attempt %d" % (i+1))
+ ad.adb.shell("am start -a android.intent.action.VIEW -d \"%s\"" % url)
+ time.sleep(2)
+ out = ad.adb.shell(
+ "dumpsys activity | grep NewVersionAvailableActivity")
+ if out:
+ ad.log.info("Skip Youtube New Version Update.")
+ ad.send_keycode("BACK")
+ if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
+ ad.log.info("Started a video in youtube, audio is in MUSIC state")
+ return True
+ ad.log.info("Force-Stop youtube and reopen youtube again.")
+ ad.force_stop_apk("com.google.android.youtube")
+ check_current_focus_app(ad)
+ raise signals.TestError("Started a video in youtube, "
+ "but audio is not in MUSIC state")
+
+
+def get_baseband_and_gms_version(ad, extra_msg=""):
+ """Get current radio baseband and GMSCore version of AndroidDevice object.
+
+ Args:
+ ad: An AndroidDevice object.
+ extra_msg: Extra message before or after the change.
+ """
+ try:
+ build_version = ad.adb.getprop("ro.build.id")
+ baseband_version = ad.adb.getprop("gsm.version.baseband")
+ gms_version = ad.adb.shell(
+ "dumpsys package com.google.android.gms | grep versionName"
+ ).split("\n")[0].split("=")[1]
+ mpss_version = ad.adb.shell("cat /sys/devices/soc0/images | grep MPSS "
+ "| cut -d ':' -f 3")
+ if not extra_msg:
+ ad.log.info("TestResult Build_Version %s" % build_version)
+ ad.log.info("TestResult Baseband_Version %s" % baseband_version)
+ ad.log.info(
+ "TestResult GMS_Version %s" % gms_version.replace(" ", ""))
+ ad.log.info("TestResult MPSS_Version %s" % mpss_version)
+ else:
+ ad.log.info(
+ "%s, Baseband_Version = %s" % (extra_msg, baseband_version))
+ except Exception as e:
+ ad.log.error(e)
+
+
+def start_toggle_gnss_by_gtw_gpstool(ad, iteration):
+ """Send toggle gnss off/on start_test_action
+
+ Args:
+ ad: An AndroidDevice object.
+ iteration: Iteration of toggle gnss off/on cycles.
+ """
+ msg_list = []
+ begin_time = get_current_epoch_time()
+ try:
+ for i in range(1, 4):
+ ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool "
+ "--es mode toggle --es cycle %d" % iteration)
+ time.sleep(1)
+ if ad.search_logcat("cmp=com.android.gpstool/.ToggleGPS",
+ begin_time):
+ ad.log.info("Send ToggleGPS start_test_action successfully.")
+ break
+ else:
+ check_current_focus_app(ad)
+ raise signals.TestError("Fail to send ToggleGPS "
+ "start_test_action within 3 attempts.")
+ time.sleep(2)
+ test_start = ad.search_logcat("GPSTool_ToggleGPS: startService",
+ begin_time)
+ if test_start:
+ ad.log.info(test_start[-1]["log_message"].split(":")[-1].strip())
+ else:
+ raise signals.TestError("Fail to start toggle GPS off/on test.")
+ # Every iteration is expected to finish within 4 minutes.
+ while get_current_epoch_time() - begin_time <= iteration * 240000:
+ crash_end = ad.search_logcat("Force finishing activity "
+ "com.android.gpstool/.GPSTool",
+ begin_time)
+ if crash_end:
+ raise signals.TestError("GPSTool crashed. Abort test.")
+ toggle_results = ad.search_logcat("GPSTool : msg", begin_time)
+ if toggle_results:
+ for toggle_result in toggle_results:
+ msg = toggle_result["log_message"]
+ if not msg in msg_list:
+ ad.log.info(msg.split(":")[-1].strip())
+ msg_list.append(msg)
+ if "timeout" in msg:
+ raise signals.TestFailure("Fail to get location fixed "
+ "within 60 seconds.")
+ if "Test end" in msg:
+ raise signals.TestPass("Completed quick toggle GNSS "
+ "off/on test.")
+ raise signals.TestFailure("Fail to finish toggle GPS off/on test "
+ "within %d minutes" % (iteration * 4))
+ finally:
+ ad.send_keycode("HOME")
+
+
+def grant_location_permission(ad, option):
+ """Grant or revoke location related permission.
+
+ Args:
+ ad: An AndroidDevice object.
+ option: Boolean to grant or revoke location related permissions.
+ """
+ action = "grant" if option else "revoke"
+ for permission in LOCATION_PERMISSIONS:
+ ad.log.info(
+ "%s permission:%s on %s" % (action, permission, TEST_PACKAGE_NAME))
+ ad.adb.shell("pm %s %s %s" % (action, TEST_PACKAGE_NAME, permission))
+
+
+def check_location_runtime_permissions(ad, package, permissions):
+ """Check if runtime permissions are granted on selected package.
+
+ Args:
+ ad: An AndroidDevice object.
+ package: Apk package name to check.
+ permissions: A list of permissions to be granted.
+ """
+ for _ in range(3):
+ location_runtime_permission = ad.adb.shell(
+ "dumpsys package %s | grep ACCESS_FINE_LOCATION" % package)
+ if "true" not in location_runtime_permission:
+ ad.log.info("ACCESS_FINE_LOCATION is NOT granted on %s" % package)
+ for permission in permissions:
+ ad.log.debug("Grant %s on %s" % (permission, package))
+ ad.adb.shell("pm grant %s %s" % (package, permission))
+ else:
+ ad.log.info("ACCESS_FINE_LOCATION is granted on %s" % package)
+ break
+ else:
+ raise signals.TestError(
+ "Fail to grant ACCESS_FINE_LOCATION on %s" % package)
+
+
+def install_mdstest_app(ad, mdsapp):
+ """
+ Install MDS test app in DUT
+
+ Args:
+ ad: An Android Device Object
+ mdsapp: Installation path of MDSTest app
+ """
+ if not ad.is_apk_installed("com.google.mdstest"):
+ ad.adb.install("-r %s" % mdsapp, timeout=300, ignore_status=True)
+
+
+def write_modemconfig(ad, mdsapp, nvitem_dict, modemparfile):
+ """
+ Modify the NV items using modem_tool.par
+ Note: modem_tool.par
+
+ Args:
+ ad: An Android Device Object
+ mdsapp: Installation path of MDSTest app
+ nvitem_dict: dictionary of NV items and values.
+ modemparfile: modem_tool.par path.
+ """
+ ad.log.info("Verify MDSTest app installed in DUT")
+ install_mdstest_app(ad, mdsapp)
+ os.system("chmod 777 %s" % modemparfile)
+ for key, value in nvitem_dict.items():
+ if key.isdigit():
+ op_name = "WriteEFS"
+ else:
+ op_name = "WriteNV"
+ ad.log.info("Modifying the NV{!r} using {}".format(key, op_name))
+ job.run("{} --op {} --item {} --data '{}'".
+ format(modemparfile, op_name, key, value))
+ time.sleep(2)
+
+
+def verify_modemconfig(ad, nvitem_dict, modemparfile):
+ """
+ Verify the NV items using modem_tool.par
+ Note: modem_tool.par
+
+ Args:
+ ad: An Android Device Object
+ nvitem_dict: dictionary of NV items and values
+ modemparfile: modem_tool.par path.
+ """
+ os.system("chmod 777 %s" % modemparfile)
+ for key, value in nvitem_dict.items():
+ if key.isdigit():
+ op_name = "ReadEFS"
+ else:
+ op_name = "ReadNV"
+ # Sleeptime to avoid Modem communication error
+ time.sleep(5)
+ result = job.run(
+ "{} --op {} --item {}".format(modemparfile, op_name, key))
+ output = str(result.stdout)
+ ad.log.info("Actual Value for NV{!r} is {!r}".format(key, output))
+ if not value.casefold() in output:
+ ad.log.error("NV Value is wrong {!r} in {!r}".format(value, result))
+ raise ValueError(
+ "could not find {!r} in {!r}".format(value, result))
+
+
+def check_ttff_pe(ad, ttff_data, ttff_mode, pe_criteria):
+ """Verify all TTFF results from ttff_data.
+
+ Args:
+ ad: An AndroidDevice object.
+ ttff_data: TTFF data of secs, position error and signal strength.
+ ttff_mode: TTFF Test mode for current test item.
+ pe_criteria: Criteria for current test item.
+
+ """
+ ad.log.info("%d iterations of TTFF %s tests finished.",
+ (len(ttff_data.keys()), ttff_mode))
+ ad.log.info("%s PASS criteria is %f meters", (ttff_mode, pe_criteria))
+ ad.log.debug("%s TTFF data: %s", (ttff_mode, ttff_data))
+
+ if len(ttff_data.keys()) == 0:
+ ad.log.error("GTW_GPSTool didn't process TTFF properly.")
+ raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
+
+ elif any(float(ttff_data[key].ttff_pe) >= pe_criteria for key in
+ ttff_data.keys()):
+ ad.log.error("One or more TTFF %s are over test criteria %f meters",
+ (ttff_mode, pe_criteria))
+ raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.")
+ ad.log.info("All TTFF %s are within test criteria %f meters.",
+ (ttff_mode, pe_criteria))
+
+
+def check_adblog_functionality(ad):
+ """Restart adb logcat if system can't write logs into file after checking
+ adblog file size.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ logcat_path = os.path.join(ad.device_log_path, "adblog_%s_debug.txt" %
+ ad.serial)
+ if not os.path.exists(logcat_path):
+ raise signals.TestError("Logcat file %s does not exist." % logcat_path)
+ original_log_size = os.path.getsize(logcat_path)
+ ad.log.debug("Original adblog size is %d" % original_log_size)
+ time.sleep(.5)
+ current_log_size = os.path.getsize(logcat_path)
+ ad.log.debug("Current adblog size is %d" % current_log_size)
+ if current_log_size == original_log_size:
+ ad.log.warn("System can't write logs into file. Restart adb "
+ "logcat process now.")
+ ad.stop_adb_logcat()
+ ad.start_adb_logcat()
+
+def build_instrumentation_call(package,
+ runner,
+ test_methods=None,
+ options=None):
+ """Build an instrumentation call for the tests
+
+ Args:
+ package: A string to identify test package.
+ runner: A string to identify test runner.
+ test_methods: A dictionary contains {class_name, test_method}.
+ options: A dictionary constant {key, value} param for test.
+
+ Returns:
+ An instrumentation call command.
+ """
+ if test_methods is None:
+ test_methods = {}
+ cmd_builder = InstrumentationCommandBuilder()
+ else:
+ cmd_builder = InstrumentationTestCommandBuilder()
+ if options is None:
+ options = {}
+ cmd_builder.set_manifest_package(package)
+ cmd_builder.set_runner(runner)
+ cmd_builder.add_flag("-w")
+ for class_name, test_method in test_methods.items():
+ cmd_builder.add_test_method(class_name, test_method)
+ for option_key, option_value in options.items():
+ cmd_builder.add_key_value_param(option_key, option_value)
+ return cmd_builder.build()
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py
new file mode 100644
index 0000000..54d47d7
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py
@@ -0,0 +1,465 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+'''Python Module for GNSS test log utilities.'''
+
+import re as regex
+import datetime
+import functools as fts
+import numpy as npy
+import pandas as pds
+from acts import logger
+
+# GPS API Log Reading Config
+CONFIG_GPSAPILOG = {
+ 'phone_time':
+ r'^(?P<date>\d+\/\d+\/\d+)\s+(?P<time>\d+:\d+:\d+)\s+'
+ r'Read:\s+(?P<logsize>\d+)\s+bytes',
+ 'SpaceVehicle':
+ r'^Fix:\s+(?P<Fix>\w+)\s+Type:\s+(?P<Type>\w+)\s+'
+ r'SV:\s+(?P<SV>\d+)\s+C\/No:\s+(?P<CNo>\d+\.\d+)\s+'
+ r'Elevation:\s+(?P<Elevation>\d+\.\d+)\s+'
+ r'Azimuth:\s+(?P<Azimuth>\d+\.\d+)\s+'
+ r'Signal:\s+(?P<Signal>\w+)\s+'
+ r'Frequency:\s+(?P<Frequency>\d+\.\d+)\s+'
+ r'EPH:\s+(?P<EPH>\w+)\s+ALM:\s+(?P<ALM>\w+)',
+ 'SpaceVehicle_wBB':
+ r'^Fix:\s+(?P<Fix>\w+)\s+Type:\s+(?P<Type>\w+)\s+'
+ r'SV:\s+(?P<SV>\d+)\s+C\/No:\s+(?P<AntCNo>\d+\.\d+),\s+'
+ r'(?P<BbCNo>\d+\.\d+)\s+'
+ r'Elevation:\s+(?P<Elevation>\d+\.\d+)\s+'
+ r'Azimuth:\s+(?P<Azimuth>\d+\.\d+)\s+'
+ r'Signal:\s+(?P<Signal>\w+)\s+'
+ r'Frequency:\s+(?P<Frequency>\d+\.\d+)\s+'
+ r'EPH:\s+(?P<EPH>\w+)\s+ALM:\s+(?P<ALM>\w+)',
+ 'HistoryAvgTop4CNo':
+ r'^History\s+Avg\s+Top4\s+:\s+(?P<HistoryAvgTop4CNo>\d+\.\d+)',
+ 'CurrentAvgTop4CNo':
+ r'^Current\s+Avg\s+Top4\s+:\s+(?P<CurrentAvgTop4CNo>\d+\.\d+)',
+ 'HistoryAvgCNo':
+ r'^History\s+Avg\s+:\s+(?P<HistoryAvgCNo>\d+\.\d+)',
+ 'CurrentAvgCNo':
+ r'^Current\s+Avg\s+:\s+(?P<CurrentAvgCNo>\d+\.\d+)',
+ 'AntennaHistoryAvgTop4CNo':
+ r'^Antenna_History\s+Avg\s+Top4\s+:\s+(?P<AntennaHistoryAvgTop4CNo>\d+\.\d+)',
+ 'AntennaCurrentAvgTop4CNo':
+ r'^Antenna_Current\s+Avg\s+Top4\s+:\s+(?P<AntennaCurrentAvgTop4CNo>\d+\.\d+)',
+ 'AntennaHistoryAvgCNo':
+ r'^Antenna_History\s+Avg\s+:\s+(?P<AntennaHistoryAvgCNo>\d+\.\d+)',
+ 'AntennaCurrentAvgCNo':
+ r'^Antenna_Current\s+Avg\s+:\s+(?P<AntennaCurrentAvgCNo>\d+\.\d+)',
+ 'BasebandHistoryAvgTop4CNo':
+ r'^Baseband_History\s+Avg\s+Top4\s+:\s+(?P<BasebandHistoryAvgTop4CNo>\d+\.\d+)',
+ 'BasebandCurrentAvgTop4CNo':
+ r'^Baseband_Current\s+Avg\s+Top4\s+:\s+(?P<BasebandCurrentAvgTop4CNo>\d+\.\d+)',
+ 'BasebandHistoryAvgCNo':
+ r'^Baseband_History\s+Avg\s+:\s+(?P<BasebandHistoryAvgCNo>\d+\.\d+)',
+ 'BasebandCurrentAvgCNo':
+ r'^Baseband_Current\s+Avg\s+:\s+(?P<BasebandCurrentAvgCNo>\d+\.\d+)',
+ 'L5inFix':
+ r'^L5\s+used\s+in\s+fix:\s+(?P<L5inFix>\w+)',
+ 'L5EngagingRate':
+ r'^L5\s+engaging\s+rate:\s+(?P<L5EngagingRate>\d+.\d+)%',
+ 'Provider':
+ r'^Provider:\s+(?P<Provider>\w+)',
+ 'Latitude':
+ r'^Latitude:\s+(?P<Latitude>-?\d+.\d+)',
+ 'Longitude':
+ r'^Longitude:\s+(?P<Longitude>-?\d+.\d+)',
+ 'Altitude':
+ r'^Altitude:\s+(?P<Altitude>-?\d+.\d+)',
+ 'GNSSTime':
+ r'^Time:\s+(?P<Date>\d+\/\d+\/\d+)\s+'
+ r'(?P<Time>\d+:\d+:\d+)',
+ 'Speed':
+ r'^Speed:\s+(?P<Speed>\d+.\d+)',
+ 'Bearing':
+ r'^Bearing:\s+(?P<Bearing>\d+.\d+)',
+}
+
+# Space Vehicle Statistics Dataframe List
+# Handle the pre GPSTool 2.12.24 case
+LIST_SVSTAT = [
+ 'HistoryAvgTop4CNo', 'CurrentAvgTop4CNo', 'HistoryAvgCNo', 'CurrentAvgCNo',
+ 'L5inFix', 'L5EngagingRate'
+]
+# Handle the post GPSTool 2.12.24 case with baseband CNo
+LIST_SVSTAT_WBB = [
+ 'AntennaHistoryAvgTop4CNo', 'AntennaCurrentAvgTop4CNo',
+ 'AntennaHistoryAvgCNo', 'AntennaCurrentAvgCNo',
+ 'BasebandHistoryAvgTop4CNo', 'BasebandCurrentAvgTop4CNo',
+ 'BasebandHistoryAvgCNo', 'BasebandCurrentAvgCNo', 'L5inFix',
+ 'L5EngagingRate'
+]
+
+# Location Fix Info Dataframe List
+LIST_LOCINFO = [
+ 'Provider', 'Latitude', 'Longitude', 'Altitude', 'GNSSTime', 'Speed',
+ 'Bearing'
+]
+
+# GPS TTFF Log Reading Config
+CONFIG_GPSTTFFLOG = {
+ 'ttff_info':
+ r'Loop:(?P<loop>\d+)\s+'
+ r'(?P<start_datetime>\d+\/\d+\/\d+-\d+:\d+:\d+.\d+)\s+'
+ r'(?P<stop_datetime>\d+\/\d+\/\d+-\d+:\d+:\d+.\d+)\s+'
+ r'(?P<ttff>\d+.\d+)\s+'
+ r'\[Antenna_Avg Top4 : (?P<ant_avg_top4_cn0>\d+.\d+)\]\s'
+ r'\[Antenna_Avg : (?P<ant_avg_cn0>\d+.\d+)\]\s'
+ r'\[Baseband_Avg Top4 : (?P<bb_avg_top4_cn0>\d+.\d+)\]\s'
+ r'\[Baseband_Avg : (?P<<bb_avg_cn0>\d+.\d+)\]\s+\[(?P<fix_type>\d+\w+ fix)\]\s+'
+ r'\[Satellites used for fix : (?P<satnum_for_fix>\d+)\]'
+}
+LOGPARSE_UTIL_LOGGER = logger.create_logger()
+
+
+def parse_log_to_df(filename, configs, index_rownum=True):
+ r"""Parse log to a dictionary of Pandas dataframes.
+
+ Args:
+ filename: log file name.
+ Type String.
+ configs: configs dictionary of parsed Pandas dataframes.
+ Type dictionary.
+ dict key, the parsed pattern name, such as 'Speed',
+ dict value, regex of the config pattern,
+ Type Raw String.
+ index_rownum: index row number from raw data.
+ Type Boolean.
+ Default, True.
+
+ Returns:
+ parsed_data: dictionary of parsed data.
+ Type dictionary.
+ dict key, the parsed pattern name, such as 'Speed',
+ dict value, the corresponding parsed dataframe.
+
+ Examples:
+ configs = {
+ 'GNSSTime':
+ r'Time:\s+(?P<Date>\d+\/\d+\/\d+)\s+
+ r(?P<Time>\d+:\d+:\d+)')},
+ 'Speed': r'Speed:\s+(?P<Speed>\d+.\d+)',
+ }
+ """
+ # Init a local config dictionary to hold compiled regex and match dict.
+ configs_local = {}
+ # Construct parsed data dictionary
+ parsed_data = {}
+
+ # Loop the config dictionary to compile regex and init data list
+ for key, regex_string in configs.items():
+ configs_local[key] = {
+ 'cregex': regex.compile(regex_string),
+ 'datalist': [],
+ }
+
+ # Open the file, loop and parse
+ with open(filename, 'r') as fid:
+
+ for idx_line, current_line in enumerate(fid):
+ for _, config in configs_local.items():
+ matched_log_object = config['cregex'].search(current_line)
+
+ if matched_log_object:
+ matched_data = matched_log_object.groupdict()
+ matched_data['rownumber'] = idx_line + 1
+ config['datalist'].append(matched_data)
+
+ # Loop to generate parsed data from configs list
+ for key, config in configs_local.items():
+ parsed_data[key] = pds.DataFrame(config['datalist'])
+ if index_rownum and not parsed_data[key].empty:
+ parsed_data[key].set_index('rownumber', inplace=True)
+ elif parsed_data[key].empty:
+ LOGPARSE_UTIL_LOGGER.debug(
+ 'The parsed dataframe of "%s" is empty.', key)
+
+ # Return parsed data list
+ return parsed_data
+
+
+def parse_gpstool_ttfflog_to_df(filename):
+ """Parse GPSTool ttff log to Pandas dataframes.
+
+ Args:
+ filename: full log file name.
+ Type, String.
+
+ Returns:
+ ttff_df: TTFF Data Frame.
+ Type, Pandas DataFrame.
+ """
+ # Get parsed dataframe list
+ parsed_data = parse_log_to_df(
+ filename=filename,
+ configs=CONFIG_GPSTTFFLOG,
+ )
+ ttff_df = parsed_data['ttff_info']
+
+ # Data Conversion
+ ttff_df['loop'] = ttff_df['loop'].astype(int)
+ ttff_df['start_datetime'] = pds.to_datetime(ttff_df['start_datetime'])
+ ttff_df['stop_datetime'] = pds.to_datetime(ttff_df['stop_datetime'])
+ ttff_df['ttff'] = ttff_df['ttff'].astype(float)
+ ttff_df['avg_top4_cn0'] = ttff_df['avg_top4_cn0'].astype(float)
+ ttff_df['avg_cn0'] = ttff_df['avg_cn0'].astype(float)
+ ttff_df['satnum_for_fix'] = ttff_df['satnum_for_fix'].astype(int)
+
+ # return ttff dataframe
+ return ttff_df
+
+
+def parse_gpsapilog_to_df(filename):
+ """Parse GPS API log to Pandas dataframes.
+
+ Args:
+ filename: full log file name.
+ Type, String.
+
+ Returns:
+ timestamp_df: Timestamp Data Frame.
+ Type, Pandas DataFrame.
+ sv_info_df: GNSS SV info Data Frame.
+ Type, Pandas DataFrame.
+ sv_stat_df: GNSS SV statistic Data Frame.
+ Type, Pandas DataFrame
+ loc_info_df: Location Information Data Frame.
+ Type, Pandas DataFrame.
+ include Provider, Latitude, Longitude, Altitude, GNSSTime, Speed, Bearing
+ """
+ def get_phone_time(target_df_row, timestamp_df):
+ """subfunction to get the phone_time."""
+
+ try:
+ row_num = timestamp_df[
+ timestamp_df.index < target_df_row.name].iloc[-1].name
+ phone_time = timestamp_df.loc[row_num]['phone_time']
+ except IndexError:
+ row_num = npy.NaN
+ phone_time = npy.NaN
+
+ return phone_time, row_num
+
+ # Get parsed dataframe list
+ parsed_data = parse_log_to_df(
+ filename=filename,
+ configs=CONFIG_GPSAPILOG,
+ )
+
+ # get DUT Timestamp
+ timestamp_df = parsed_data['phone_time']
+ timestamp_df['phone_time'] = timestamp_df.apply(
+ lambda row: datetime.datetime.strptime(row.date + '-' + row.time,
+ '%Y/%m/%d-%H:%M:%S'),
+ axis=1)
+
+ # Add phone_time from timestamp_df dataframe by row number
+ for key in parsed_data:
+ if key != 'phone_time':
+ current_df = parsed_data[key]
+ time_n_row_num = current_df.apply(get_phone_time,
+ axis=1,
+ timestamp_df=timestamp_df)
+ current_df[['phone_time', 'time_row_num'
+ ]] = pds.DataFrame(time_n_row_num.apply(pds.Series))
+
+ # Get space vehicle info dataframe
+ sv_info_df = parsed_data['SpaceVehicle']
+
+ # Get space vehicle statistics dataframe
+ # First merge all dataframe from LIST_SVSTAT[1:],
+ # Drop duplicated 'phone_time', based on time_row_num
+ sv_stat_df = fts.reduce(
+ lambda item1, item2: pds.merge(item1, item2, on='time_row_num'), [
+ parsed_data[key].drop(['phone_time'], axis=1)
+ for key in LIST_SVSTAT[1:]
+ ])
+ # Then merge with LIST_SVSTAT[0]
+ sv_stat_df = pds.merge(sv_stat_df,
+ parsed_data[LIST_SVSTAT[0]],
+ on='time_row_num')
+
+ # Get location fix information dataframe
+ # First merge all dataframe from LIST_LOCINFO[1:],
+ # Drop duplicated 'phone_time', based on time_row_num
+ loc_info_df = fts.reduce(
+ lambda item1, item2: pds.merge(item1, item2, on='time_row_num'), [
+ parsed_data[key].drop(['phone_time'], axis=1)
+ for key in LIST_LOCINFO[1:]
+ ])
+ # Then merge with LIST_LOCINFO[8]
+ loc_info_df = pds.merge(loc_info_df,
+ parsed_data[LIST_LOCINFO[0]],
+ on='time_row_num')
+ # Convert GNSS Time
+ loc_info_df['gnsstime'] = loc_info_df.apply(
+ lambda row: datetime.datetime.strptime(row.Date + '-' + row.Time,
+ '%Y/%m/%d-%H:%M:%S'),
+ axis=1)
+
+ return timestamp_df, sv_info_df, sv_stat_df, loc_info_df
+
+
+def parse_gpsapilog_to_df_v2(filename):
+ """Parse GPS API log to Pandas dataframes, by using merge_asof.
+
+ Args:
+ filename: full log file name.
+ Type, String.
+
+ Returns:
+ timestamp_df: Timestamp Data Frame.
+ Type, Pandas DataFrame.
+ sv_info_df: GNSS SV info Data Frame.
+ Type, Pandas DataFrame.
+ sv_stat_df: GNSS SV statistic Data Frame.
+ Type, Pandas DataFrame
+ loc_info_df: Location Information Data Frame.
+ Type, Pandas DataFrame.
+ include Provider, Latitude, Longitude, Altitude, GNSSTime, Speed, Bearing
+ """
+ # Get parsed dataframe list
+ parsed_data = parse_log_to_df(
+ filename=filename,
+ configs=CONFIG_GPSAPILOG,
+ )
+
+ # get DUT Timestamp
+ timestamp_df = parsed_data['phone_time']
+ timestamp_df['phone_time'] = timestamp_df.apply(
+ lambda row: datetime.datetime.strptime(row.date + '-' + row.time,
+ '%Y/%m/%d-%H:%M:%S'),
+ axis=1)
+ # drop logsize, date, time
+ parsed_data['phone_time'] = timestamp_df.drop(['logsize', 'date', 'time'],
+ axis=1)
+
+ # Add phone_time from timestamp dataframe by row number
+ for key in parsed_data:
+ if (key != 'phone_time') and (not parsed_data[key].empty):
+ parsed_data[key] = pds.merge_asof(parsed_data[key],
+ parsed_data['phone_time'],
+ left_index=True,
+ right_index=True)
+
+ # Get space vehicle info dataframe
+ # Handle the pre GPSTool 2.12.24 case
+ if not parsed_data['SpaceVehicle'].empty:
+ sv_info_df = parsed_data['SpaceVehicle']
+
+ # Handle the post GPSTool 2.12.24 case with baseband CNo
+ elif not parsed_data['SpaceVehicle_wBB'].empty:
+ sv_info_df = parsed_data['SpaceVehicle_wBB']
+
+ # Get space vehicle statistics dataframe
+ # Handle the pre GPSTool 2.12.24 case
+ if not parsed_data['HistoryAvgTop4CNo'].empty:
+ # First merge all dataframe from LIST_SVSTAT[1:],
+ sv_stat_df = fts.reduce(
+ lambda item1, item2: pds.merge(item1, item2, on='phone_time'),
+ [parsed_data[key] for key in LIST_SVSTAT[1:]])
+ # Then merge with LIST_SVSTAT[0]
+ sv_stat_df = pds.merge(sv_stat_df,
+ parsed_data[LIST_SVSTAT[0]],
+ on='phone_time')
+
+ # Handle the post GPSTool 2.12.24 case with baseband CNo
+ elif not parsed_data['AntennaHistoryAvgTop4CNo'].empty:
+ # First merge all dataframe from LIST_SVSTAT[1:],
+ sv_stat_df = fts.reduce(
+ lambda item1, item2: pds.merge(item1, item2, on='phone_time'),
+ [parsed_data[key] for key in LIST_SVSTAT_WBB[1:]])
+ # Then merge with LIST_SVSTAT[0]
+ sv_stat_df = pds.merge(sv_stat_df,
+ parsed_data[LIST_SVSTAT_WBB[0]],
+ on='phone_time')
+
+ # Get location fix information dataframe
+ # First merge all dataframe from LIST_LOCINFO[1:],
+ loc_info_df = fts.reduce(
+ lambda item1, item2: pds.merge(item1, item2, on='phone_time'),
+ [parsed_data[key] for key in LIST_LOCINFO[1:]])
+ # Then merge with LIST_LOCINFO[8]
+ loc_info_df = pds.merge(loc_info_df,
+ parsed_data[LIST_LOCINFO[0]],
+ on='phone_time')
+ # Convert GNSS Time
+ loc_info_df['gnsstime'] = loc_info_df.apply(
+ lambda row: datetime.datetime.strptime(row.Date + '-' + row.Time,
+ '%Y/%m/%d-%H:%M:%S'),
+ axis=1)
+
+ # Data Conversion
+ timestamp_df['logsize'] = timestamp_df['logsize'].astype(int)
+
+ sv_info_df['SV'] = sv_info_df['SV'].astype(int)
+ sv_info_df['Elevation'] = sv_info_df['Elevation'].astype(float)
+ sv_info_df['Azimuth'] = sv_info_df['Azimuth'].astype(float)
+ sv_info_df['Frequency'] = sv_info_df['Frequency'].astype(float)
+
+ if 'CNo' in list(sv_info_df.columns):
+ sv_info_df['CNo'] = sv_info_df['CNo'].astype(float)
+ sv_info_df['AntCNo'] = sv_info_df['CNo']
+ elif 'AntCNo' in list(sv_info_df.columns):
+ sv_info_df['AntCNo'] = sv_info_df['AntCNo'].astype(float)
+ sv_info_df['BbCNo'] = sv_info_df['BbCNo'].astype(float)
+
+ if 'CurrentAvgTop4CNo' in list(sv_stat_df.columns):
+ sv_stat_df['CurrentAvgTop4CNo'] = sv_stat_df[
+ 'CurrentAvgTop4CNo'].astype(float)
+ sv_stat_df['CurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo'].astype(float)
+ sv_stat_df['HistoryAvgTop4CNo'] = sv_stat_df[
+ 'HistoryAvgTop4CNo'].astype(float)
+ sv_stat_df['HistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo'].astype(float)
+ sv_stat_df['AntennaCurrentAvgTop4CNo'] = sv_stat_df[
+ 'CurrentAvgTop4CNo']
+ sv_stat_df['AntennaCurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo']
+ sv_stat_df['AntennaHistoryAvgTop4CNo'] = sv_stat_df[
+ 'HistoryAvgTop4CNo']
+ sv_stat_df['AntennaHistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo']
+ sv_stat_df['BasebandCurrentAvgTop4CNo'] = npy.nan
+ sv_stat_df['BasebandCurrentAvgCNo'] = npy.nan
+ sv_stat_df['BasebandHistoryAvgTop4CNo'] = npy.nan
+ sv_stat_df['BasebandHistoryAvgCNo'] = npy.nan
+
+ elif 'AntennaCurrentAvgTop4CNo' in list(sv_stat_df.columns):
+ sv_stat_df['AntennaCurrentAvgTop4CNo'] = sv_stat_df[
+ 'AntennaCurrentAvgTop4CNo'].astype(float)
+ sv_stat_df['AntennaCurrentAvgCNo'] = sv_stat_df[
+ 'AntennaCurrentAvgCNo'].astype(float)
+ sv_stat_df['AntennaHistoryAvgTop4CNo'] = sv_stat_df[
+ 'AntennaHistoryAvgTop4CNo'].astype(float)
+ sv_stat_df['AntennaHistoryAvgCNo'] = sv_stat_df[
+ 'AntennaHistoryAvgCNo'].astype(float)
+ sv_stat_df['BasebandCurrentAvgTop4CNo'] = sv_stat_df[
+ 'BasebandCurrentAvgTop4CNo'].astype(float)
+ sv_stat_df['BasebandCurrentAvgCNo'] = sv_stat_df[
+ 'BasebandCurrentAvgCNo'].astype(float)
+ sv_stat_df['BasebandHistoryAvgTop4CNo'] = sv_stat_df[
+ 'BasebandHistoryAvgTop4CNo'].astype(float)
+ sv_stat_df['BasebandHistoryAvgCNo'] = sv_stat_df[
+ 'BasebandHistoryAvgCNo'].astype(float)
+
+ sv_stat_df['L5EngagingRate'] = sv_stat_df['L5EngagingRate'].astype(float)
+
+ loc_info_df['Latitude'] = loc_info_df['Latitude'].astype(float)
+ loc_info_df['Longitude'] = loc_info_df['Longitude'].astype(float)
+ loc_info_df['Altitude'] = loc_info_df['Altitude'].astype(float)
+ loc_info_df['Speed'] = loc_info_df['Speed'].astype(float)
+ loc_info_df['Bearing'] = loc_info_df['Bearing'].astype(float)
+
+ return timestamp_df, sv_info_df, sv_stat_df, loc_info_df
diff --git a/acts_tests/acts_contrib/test_utils/instrumentation/__init__.py b/acts_tests/acts_contrib/test_utils/instrumentation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/instrumentation/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/instrumentation/device/__init__.py b/acts_tests/acts_contrib/test_utils/instrumentation/device/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/instrumentation/device/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/instrumentation/device/command/__init__.py b/acts_tests/acts_contrib/test_utils/instrumentation/device/command/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/instrumentation/device/command/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/instrumentation/device/command/instrumentation_command_builder.py b/acts_tests/acts_contrib/test_utils/instrumentation/device/command/instrumentation_command_builder.py
new file mode 100644
index 0000000..8fa2c72
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/instrumentation/device/command/instrumentation_command_builder.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import shlex
+
+DEFAULT_INSTRUMENTATION_LOG_OUTPUT = 'instrumentation_output.txt'
+
+
+class InstrumentationCommandBuilder(object):
+ """Helper class to build instrumentation commands."""
+
+ def __init__(self):
+ self._manifest_package_name = None
+ self._flags = []
+ self._key_value_params = {}
+ self._runner = None
+ self._nohup = False
+ self._proto_path = None
+ self._nohup_log_path = None
+ self._output_as_proto = False
+
+ def set_manifest_package(self, test_package):
+ self._manifest_package_name = test_package
+
+ def set_runner(self, runner):
+ self._runner = runner
+
+ def add_flag(self, param):
+ self._flags.append(param)
+
+ def remove_flag(self, param):
+ while self._flags.count(param):
+ self._flags.remove(param)
+
+ def add_key_value_param(self, key, value):
+ if isinstance(value, bool):
+ value = str(value).lower()
+ self._key_value_params[key] = str(value)
+
+ def set_proto_path(self, path=None):
+ """Sets a custom path to store result proto. Note that this path will
+ be relative to $EXTERNAL_STORAGE on device. Calling this function
+ automatically enables output as proto.
+
+ Args:
+ path: The $EXTERNAL_STORAGE subdirectory to write the result proto
+ to. If left as None, the default location will be used.
+ """
+ self._output_as_proto = True
+ self._proto_path = path
+
+ def set_output_as_text(self):
+ """This is the default behaviour. It will simply output the
+ instrumentation output to the devices stdout. If the nohup option is
+ enabled the instrumentation output will be redirected to the defined
+ path or its default.
+ """
+ self._output_as_proto = False
+ self._proto_path = None
+
+ def set_nohup(self, log_path=None):
+ """Enables nohup mode. This enables the instrumentation command to
+ continue running after a USB disconnect.
+
+ Args:
+ log_path: Path to store stdout of the process. Default is:
+ $EXTERNAL_STORAGE/instrumentation_output.txt
+ """
+ if log_path is None:
+ log_path = os.path.join('$EXTERNAL_STORAGE',
+ DEFAULT_INSTRUMENTATION_LOG_OUTPUT)
+ self._nohup = True
+ self._nohup_log_path = log_path
+
+ def build(self):
+ call = self._instrument_call_with_arguments()
+ call.append('{}/{}'.format(self._manifest_package_name, self._runner))
+ if self._nohup:
+ call = ['nohup'] + call
+ call.append('>>')
+ call.append(self._nohup_log_path)
+ call.append('2>&1')
+ return " ".join(call)
+
+ def _instrument_call_with_arguments(self):
+ errors = []
+ if self._manifest_package_name is None:
+ errors.append('manifest package cannot be none')
+ if self._runner is None:
+ errors.append('instrumentation runner cannot be none')
+ if len(errors) > 0:
+ raise Exception('instrumentation call build errors: {}'
+ .format(','.join(errors)))
+ call = ['am instrument']
+
+ for flag in self._flags:
+ call.append(flag)
+
+ if self._output_as_proto:
+ call.append('-f')
+ if self._proto_path:
+ call.append(self._proto_path)
+ for key, value in self._key_value_params.items():
+ call.append('-e')
+ call.append(key)
+ call.append(shlex.quote(value))
+ return call
+
+
+class InstrumentationTestCommandBuilder(InstrumentationCommandBuilder):
+
+ def __init__(self):
+ super().__init__()
+ self._packages = []
+ self._classes = []
+
+ @staticmethod
+ def default():
+ """Default instrumentation call builder.
+
+ The flags -w, -r are enabled.
+
+ -w Forces am instrument to wait until the instrumentation terminates
+ (needed for logging)
+ -r Outputs results in raw format.
+ https://developer.android.com/studio/test/command-line#AMSyntax
+
+ The default test runner is androidx.test.runner.AndroidJUnitRunner.
+ """
+ builder = InstrumentationTestCommandBuilder()
+ builder.add_flag('-w')
+ builder.add_flag('-r')
+ builder.set_runner('androidx.test.runner.AndroidJUnitRunner')
+ return builder
+
+ CONFLICTING_PARAMS_MESSAGE = ('only a list of classes and test methods or '
+ 'a list of test packages are allowed.')
+
+ def add_test_package(self, package):
+ if len(self._classes) != 0:
+ raise Exception(self.CONFLICTING_PARAMS_MESSAGE)
+ self._packages.append(package)
+
+ def add_test_method(self, class_name, test_method):
+ if len(self._packages) != 0:
+ raise Exception(self.CONFLICTING_PARAMS_MESSAGE)
+ self._classes.append('{}#{}'.format(class_name, test_method))
+
+ def add_test_class(self, class_name):
+ if len(self._packages) != 0:
+ raise Exception(self.CONFLICTING_PARAMS_MESSAGE)
+ self._classes.append(class_name)
+
+ def build(self):
+ errors = []
+ if len(self._packages) == 0 and len(self._classes) == 0:
+ errors.append('at least one of package, class or test method need '
+ 'to be defined')
+
+ if len(errors) > 0:
+ raise Exception('instrumentation call build errors: {}'
+ .format(','.join(errors)))
+
+ call = self._instrument_call_with_arguments()
+
+ if len(self._packages) > 0:
+ call.append('-e')
+ call.append('package')
+ call.append(','.join(self._packages))
+ elif len(self._classes) > 0:
+ call.append('-e')
+ call.append('class')
+ call.append(','.join(self._classes))
+
+ call.append('{}/{}'.format(self._manifest_package_name, self._runner))
+ if self._nohup:
+ call = ['nohup'] + call
+ call.append('>>')
+ call.append(self._nohup_log_path)
+ call.append('2>&1')
+ return ' '.join(call)
diff --git a/acts_tests/acts_contrib/test_utils/net/NetstackBaseTest.py b/acts_tests/acts_contrib/test_utils/net/NetstackBaseTest.py
new file mode 100755
index 0000000..a59a2e0
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/NetstackBaseTest.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from acts.base_test import BaseTestClass
+from acts import asserts
+
+
+class NetstackBaseTest(BaseTestClass):
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
diff --git a/acts_tests/acts_contrib/test_utils/net/__init__.py b/acts_tests/acts_contrib/test_utils/net/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py b/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py
new file mode 100644
index 0000000..595512c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+import pprint
+
+from enum import IntEnum
+from queue import Empty
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.controllers import attenuator
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+
+ARDUINO = "/root/arduino/arduino-1.8.5/arduino "
+CONNECT_WIFI = "/arduino/connect_wifi/connect_wifi.ino"
+DISCONNECT_WIFI = "/arduino/disconnect_wifi/disconnect_wifi.ino"
+SSID = wutils.WifiEnums.SSID_KEY
+PWD = wutils.WifiEnums.PWD_KEY
+
+def connect_wifi(wd, network=None):
+ """Connect wifi on arduino wifi dongle
+
+ Args:
+ wd - wifi dongle object
+ network - wifi network to connect to
+ """
+ wd.log.info("Flashing connect_wifi.ino onto dongle")
+ cmd = "locate %s" % CONNECT_WIFI
+ file_path = utils.exe_cmd(cmd).decode("utf-8", "ignore").split()[-1]
+ write_status = wd.write(ARDUINO, file_path, network)
+ asserts.assert_true(write_status, "Failed to flash connect wifi")
+ wd.log.info("Flashing complete")
+ wifi_status = wd.wifi_status()
+ asserts.assert_true(wifi_status, "Failed to connect to %s" % network)
+ ping_status = wd.ping_status()
+ asserts.assert_true(ping_status, "Failed to connect to internet")
+
+def disconnect_wifi(wd):
+ """Disconnect wifi on arduino wifi dongle
+
+ Args:
+ wd - wifi dongle object
+
+ Returns:
+ True - if wifi is disconnected
+ False - if not
+ """
+ wd.log.info("Flashing disconnect_wifi.ino onto dongle")
+ cmd = "locate %s" % DISCONNECT_WIFI
+ file_path = utils.exe_cmd(cmd).decode("utf-8", "ignore").rstrip()
+ write_status = wd.write(ARDUINO, file_path)
+ asserts.assert_true(write_status, "Failed to flash disconnect wifi")
+ wd.log.info("Flashing complete")
+ wifi_status = wd.wifi_status(False)
+ asserts.assert_true(not wifi_status, "Failed to disconnect wifi")
diff --git a/acts_tests/acts_contrib/test_utils/net/connectivity_const.py b/acts_tests/acts_contrib/test_utils/net/connectivity_const.py
new file mode 100644
index 0000000..60d4f67
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/connectivity_const.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import enum
+
+######################################################
+# ConnectivityManager.NetworkCallback events
+######################################################
+EVENT_NETWORK_CALLBACK = "NetworkCallback"
+
+# event types
+NETWORK_CB_PRE_CHECK = "PreCheck"
+NETWORK_CB_AVAILABLE = "Available"
+NETWORK_CB_LOSING = "Losing"
+NETWORK_CB_LOST = "Lost"
+NETWORK_CB_UNAVAILABLE = "Unavailable"
+NETWORK_CB_CAPABILITIES_CHANGED = "CapabilitiesChanged"
+NETWORK_CB_SUSPENDED = "Suspended"
+NETWORK_CB_RESUMED = "Resumed"
+NETWORK_CB_LINK_PROPERTIES_CHANGED = "LinkPropertiesChanged"
+NETWORK_CB_INVALID = "Invalid"
+
+# event data keys
+NETWORK_CB_KEY_ID = "id"
+NETWORK_CB_KEY_EVENT = "networkCallbackEvent"
+NETWORK_CB_KEY_MAX_MS_TO_LIVE = "maxMsToLive"
+NETWORK_CB_KEY_RSSI = "rssi"
+NETWORK_CB_KEY_INTERFACE_NAME = "interfaceName"
+NETWORK_CB_KEY_CREATE_TS = "creation_timestamp"
+NETWORK_CB_KEY_CURRENT_TS = "current_timestamp"
+NETWORK_CB_KEY_NETWORK_SPECIFIER = "network_specifier"
+
+# Constants for VPN connection status
+VPN_STATE_DISCONNECTED = 0
+VPN_STATE_INITIALIZING = 1
+VPN_STATE_CONNECTING = 2
+VPN_STATE_CONNECTED = 3
+VPN_STATE_TIMEOUT = 4
+VPN_STATE_FAILED = 5
+# TODO gmoturu: determine the exact timeout value
+# This is a random value as of now
+VPN_TIMEOUT = 30
+
+# Connectiivty Manager constants
+TYPE_MOBILE = 0
+TYPE_WIFI = 1
+
+# Multipath preference constants
+MULTIPATH_PREFERENCE_NONE = 0
+MULTIPATH_PREFERENCE_HANDOVER = 1 << 0
+MULTIPATH_PREFERENCE_RELIABILITY = 1 << 1
+MULTIPATH_PREFERENCE_PERFORMANCE = 1 << 2
+
+# Private DNS constants
+DNS_GOOGLE = "dns.google"
+DNS_QUAD9 = "dns.quad9.net"
+DNS_CLOUDFLARE = "1dot1dot1dot1.cloudflare-dns.com"
+PRIVATE_DNS_MODE_OFF = "off"
+PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"
+PRIVATE_DNS_MODE_STRICT = "hostname"
+
+# IpSec constants
+SOCK_STREAM = 1
+SOCK_DGRAM = 2
+AF_INET = 2
+AF_INET6 = 10
+DIRECTION_IN = 0
+DIRECTION_OUT = 1
+MODE_TRANSPORT = 0
+MODE_TUNNEL = 1
+CRYPT_NULL = "ecb(cipher_null)"
+CRYPT_AES_CBC = "cbc(aes)"
+AUTH_HMAC_MD5 = "hmac(md5)"
+AUTH_HMAC_SHA1 = "hmac(sha1)"
+AUTH_HMAC_SHA256 = "hmac(sha256)"
+AUTH_HMAC_SHA384 = "hmac(sha384)"
+AUTH_HMAC_SHA512 = "hmac(sha512)"
+AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"
+
+
+# Constants for VpnProfile
+class VpnProfile(object):
+ """ This class contains all the possible
+ parameters required for VPN connection
+ """
+ NAME = "name"
+ TYPE = "type"
+ SERVER = "server"
+ USER = "username"
+ PWD = "password"
+ DNS = "dnsServers"
+ SEARCH_DOMAINS = "searchDomains"
+ ROUTES = "routes"
+ MPPE = "mppe"
+ L2TP_SECRET = "l2tpSecret"
+ IPSEC_ID = "ipsecIdentifier"
+ IPSEC_SECRET = "ipsecSecret"
+ IPSEC_USER_CERT = "ipsecUserCert"
+ IPSEC_CA_CERT = "ipsecCaCert"
+ IPSEC_SERVER_CERT = "ipsecServerCert"
+
+
+# Enums for VPN profile types
+class VpnProfileType(enum.Enum):
+ """ Integer constant for each type of VPN
+ """
+ PPTP = 0
+ L2TP_IPSEC_PSK = 1
+ L2TP_IPSEC_RSA = 2
+ IPSEC_XAUTH_PSK = 3
+ IPSEC_XAUTH_RSA = 4
+ IPSEC_HYBRID_RSA = 5
+ IKEV2_IPSEC_USER_PASS = 6
+ IKEV2_IPSEC_PSK = 7
+ IKEV2_IPSEC_RSA = 8
+
+
+# Constants for config file
+class VpnReqParams(object):
+ """ Config file parameters required for
+ VPN connection
+ """
+ vpn_server_addresses = "vpn_server_addresses"
+ vpn_verify_addresses = "vpn_verify_addresses"
+ vpn_username = "vpn_username"
+ vpn_password = "vpn_password"
+ psk_secret = "psk_secret"
+ client_pkcs_file_name = "client_pkcs_file_name"
+ cert_path_vpnserver = "cert_path_vpnserver"
+ cert_password = "cert_password"
+ pptp_mppe = "pptp_mppe"
+ ipsec_server_type = "ipsec_server_type"
+ wifi_network = "wifi_network"
+ vpn_identity = "vpn_identity"
+ vpn_server_hostname = "vpn_server_hostname"
diff --git a/acts_tests/acts_contrib/test_utils/net/connectivity_test_utils.py b/acts_tests/acts_contrib/test_utils/net/connectivity_test_utils.py
new file mode 100644
index 0000000..d35fe04
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/connectivity_test_utils.py
@@ -0,0 +1,119 @@
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts import asserts
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from queue import Empty
+
+def _listen_for_keepalive_event(ad, key, msg, ka_event):
+ """Listen for keepalive event and return status
+
+ Args:
+ ad: DUT object
+ key: keepalive key
+ msg: Error message
+ event: Keepalive event type
+ """
+ ad.droid.socketKeepaliveStartListeningForEvent(key, ka_event)
+ try:
+ event = ad.ed.pop_event("SocketKeepaliveCallback")
+ status = event["data"]["socketKeepaliveEvent"] == ka_event
+ except Empty:
+ asserts.fail(msg)
+ finally:
+ ad.droid.socketKeepaliveStopListeningForEvent(key, ka_event)
+ if ka_event != "Started":
+ ad.droid.removeSocketKeepaliveReceiverKey(key)
+ if status:
+ ad.log.info("'%s' keepalive event successful" % ka_event)
+ return status
+
+def start_natt_socket_keepalive(ad, udp_encap, src_ip, dst_ip, interval = 10):
+ """Start NATT SocketKeepalive on DUT
+
+ Args:
+ ad: DUT object
+ udp_encap: udp_encap socket key
+ src_ip: IP addr of the client
+ dst_ip: IP addr of the keepalive server
+ interval: keepalive time interval
+ """
+ ad.log.info("Starting Natt Socket Keepalive")
+ key = ad.droid.startNattSocketKeepalive(udp_encap, src_ip, dst_ip, interval)
+ msg = "Failed to receive confirmation of starting natt socket keeaplive"
+ status = _listen_for_keepalive_event(ad, key, msg, "Started")
+ return key if status else None
+
+def start_tcp_socket_keepalive(ad, socket, time_interval = 10):
+ """Start TCP socket keepalive on DUT
+
+ Args:
+ ad: DUT object
+ socket: TCP socket key
+ time_interval: Keepalive time interval
+ """
+ ad.log.info("Starting TCP Socket Keepalive")
+ key = ad.droid.startTcpSocketKeepalive(socket, time_interval)
+ msg = "Failed to receive confirmation of starting tcp socket keeaplive"
+ status = _listen_for_keepalive_event(ad, key, msg, "Started")
+ return key if status else None
+
+def socket_keepalive_error(ad, key):
+ """Verify Error callback
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Verify Error callback on keepalive: %s" % key)
+ msg = "Failed to receive confirmation of Error callback"
+ return _listen_for_keepalive_event(ad, key, msg, "Error")
+
+def socket_keepalive_data_received(ad, key):
+ """Verify OnDataReceived callback
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Verify OnDataReceived callback on keepalive: %s" % key)
+ msg = "Failed to receive confirmation of OnDataReceived callback"
+ return _listen_for_keepalive_event(ad, key, msg, "OnDataReceived")
+
+def stop_socket_keepalive(ad, key):
+ """Stop SocketKeepalive on DUT
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Stopping Socket keepalive: %s" % key)
+ ad.droid.stopSocketKeepalive(key)
+ msg = "Failed to receive confirmation of stopping socket keepalive"
+ return _listen_for_keepalive_event(ad, key, msg, "Stopped")
+
+def set_private_dns(ad, dns_mode, hostname=None):
+ """ Set private DNS mode on dut """
+ if dns_mode == cconst.PRIVATE_DNS_MODE_OFF:
+ ad.droid.setPrivateDnsMode(False)
+ else:
+ ad.droid.setPrivateDnsMode(True, hostname)
+
+ mode = ad.droid.getPrivateDnsMode()
+ host = ad.droid.getPrivateDnsSpecifier()
+ ad.log.info("DNS mode is %s and DNS server is %s" % (mode, host))
+ asserts.assert_true(dns_mode == mode and host == hostname,
+ "Failed to set DNS mode to %s and DNS to %s" % \
+ (dns_mode, hostname))
diff --git a/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py b/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py
new file mode 100644
index 0000000..5f383ee
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py
@@ -0,0 +1,249 @@
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import binascii
+import os
+import random
+import re
+import threading
+import time
+
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts import asserts
+
+PKTS = 5
+
+def make_key(len_bits):
+ asserts.assert_true(
+ len_bits % 8 == 0, "Unexpected key length. Should be a multiple "
+ "of 8, got %s" % len_bits)
+ return binascii.hexlify(os.urandom(int(len_bits/8))).decode()
+
+def allocate_spis(ad, ip_a, ip_b, in_spi = None, out_spi = None):
+ """ Allocate in and out SPIs for android device
+
+ Args:
+ 1. ad : android device object
+ 2. ip_a : local IP address for In SPI
+ 3. ip_b : remote IP address for Out SPI
+ 4. in_spi : Generate In SPI with this value
+ 5. out_spi : Generate Out SPI with this value
+
+ Returns:
+ List of In and Out SPI
+ """
+ in_spi_key = ad.droid.ipSecAllocateSecurityParameterIndex(ip_a, in_spi)
+ in_spi = ad.droid.ipSecGetSecurityParameterIndex(in_spi_key)
+ ad.log.info("In SPI: %s" % hex(in_spi))
+
+ out_spi_key = ad.droid.ipSecAllocateSecurityParameterIndex(ip_b, out_spi)
+ out_spi = ad.droid.ipSecGetSecurityParameterIndex(out_spi_key)
+ ad.log.info("Out SPI: %s" % hex(out_spi))
+
+ asserts.assert_true(in_spi and out_spi, "Failed to allocate SPIs")
+ return [in_spi_key, out_spi_key]
+
+def release_spis(ad, spis):
+ """ Destroy SPIs
+
+ Args:
+ 1. ad : android device object
+ 2. spis : list of SPI keys to destroy
+ """
+ for spi_key in spis:
+ ad.droid.ipSecReleaseSecurityParameterIndex(spi_key)
+ spi = ad.droid.ipSecGetSecurityParameterIndex(spi_key)
+ asserts.assert_true(not spi, "Failed to release SPI")
+
+def create_transport_mode_transforms(ad,
+ spis,
+ ip_a,
+ ip_b,
+ crypt_algo,
+ crypt_key,
+ auth_algo,
+ auth_key,
+ trunc_bit,
+ udp_encap_sock=None):
+ """ Create transport mode transforms on the device
+
+ Args:
+ 1. ad : android device object
+ 2. spis : spi keys of the SPIs created
+ 3. ip_a : local IP addr
+ 4. ip_b : remote IP addr
+ 5. crypt_key : encryption key
+ 6. auth_key : authentication key
+ 7. udp_encap_sock : set udp encapsulation for ESP packets
+
+ Returns:
+ List of In and Out Transforms
+ """
+ in_transform = ad.droid.ipSecCreateTransportModeTransform(
+ crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[0],
+ ip_b, udp_encap_sock)
+ ad.log.info("In Transform: %s" % in_transform)
+ out_transform = ad.droid.ipSecCreateTransportModeTransform(
+ crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[1],
+ ip_a, udp_encap_sock)
+ ad.log.info("Out Transform: %s" % out_transform)
+ asserts.assert_true(in_transform and out_transform,
+ "Failed to create transforms")
+ return [in_transform, out_transform]
+
+def destroy_transport_mode_transforms(ad, transforms):
+ """ Destroy transforms on the device
+
+ Args:
+ 1. ad : android device object
+ 2. transforms : list to transform keys to destroy
+ """
+ for transform in transforms:
+ ad.droid.ipSecDestroyTransportModeTransform(transform)
+ status = ad.droid.ipSecGetTransformStatus(transform)
+ ad.log.info("Transform status: %s" % status)
+ asserts.assert_true(not status, "Failed to destroy transform")
+
+def apply_transport_mode_transforms_file_descriptors(ad, fd, transforms):
+ """ Apply transpot mode transform to FileDescriptor object
+
+ Args:
+ 1. ad - android device object
+ 2. fd - FileDescriptor key
+ 3. transforms - list of in and out transforms
+ """
+ in_transform = ad.droid.ipSecApplyTransportModeTransformFileDescriptor(
+ fd, cconst.DIRECTION_IN, transforms[0])
+ out_transform = ad.droid.ipSecApplyTransportModeTransformFileDescriptor(
+ fd, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_transform and out_transform,
+ "Failed to apply transform")
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+ ip_xfrm_policy = ad.adb.shell("ip -s xfrm policy")
+ ad.log.info("XFRM POLICY:\n%s\n" % ip_xfrm_policy)
+
+def remove_transport_mode_transforms_file_descriptors(ad, fd):
+ """ Remove transport mode transform from FileDescriptor object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - FileDescriptor key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsFileDescriptor(fd)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def apply_transport_mode_transforms_datagram_socket(ad, socket, transforms):
+ """ Apply transport mode transform to DatagramSocket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - DatagramSocket object key
+ 3. transforms - list of in and out transforms
+ """
+ in_tfrm_status = ad.droid.ipSecApplyTransportModeTransformDatagramSocket(
+ socket, cconst.DIRECTION_IN, transforms[0])
+ out_tfrm_status = ad.droid.ipSecApplyTransportModeTransformDatagramSocket(
+ socket, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_tfrm_status and out_tfrm_status,
+ "Failed to apply transform")
+
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+
+def remove_transport_mode_transforms_datagram_socket(ad, socket):
+ """ Remove transport mode transform from DatagramSocket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - DatagramSocket object key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsDatagramSocket(socket)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def apply_transport_mode_transforms_socket(ad, socket, transforms):
+ """ Apply transport mode transform to Socket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - Socket object key
+ 3. transforms - list of in and out transforms
+ """
+ in_tfrm_status = ad.droid.ipSecApplyTransportModeTransformSocket(
+ socket, cconst.DIRECTION_IN, transforms[0])
+ out_tfrm_status = ad.droid.ipSecApplyTransportModeTransformSocket(
+ socket, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_tfrm_status and out_tfrm_status,
+ "Failed to apply transform")
+
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+
+def remove_transport_mode_transforms_socket(ad, socket):
+ """ Remove transport mode transform from Socket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - Socket object key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsSocket(socket)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def verify_esp_packets(ads):
+ """ Verify that encrypted ESP packets are sent
+
+ Args:
+ 1. ads - Verify ESP packets on all devices
+ """
+ for ad in ads:
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE on %s:\n%s\n" % (ad.serial, ip_xfrm_state))
+ pattern = re.findall(r'\d+\(packets\)', ip_xfrm_state)
+ esp_pkts = False
+ for _ in pattern:
+ if int(_.split('(')[0]) >= PKTS:
+ esp_pkts = True
+ break
+ asserts.assert_true(esp_pkts, "Could not find ESP pkts")
+
+def generate_random_crypt_auth_combo():
+ """ Generate every possible combination of crypt and auth keys,
+ auth algo, trunc bits supported by IpSecManager
+ """
+ crypt_key_length = [128, 192, 256]
+ auth_method_key = { cconst.AUTH_HMAC_MD5 : 128,
+ cconst.AUTH_HMAC_SHA1 : 160,
+ cconst.AUTH_HMAC_SHA256 : 256,
+ cconst.AUTH_HMAC_SHA384 : 384,
+ cconst.AUTH_HMAC_SHA512 : 512 }
+ auth_method_trunc = { cconst.AUTH_HMAC_MD5 : list(range(96, 136, 8)),
+ cconst.AUTH_HMAC_SHA1 : list(range(96, 168, 8)),
+ cconst.AUTH_HMAC_SHA256 : list(range(96, 264, 8)),
+ cconst.AUTH_HMAC_SHA384 : list(range(192, 392, 8)),
+ cconst.AUTH_HMAC_SHA512 : list(range(256, 520, 8)) }
+ return_list = []
+ for c in crypt_key_length:
+ for k in auth_method_key.keys():
+ auth_key = auth_method_key[k]
+ lst = auth_method_trunc[k]
+ for t in lst:
+ combo = []
+ combo.append(c)
+ combo.append(k)
+ combo.append(auth_key)
+ combo.append(t)
+ return_list.append(combo)
+
+ return return_list
diff --git a/acts_tests/acts_contrib/test_utils/net/net_test_utils.py b/acts_tests/acts_contrib/test_utils/net/net_test_utils.py
new file mode 100644
index 0000000..3d884ca
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/net_test_utils.py
@@ -0,0 +1,512 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import os
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.controllers import adb
+from acts.controllers.adb_lib.error import AdbError
+from acts.logger import epoch_to_log_line_timestamp
+from acts.logger import normalize_log_line_timestamp
+from acts.utils import get_current_epoch_time
+from acts.utils import start_standing_subprocess
+from acts.utils import stop_standing_subprocess
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from scapy.all import get_if_list
+
+import os
+import re
+import time
+import urllib.request
+
+VPN_CONST = cconst.VpnProfile
+VPN_TYPE = cconst.VpnProfileType
+VPN_PARAMS = cconst.VpnReqParams
+TCPDUMP_PATH = "/data/local/tmp/"
+USB_CHARGE_MODE = "svc usb setFunctions"
+USB_TETHERING_MODE = "svc usb setFunctions rndis"
+DEVICE_IP_ADDRESS = "ip address"
+
+GCE_SSH = "gcloud compute ssh "
+GCE_SCP = "gcloud compute scp "
+
+
+def verify_lte_data_and_tethering_supported(ad):
+ """Verify if LTE data is enabled and tethering supported"""
+ wutils.wifi_toggle_state(ad, False)
+ ad.droid.telephonyToggleDataConnection(True)
+ wait_for_cell_data_connection(ad.log, ad, True)
+ asserts.assert_true(
+ verify_http_connection(ad.log, ad),
+ "HTTP verification failed on cell data connection")
+ asserts.assert_true(
+ ad.droid.connectivityIsTetheringSupported(),
+ "Tethering is not supported for the provider")
+ wutils.wifi_toggle_state(ad, True)
+
+
+def set_chrome_browser_permissions(ad):
+ """Set chrome browser start with no-first-run verification.
+ Give permission to read from and write to storage
+ """
+ commands = ["pm grant com.android.chrome "
+ "android.permission.READ_EXTERNAL_STORAGE",
+ "pm grant com.android.chrome "
+ "android.permission.WRITE_EXTERNAL_STORAGE",
+ "rm /data/local/chrome-command-line",
+ "am set-debug-app --persistent com.android.chrome",
+ 'echo "chrome --no-default-browser-check --no-first-run '
+ '--disable-fre" > /data/local/tmp/chrome-command-line']
+ for cmd in commands:
+ try:
+ ad.adb.shell(cmd)
+ except AdbError:
+ logging.warning("adb command %s failed on %s" % (cmd, ad.serial))
+
+
+def verify_ping_to_vpn_ip(ad, vpn_ping_addr):
+ """ Verify if IP behind VPN server is pingable.
+ Ping should pass, if VPN is connected.
+ Ping should fail, if VPN is disconnected.
+
+ Args:
+ ad: android device object
+ """
+ ping_result = None
+ pkt_loss = "100% packet loss"
+ logging.info("Pinging: %s" % vpn_ping_addr)
+ try:
+ ping_result = ad.adb.shell("ping -c 3 -W 2 %s" % vpn_ping_addr)
+ except AdbError:
+ pass
+ return ping_result and pkt_loss not in ping_result
+
+
+def legacy_vpn_connection_test_logic(ad, vpn_profile, vpn_ping_addr):
+ """ Test logic for each legacy VPN connection
+
+ Steps:
+ 1. Generate profile for the VPN type
+ 2. Establish connection to the server
+ 3. Verify that connection is established using LegacyVpnInfo
+ 4. Verify the connection by pinging the IP behind VPN
+ 5. Stop the VPN connection
+ 6. Check the connection status
+ 7. Verify that ping to IP behind VPN fails
+
+ Args:
+ 1. ad: Android device object
+ 2. VpnProfileType (1 of the 6 types supported by Android)
+ """
+ # Wait for sometime so that VPN server flushes all interfaces and
+ # connections after graceful termination
+ time.sleep(10)
+
+ ad.adb.shell("ip xfrm state flush")
+ ad.log.info("Connecting to: %s", vpn_profile)
+ ad.droid.vpnStartLegacyVpn(vpn_profile)
+ time.sleep(cconst.VPN_TIMEOUT)
+
+ connected_vpn_info = ad.droid.vpnGetLegacyVpnInfo()
+ asserts.assert_equal(connected_vpn_info["state"],
+ cconst.VPN_STATE_CONNECTED,
+ "Unable to establish VPN connection for %s"
+ % vpn_profile)
+
+ ping_result = verify_ping_to_vpn_ip(ad, vpn_ping_addr)
+ ip_xfrm_state = ad.adb.shell("ip xfrm state")
+ match_obj = re.search(r'hmac(.*)', "%s" % ip_xfrm_state)
+ if match_obj:
+ ip_xfrm_state = format(match_obj.group(0)).split()
+ ad.log.info("HMAC for ESP is %s " % ip_xfrm_state[0])
+
+ ad.droid.vpnStopLegacyVpn()
+ asserts.assert_true(ping_result,
+ "Ping to the internal IP failed. "
+ "Expected to pass as VPN is connected")
+
+ connected_vpn_info = ad.droid.vpnGetLegacyVpnInfo()
+ asserts.assert_true(not connected_vpn_info,
+ "Unable to terminate VPN connection for %s"
+ % vpn_profile)
+
+
+def download_load_certs(ad, vpn_params, vpn_type, vpn_server_addr,
+ ipsec_server_type, log_path):
+ """ Download the certificates from VPN server and push to sdcard of DUT
+
+ Args:
+ ad: android device object
+ vpn_params: vpn params from config file
+ vpn_type: 1 of the 6 VPN types
+ vpn_server_addr: server addr to connect to
+ ipsec_server_type: ipsec version - strongswan or openswan
+ log_path: log path to download cert
+
+ Returns:
+ Client cert file name on DUT's sdcard
+ """
+ url = "http://%s%s%s" % (vpn_server_addr,
+ vpn_params['cert_path_vpnserver'],
+ vpn_params['client_pkcs_file_name'])
+ local_cert_name = "%s_%s_%s" % (vpn_type.name,
+ ipsec_server_type,
+ vpn_params['client_pkcs_file_name'])
+
+ local_file_path = os.path.join(log_path, local_cert_name)
+ logging.info("URL is: %s" % url)
+ try:
+ ret = urllib.request.urlopen(url)
+ with open(local_file_path, "wb") as f:
+ f.write(ret.read())
+ except Exception:
+ asserts.fail("Unable to download certificate from the server")
+
+ ad.adb.push("%s sdcard/" % local_file_path)
+ return local_cert_name
+
+
+def generate_legacy_vpn_profile(ad,
+ vpn_params,
+ vpn_type,
+ vpn_server_addr,
+ ipsec_server_type,
+ log_path):
+ """ Generate legacy VPN profile for a VPN
+
+ Args:
+ ad: android device object
+ vpn_params: vpn params from config file
+ vpn_type: 1 of the 6 VPN types
+ vpn_server_addr: server addr to connect to
+ ipsec_server_type: ipsec version - strongswan or openswan
+ log_path: log path to download cert
+
+ Returns:
+ Vpn profile
+ """
+ vpn_profile = {VPN_CONST.USER: vpn_params['vpn_username'],
+ VPN_CONST.PWD: vpn_params['vpn_password'],
+ VPN_CONST.TYPE: vpn_type.value,
+ VPN_CONST.SERVER: vpn_server_addr, }
+ vpn_profile[VPN_CONST.NAME] = "test_%s_%s" % (vpn_type.name,
+ ipsec_server_type)
+ if vpn_type.name == "PPTP":
+ vpn_profile[VPN_CONST.NAME] = "test_%s" % vpn_type.name
+
+ psk_set = set(["L2TP_IPSEC_PSK", "IPSEC_XAUTH_PSK"])
+ rsa_set = set(["L2TP_IPSEC_RSA", "IPSEC_XAUTH_RSA", "IPSEC_HYBRID_RSA"])
+
+ if vpn_type.name in psk_set:
+ vpn_profile[VPN_CONST.IPSEC_SECRET] = vpn_params['psk_secret']
+ elif vpn_type.name in rsa_set:
+ cert_name = download_load_certs(ad,
+ vpn_params,
+ vpn_type,
+ vpn_server_addr,
+ ipsec_server_type,
+ log_path)
+ vpn_profile[VPN_CONST.IPSEC_USER_CERT] = cert_name.split('.')[0]
+ vpn_profile[VPN_CONST.IPSEC_CA_CERT] = cert_name.split('.')[0]
+ ad.droid.installCertificate(vpn_profile, cert_name,
+ vpn_params['cert_password'])
+ else:
+ vpn_profile[VPN_CONST.MPPE] = "mppe"
+
+ return vpn_profile
+
+def generate_ikev2_vpn_profile(ad, vpn_params, vpn_type, server_addr, log_path):
+ """Generate VPN profile for IKEv2 VPN.
+
+ Args:
+ ad: android device object.
+ vpn_params: vpn params from config file.
+ vpn_type: ikev2 vpn type.
+ server_addr: vpn server addr.
+ log_path: log path to download cert.
+
+ Returns:
+ Vpn profile.
+ """
+ vpn_profile = {
+ VPN_CONST.TYPE: vpn_type.value,
+ VPN_CONST.SERVER: server_addr,
+ }
+
+ if vpn_type.name == "IKEV2_IPSEC_USER_PASS":
+ vpn_profile[VPN_CONST.USER] = vpn_params["vpn_username"]
+ vpn_profile[VPN_CONST.PWD] = vpn_params["vpn_password"]
+ vpn_profile[VPN_CONST.IPSEC_ID] = vpn_params["vpn_identity"]
+ cert_name = download_load_certs(
+ ad, vpn_params, vpn_type, vpn_params["server_addr"],
+ "IKEV2_IPSEC_USER_PASS", log_path)
+ vpn_profile[VPN_CONST.IPSEC_CA_CERT] = cert_name.split('.')[0]
+ ad.droid.installCertificate(
+ vpn_profile, cert_name, vpn_params['cert_password'])
+ elif vpn_type.name == "IKEV2_IPSEC_PSK":
+ vpn_profile[VPN_CONST.IPSEC_ID] = vpn_params["vpn_identity"]
+ vpn_profile[VPN_CONST.IPSEC_SECRET] = vpn_params["psk_secret"]
+ else:
+ vpn_profile[VPN_CONST.IPSEC_ID] = "%s@%s" % (
+ vpn_params["vpn_identity"], server_addr)
+ logging.info("ID: %s@%s" % (vpn_params["vpn_identity"], server_addr))
+ cert_name = download_load_certs(
+ ad, vpn_params, vpn_type, vpn_params["server_addr"],
+ "IKEV2_IPSEC_RSA", log_path)
+ vpn_profile[VPN_CONST.IPSEC_USER_CERT] = cert_name.split('.')[0]
+ vpn_profile[VPN_CONST.IPSEC_CA_CERT] = cert_name.split('.')[0]
+ ad.droid.installCertificate(
+ vpn_profile, cert_name, vpn_params['cert_password'])
+
+ return vpn_profile
+
+def start_tcpdump(ad, test_name):
+ """Start tcpdump on all interfaces
+
+ Args:
+ ad: android device object.
+ test_name: tcpdump file name will have this
+ """
+ ad.log.info("Starting tcpdump on all interfaces")
+ try:
+ ad.adb.shell("killall -9 tcpdump")
+ except AdbError:
+ ad.log.warn("Killing existing tcpdump processes failed")
+ out = ad.adb.shell("ls -l %s" % TCPDUMP_PATH)
+ if "No such file" in out or not out:
+ ad.adb.shell("mkdir %s" % TCPDUMP_PATH)
+ else:
+ ad.adb.shell("rm -rf %s/*" % TCPDUMP_PATH, ignore_status=True)
+
+ begin_time = epoch_to_log_line_timestamp(get_current_epoch_time())
+ begin_time = normalize_log_line_timestamp(begin_time)
+
+ file_name = "%s/tcpdump_%s_%s.pcap" % (TCPDUMP_PATH, ad.serial, test_name)
+ ad.log.info("tcpdump file is %s", file_name)
+ cmd = "adb -s {} shell tcpdump -i any -s0 -w {}".format(ad.serial,
+ file_name)
+ try:
+ return start_standing_subprocess(cmd, 5)
+ except Exception:
+ ad.log.exception('Could not start standing process %s' % repr(cmd))
+
+ return None
+
+def stop_tcpdump(ad,
+ proc,
+ test_name,
+ adb_pull_timeout=adb.DEFAULT_ADB_PULL_TIMEOUT):
+ """Stops tcpdump on any iface
+ Pulls the tcpdump file in the tcpdump dir
+
+ Args:
+ ad: android device object.
+ proc: need to know which pid to stop
+ test_name: test name to save the tcpdump file
+ adb_pull_timeout: timeout for adb_pull
+
+ Returns:
+ log_path of the tcpdump file
+ """
+ ad.log.info("Stopping and pulling tcpdump if any")
+ if proc is None:
+ return None
+ try:
+ stop_standing_subprocess(proc)
+ except Exception as e:
+ ad.log.warning(e)
+ log_path = os.path.join(ad.log_path, test_name)
+ os.makedirs(log_path, exist_ok=True)
+ ad.adb.pull("%s/. %s" % (TCPDUMP_PATH, log_path), timeout=adb_pull_timeout)
+ ad.adb.shell("rm -rf %s/*" % TCPDUMP_PATH, ignore_status=True)
+ file_name = "tcpdump_%s_%s.pcap" % (ad.serial, test_name)
+ return "%s/%s" % (log_path, file_name)
+
+def start_tcpdump_gce_server(ad, test_name, dest_port, gce):
+ """ Start tcpdump on gce server
+
+ Args:
+ ad: android device object
+ test_name: test case name
+ dest_port: port to collect tcpdump
+ gce: dictionary of gce instance
+
+ Returns:
+ process id and pcap file path from gce server
+ """
+ ad.log.info("Starting tcpdump on gce server")
+
+ # pcap file name
+ fname = "/tmp/%s_%s_%s_%s" % \
+ (test_name, ad.model, ad.serial,
+ time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime(time.time())))
+
+ # start tcpdump
+ tcpdump_cmd = "sudo bash -c \'tcpdump -i %s -w %s.pcap port %s > \
+ %s.txt 2>&1 & echo $!\'" % (gce["interface"], fname, dest_port, fname)
+ gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \
+ (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd)
+ utils.exe_cmd(gce_ssh_cmd)
+
+ # get process id
+ ps_cmd = '%s "ps aux | grep tcpdump | grep %s"' % (gcloud_ssh_cmd, fname)
+ tcpdump_pid = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore").split()
+ if not tcpdump_pid:
+ raise signals.TestFailure("Failed to start tcpdump on gce server")
+ return tcpdump_pid[1], fname
+
+def stop_tcpdump_gce_server(ad, tcpdump_pid, fname, gce):
+ """ Stop and pull tcpdump file from gce server
+
+ Args:
+ ad: android device object
+ tcpdump_pid: process id for tcpdump file
+ fname: tcpdump file path
+ gce: dictionary of gce instance
+
+ Returns:
+ pcap file from gce server
+ """
+ ad.log.info("Stop and pull pcap file from gce server")
+
+ # stop tcpdump
+ tcpdump_cmd = "sudo kill %s" % tcpdump_pid
+ gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \
+ (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd)
+ utils.exe_cmd(gce_ssh_cmd)
+
+ # verify tcpdump is stopped
+ ps_cmd = '%s "ps aux | grep tcpdump"' % gcloud_ssh_cmd
+ res = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore")
+ if tcpdump_pid in res.split():
+ raise signals.TestFailure("Failed to stop tcpdump on gce server")
+ if not fname:
+ return None
+
+ # pull pcap file
+ gcloud_scp_cmd = "%s --project=%s --zone=%s %s@%s:" % \
+ (GCE_SCP, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ pull_file = '%s%s.pcap %s/' % (gcloud_scp_cmd, fname, ad.device_log_path)
+ utils.exe_cmd(pull_file)
+ if not os.path.exists(
+ "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1])):
+ raise signals.TestFailure("Failed to pull tcpdump from gce server")
+
+ # delete pcaps
+ utils.exe_cmd('%s "sudo rm %s.*"' % (gcloud_ssh_cmd, fname))
+
+ # return pcap file
+ pcap_file = "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1])
+ return pcap_file
+
+def is_ipaddress_ipv6(ip_address):
+ """Verify if the given string is a valid IPv6 address.
+
+ Args:
+ ip_address: string containing the IP address
+
+ Returns:
+ True: if valid ipv6 address
+ False: if not
+ """
+ try:
+ socket.inet_pton(socket.AF_INET6, ip_address)
+ return True
+ except socket.error:
+ return False
+
+def carrier_supports_ipv6(dut):
+ """Verify if carrier supports ipv6.
+
+ Args:
+ dut: Android device that is being checked
+
+ Returns:
+ True: if carrier supports ipv6
+ False: if not
+ """
+
+ carrier_supports_ipv6 = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"]
+ operator = get_operator_name("log", dut)
+ return operator in carrier_supports_ipv6
+
+def supports_ipv6_tethering(self, dut):
+ """ Check if provider supports IPv6 tethering.
+
+ Returns:
+ True: if provider supports IPv6 tethering
+ False: if not
+ """
+ carrier_supports_tethering = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"]
+ operator = get_operator_name(self.log, dut)
+ return operator in carrier_supports_tethering
+
+
+def start_usb_tethering(ad):
+ """Start USB tethering.
+
+ Args:
+ ad: android device object
+ """
+ # TODO: test USB tethering by #startTethering API - b/149116235
+ ad.log.info("Starting USB Tethering")
+ ad.stop_services()
+ ad.adb.shell(USB_TETHERING_MODE, ignore_status=True)
+ ad.adb.wait_for_device()
+ ad.start_services()
+ if "rndis" not in ad.adb.shell(DEVICE_IP_ADDRESS):
+ raise signals.TestFailure("Unable to enable USB tethering.")
+
+
+def stop_usb_tethering(ad):
+ """Stop USB tethering.
+
+ Args:
+ ad: android device object
+ """
+ ad.log.info("Stopping USB Tethering")
+ ad.stop_services()
+ ad.adb.shell(USB_CHARGE_MODE)
+ ad.adb.wait_for_device()
+ ad.start_services()
+
+
+def wait_for_new_iface(old_ifaces):
+ """Wait for the new interface to come up.
+
+ Args:
+ old_ifaces: list of old interfaces
+ """
+ old_set = set(old_ifaces)
+ # Try 10 times to find a new interface with a 1s sleep every time
+ # (equivalent to a 9s timeout)
+ for i in range(0, 10):
+ new_ifaces = set(get_if_list()) - old_set
+ asserts.assert_true(len(new_ifaces) < 2,
+ "Too many new interfaces after turning on "
+ "tethering")
+ if len(new_ifaces) == 1:
+ return new_ifaces.pop()
+ time.sleep(1)
+ asserts.fail("Timeout waiting for tethering interface on host")
diff --git a/acts_tests/acts_contrib/test_utils/net/nsd_const.py b/acts_tests/acts_contrib/test_utils/net/nsd_const.py
new file mode 100644
index 0000000..20b0ae1
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/nsd_const.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+######################################################
+# NsdManager.RegistrationListener events
+######################################################
+REG_LISTENER_EVENT = "NsdRegistrationListener"
+
+# event type - using REG_LISTENER_CALLBACK
+REG_LISTENER_EVENT_ON_REG_FAILED = "OnRegistrationFailed"
+REG_LISTENER_EVENT_ON_SERVICE_REGISTERED = "OnServiceRegistered"
+REG_LISTENER_EVENT_ON_SERVICE_UNREG = "OnServiceUnregistered"
+REG_LISTENER_EVENT_ON_UNREG_FAILED = "OnUnregistrationFailed"
+
+# event data keys
+REG_LISTENER_DATA_ID = "id"
+REG_LISTENER_CALLBACK = "callback"
+REG_LISTENER_ERROR_CODE = "error_code"
+
+######################################################
+# NsdManager.DiscoveryListener events
+######################################################
+DISCOVERY_LISTENER_EVENT = "NsdDiscoveryListener"
+
+# event type - using DISCOVERY_LISTENER_DATA_CALLBACK
+DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STARTED = "OnDiscoveryStarted"
+DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STOPPED = "OnDiscoveryStopped"
+DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND = "OnServiceFound"
+DISCOVERY_LISTENER_EVENT_ON_SERVICE_LOST = "OnServiceLost"
+DISCOVERY_LISTENER_EVENT_ON_START_DISCOVERY_FAILED = "OnStartDiscoveryFailed"
+DISCOVERY_LISTENER_EVENT_ON_STOP_DISCOVERY_FAILED = "OnStopDiscoveryFailed"
+
+# event data keys
+DISCOVERY_LISTENER_DATA_ID = "id"
+DISCOVERY_LISTENER_DATA_CALLBACK = "callback"
+DISCOVERY_LISTENER_DATA_SERVICE_TYPE = "service_type"
+DISCOVERY_LISTENER_DATA_ERROR_CODE = "error_code"
+
+######################################################
+# NsdManager.ResolveListener events
+######################################################
+RESOLVE_LISTENER_EVENT = "NsdResolveListener"
+
+# event type using RESOLVE_LISTENER_DATA_CALLBACK
+RESOLVE_LISTENER_EVENT_ON_RESOLVE_FAIL = "OnResolveFail"
+RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED = "OnServiceResolved"
+
+# event data keys
+RESOLVE_LISTENER_DATA_ID = "id"
+RESOLVE_LISTENER_DATA_CALLBACK = "callback"
+RESOLVE_LISTENER_DATA_ERROR_CODE = "error_code"
+
+######################################################
+# NsdServiceInfo elements
+######################################################
+NSD_SERVICE_INFO_HOST = "serviceInfoHost"
+NSD_SERVICE_INFO_PORT = "serviceInfoPort"
+NSD_SERVICE_INFO_SERVICE_NAME = "serviceInfoServiceName"
+NSD_SERVICE_INFO_SERVICE_TYPE = "serviceInfoServiceType"
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py b/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py
new file mode 100644
index 0000000..b9a6bdf
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py
@@ -0,0 +1,297 @@
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import queue
+import re
+import threading
+import time
+
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts import asserts
+
+MSG = "Test message "
+PKTS = 5
+
+""" Methods for android.system.Os based sockets """
+def open_android_socket(ad, domain, sock_type, ip, port):
+ """ Open TCP or UDP using android.system.Os class
+
+ Args:
+ 1. ad - android device object
+ 2. domain - IPv4 or IPv6 type
+ 3. sock_type - UDP or TCP socket
+ 4. ip - IP addr on the device
+ 5. port - open socket on port
+
+ Returns:
+ File descriptor key
+ """
+ fd_key = ad.droid.openSocket(domain, sock_type, ip, port)
+ ad.log.info("File descriptor: %s" % fd_key)
+ asserts.assert_true(fd_key, "Failed to open socket")
+ return fd_key
+
+def close_android_socket(ad, fd_key):
+ """ Close socket
+
+ Args:
+ 1. ad - android device object
+ 2. fd_key - file descriptor key
+ """
+ status = ad.droid.closeSocket(fd_key)
+ asserts.assert_true(status, "Failed to close socket")
+
+def listen_accept_android_socket(client,
+ server,
+ client_fd,
+ server_fd,
+ server_ip,
+ server_port):
+ """ Listen, accept TCP sockets
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_fd : client's socket handle
+ 4. server_fd : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ server.droid.listenSocket(server_fd)
+ client.droid.connectSocket(client_fd, server_ip, server_port)
+ sock = server.droid.acceptSocket(server_fd)
+ asserts.assert_true(sock, "Failed to accept socket")
+ return sock
+
+def send_recv_data_android_sockets(client,
+ server,
+ client_fd,
+ server_fd,
+ server_ip,
+ server_port):
+ """ Send TCP or UDP data over android os sockets from client to server.
+ Verify that server received the data.
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_fd : client's socket handle
+ 4. server_fd : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverSocket(server_ip, server_port, msg, client_fd)
+ recv_msg = server.droid.recvDataOverSocket(server_fd)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
+
+""" Methods for java.net.DatagramSocket based sockets """
+def open_datagram_socket(ad, ip, port):
+ """ Open datagram socket
+
+ Args:
+ 1. ad : android device object
+ 2. ip : IP addr on the device
+ 3. port : socket port
+
+ Returns:
+ Hash key of the datagram socket
+ """
+ socket_key = ad.droid.openDatagramSocket(ip, port)
+ ad.log.info("Datagram socket: %s" % socket_key)
+ asserts.assert_true(socket_key, "Failed to open datagram socket")
+ return socket_key
+
+def close_datagram_socket(ad, socket_key):
+ """ Close datagram socket
+
+ Args:
+ 1. socket_key : hash key of datagram socket
+ """
+ status = ad.droid.closeDatagramSocket(socket_key)
+ asserts.assert_true(status, "Failed to close datagram socket")
+
+def send_recv_data_datagram_sockets(client,
+ server,
+ client_sock,
+ server_sock,
+ server_ip,
+ server_port):
+ """ Send data over datagram socket from dut_a to dut_b.
+ Verify that dut_b received the data.
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_sock : client's socket handle
+ 4. server_sock : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverDatagramSocket(client_sock,
+ msg,
+ server_ip,
+ server_port)
+ recv_msg = server.droid.recvDataOverDatagramSocket(server_sock)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
+
+""" Utils methods for java.net.Socket based sockets """
+def _accept_socket(server, server_ip, server_port, server_sock, q):
+ sock = server.droid.acceptTcpSocket(server_sock)
+ server.log.info("Server socket: %s" % sock)
+ q.put(sock)
+
+def _client_socket(client, server_ip, server_port, client_ip, client_port, q):
+ time.sleep(0.5)
+ sock = client.droid.openTcpSocket(server_ip,
+ server_port,
+ client_ip,
+ client_port)
+ client.log.info("Client socket: %s" % sock)
+ q.put(sock)
+
+def open_connect_socket(client,
+ server,
+ client_ip,
+ server_ip,
+ client_port,
+ server_port,
+ server_sock):
+ """ Open tcp socket and connect to server
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_ip : client's socket handle
+ 4. server_ip : send data to this IP
+ 5. client_port : port on client socket
+ 6. server_port : port on server socket
+ 7. server_sock : server socket
+
+ Returns:
+ client and server socket from successful connect
+ """
+ sq = queue.Queue()
+ cq = queue.Queue()
+ s = threading.Thread(target = _accept_socket,
+ args = (server, server_ip, server_port, server_sock,
+ sq))
+ c = threading.Thread(target = _client_socket,
+ args = (client, server_ip, server_port, client_ip,
+ client_port, cq))
+ s.start()
+ c.start()
+ c.join()
+ s.join()
+
+ client_sock = cq.get()
+ server_sock = sq.get()
+ asserts.assert_true(client_sock and server_sock, "Failed to open sockets")
+
+ return client_sock, server_sock
+
+def open_server_socket(server, server_ip, server_port):
+ """ Open tcp server socket
+
+ Args:
+ 1. server : ad object for server device
+ 2. server_ip : send data to this IP
+ 3. server_port : send data to this port
+ """
+ sock = server.droid.openTcpServerSocket(server_ip, server_port)
+ server.log.info("Server Socket: %s" % sock)
+ asserts.assert_true(sock, "Failed to open server socket")
+ return sock
+
+def close_socket(ad, socket):
+ """ Close socket
+
+ Args:
+ 1. ad - android device object
+ 2. socket - socket key
+ """
+ status = ad.droid.closeTcpSocket(socket)
+ asserts.assert_true(status, "Failed to socket")
+
+def close_server_socket(ad, socket):
+ """ Close server socket
+
+ Args:
+ 1. ad - android device object
+ 2. socket - server socket key
+ """
+ status = ad.droid.closeTcpServerSocket(socket)
+ asserts.assert_true(status, "Failed to socket")
+
+def shutdown_socket(ad, socket):
+ """ Shutdown socket
+
+ Args:
+ 1. ad - androidandroid device object
+ 2. socket - socket key
+ """
+ fd = ad.droid.getFileDescriptorOfSocket(socket)
+ asserts.assert_true(fd, "Failed to get FileDescriptor key")
+ status = ad.droid.shutdownFileDescriptor(fd)
+ asserts.assert_true(status, "Failed to shutdown socket")
+
+def send_recv_data_sockets(client, server, client_sock, server_sock):
+ """ Send data over TCP socket from client to server.
+ Verify that server received the data
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_sock : client's socket handle
+ 4. server_sock : server's socket handle
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverTcpSocket(client_sock, msg)
+ recv_msg = server.droid.recvDataOverTcpSocket(server_sock)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
diff --git a/acts_tests/acts_contrib/test_utils/net/ui_utils.py b/acts_tests/acts_contrib/test_utils/net/ui_utils.py
new file mode 100644
index 0000000..eb3bc14
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/net/ui_utils.py
@@ -0,0 +1,246 @@
+"""Utils for adb-based UI operations."""
+
+import collections
+import logging
+import os
+import re
+import time
+
+from xml.dom import minidom
+from acts.controllers.android_lib.errors import AndroidDeviceError
+
+
+class Point(collections.namedtuple('Point', ['x', 'y'])):
+
+ def __repr__(self):
+ return '{x},{y}'.format(x=self.x, y=self.y)
+
+
+class Bounds(collections.namedtuple('Bounds', ['start', 'end'])):
+
+ def __repr__(self):
+ return '[{start}][{end}]'.format(start=str(self.start), end=str(self.end))
+
+ def calculate_middle_point(self):
+ return Point((self.start.x + self.end.x) // 2,
+ (self.start.y + self.end.y) // 2)
+
+
+def get_key_value_pair_strings(kv_pairs):
+ return ' '.join(['%s="%s"' % (k, v) for k, v in kv_pairs.items()])
+
+
+def parse_bound(bounds_string):
+ """Parse UI bound string.
+
+ Args:
+ bounds_string: string, In the format of the UI element bound.
+ e.g '[0,0][1080,2160]'
+
+ Returns:
+ Bounds, The bound of UI element.
+ """
+ bounds_pattern = re.compile(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]')
+ points = bounds_pattern.match(bounds_string).groups()
+ points = list(map(int, points))
+ return Bounds(Point(*points[:2]), Point(*points[-2:]))
+
+
+def _find_point_in_bounds(bounds_string):
+ """Finds a point that resides within the given bounds.
+
+ Args:
+ bounds_string: string, In the format of the UI element bound.
+
+ Returns:
+ A tuple of integers, representing X and Y coordinates of a point within
+ the given boundary.
+ """
+ return parse_bound(bounds_string).calculate_middle_point()
+
+
+def get_screen_dump_xml(device):
+ """Gets an XML dump of the current device screen.
+
+ This only works when there is no instrumentation process running. A running
+ instrumentation process will disrupt calls for `adb shell uiautomator dump`.
+
+ Args:
+ device: AndroidDevice object.
+
+ Returns:
+ XML Document of the screen dump.
+ """
+ os.makedirs(device.log_path, exist_ok=True)
+ device.adb.shell('uiautomator dump')
+ device.adb.pull('/sdcard/window_dump.xml %s' % device.log_path)
+ return minidom.parse('%s/window_dump.xml' % device.log_path)
+
+
+def match_node(node, **matcher):
+ """Determine if a mode matches with the given matcher.
+
+ Args:
+ node: Is a XML node to be checked against matcher.
+ **matcher: Is a dict representing mobly AdbUiDevice matchers.
+
+ Returns:
+ True if all matchers match the given node.
+ """
+ match_list = []
+ for k, v in matcher.items():
+ if k == 'class_name':
+ key = k.replace('class_name', 'class')
+ elif k == 'text_contains':
+ key = k.replace('text_contains', 'text')
+ else:
+ key = k.replace('_', '-')
+ try:
+ if k == 'text_contains':
+ match_list.append(v in node.attributes[key].value)
+ else:
+ match_list.append(node.attributes[key].value == v)
+ except KeyError:
+ match_list.append(False)
+ return all(match_list)
+
+
+def _find_node(screen_dump_xml, **kwargs):
+ """Finds an XML node from an XML DOM.
+
+ Args:
+ screen_dump_xml: XML doc, parsed from adb ui automator dump.
+ **kwargs: key/value pairs to match in an XML node's attributes. Value of
+ each key has to be string type. Below lists keys which can be used:
+ index
+ text
+ text_contains (matching a part of text attribute)
+ resource_id
+ class_name (representing "class" attribute)
+ package
+ content_desc
+ checkable
+ checked
+ clickable
+ enabled
+ focusable
+ focused
+ scrollable
+ long_clickable
+ password
+ selected
+
+ Returns:
+ XML node of the UI element or None if not found.
+ """
+ nodes = screen_dump_xml.getElementsByTagName('node')
+ for node in nodes:
+ if match_node(node, **kwargs):
+ logging.debug('Found a node matching conditions: %s',
+ get_key_value_pair_strings(kwargs))
+ return node
+
+
+def wait_and_get_xml_node(device, timeout, child=None, sibling=None, **kwargs):
+ """Waits for a node to appear and return it.
+
+ Args:
+ device: AndroidDevice object.
+ timeout: float, The number of seconds to wait for before giving up.
+ child: dict, a dict contains child XML node's attributes. It is extra set of
+ conditions to match an XML node that is under the XML node which is found
+ by **kwargs.
+ sibling: dict, a dict contains sibling XML node's attributes. It is extra
+ set of conditions to match an XML node that is under parent of the XML
+ node which is found by **kwargs.
+ **kwargs: Key/value pairs to match in an XML node's attributes.
+
+ Returns:
+ The XML node of the UI element.
+
+ Raises:
+ AndroidDeviceError: if the UI element does not appear on screen within
+ timeout or extra sets of conditions of child and sibling are used in a call.
+ """
+ if child and sibling:
+ raise AndroidDeviceError(
+ device, 'Only use one extra set of conditions: child or sibling.')
+ start_time = time.time()
+ threshold = start_time + timeout
+ while time.time() < threshold:
+ time.sleep(1)
+ screen_dump_xml = get_screen_dump_xml(device)
+ node = _find_node(screen_dump_xml, **kwargs)
+ if node and child:
+ node = _find_node(node, **child)
+ if node and sibling:
+ node = _find_node(node.parentNode, **sibling)
+ if node:
+ return node
+ msg = ('Timed out after %ds waiting for UI node matching conditions: %s.'
+ % (timeout, get_key_value_pair_strings(kwargs)))
+ if child:
+ msg = ('%s extra conditions: %s'
+ % (msg, get_key_value_pair_strings(child)))
+ if sibling:
+ msg = ('%s extra conditions: %s'
+ % (msg, get_key_value_pair_strings(sibling)))
+ raise AndroidDeviceError(device, msg)
+
+
+def has_element(device, **kwargs):
+ """Checks a UI element whether appears or not in the current screen.
+
+ Args:
+ device: AndroidDevice object.
+ **kwargs: Key/value pairs to match in an XML node's attributes.
+
+ Returns:
+ True if the UI element appears in the current screen else False.
+ """
+ timeout_sec = kwargs.pop('timeout', 30)
+ try:
+ wait_and_get_xml_node(device, timeout_sec, **kwargs)
+ return True
+ except AndroidDeviceError:
+ return False
+
+
+def get_element_attributes(device, **kwargs):
+ """Gets a UI element's all attributes.
+
+ Args:
+ device: AndroidDevice object.
+ **kwargs: Key/value pairs to match in an XML node's attributes.
+
+ Returns:
+ XML Node Attributes.
+ """
+ timeout_sec = kwargs.pop('timeout', 30)
+ node = wait_and_get_xml_node(device, timeout_sec, **kwargs)
+ return node.attributes
+
+
+def wait_and_click(device, duration_ms=None, **kwargs):
+ """Wait for a UI element to appear and click on it.
+
+ This function locates a UI element on the screen by matching attributes of
+ nodes in XML DOM, calculates a point's coordinates within the boundary of the
+ element, and clicks on the point marked by the coordinates.
+
+ Args:
+ device: AndroidDevice object.
+ duration_ms: int, The number of milliseconds to long-click.
+ **kwargs: A set of `key=value` parameters that identifies a UI element.
+ """
+ timeout_sec = kwargs.pop('timeout', 30)
+ button_node = wait_and_get_xml_node(device, timeout_sec, **kwargs)
+ x, y = _find_point_in_bounds(button_node.attributes['bounds'].value)
+ args = []
+ if duration_ms is None:
+ args = 'input tap %s %s' % (str(x), str(y))
+ else:
+ # Long click.
+ args = 'input swipe %s %s %s %s %s' % \
+ (str(x), str(y), str(x), str(y), str(duration_ms))
+ device.adb.shell(args)
diff --git a/acts_tests/acts_contrib/test_utils/power/IperfHelper.py b/acts_tests/acts_contrib/test_utils/power/IperfHelper.py
new file mode 100644
index 0000000..cb5af7f
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/IperfHelper.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import statistics
+import time
+import acts.controllers.iperf_server as ipf
+
+
+class IperfHelper(object):
+ """ Helps with iperf config and processing the results
+
+ This class can be used to process the results of multiple iperf servers
+ (for example, dual traffic scenarios). It also helps in setting the
+ correct arguments for when using the phone as an iperf server
+ """
+ IPERF_CLIENT_RESULT_FILE_LOC_PHONE = '/sdcard/Download/'
+
+ def __init__(self, config):
+ self.traffic_type = config['traffic_type']
+ self.traffic_direction = config['traffic_direction']
+ self.duration = config['duration']
+ self.port = config['port']
+ self.server_idx = config['server_idx']
+ self.use_client_output = False
+ if 'bandwidth' in config:
+ self.bandwidth = config['bandwidth']
+ else:
+ self.bandwidth = None
+ if 'start_meas_time' in config:
+ self.start_meas_time = config['start_meas_time']
+ else:
+ self.start_meas_time = 0
+
+ iperf_args = '-i 1 -t {} -p {} -J'.format(self.duration, self.port)
+
+ if self.traffic_type == "UDP":
+ iperf_args = iperf_args + ' -u'
+ if self.traffic_direction == "DL":
+ iperf_args = iperf_args + ' -R'
+ self.use_client_output = True
+ # Set bandwidth in Mbit/s
+ if self.bandwidth is not None:
+ iperf_args = iperf_args + ' -b {}M'.format(self.bandwidth)
+
+ # Set the TCP window size
+ self.window = config.get("window", None)
+ if self.window:
+ iperf_args += ' -w {}M'.format(self.window)
+
+ # Parse the client side data to a file saved on the phone
+ self.results_filename_phone = self.IPERF_CLIENT_RESULT_FILE_LOC_PHONE \
+ + 'iperf_client_port_{}_{}.log'.format( \
+ self.port, self.traffic_direction)
+ iperf_args = iperf_args + ' > %s' % self.results_filename_phone
+
+ self.iperf_args = iperf_args
+
+ def process_iperf_results(self, dut, log, iperf_servers, test_name):
+ """Gets the iperf results from the phone and computes the average rate
+
+ Returns:
+ throughput: the average throughput (Mbit/s).
+ """
+ # Stopping the server (as safety to get the result file)
+ iperf_servers[self.server_idx].stop()
+ time.sleep(1)
+
+ # Get IPERF results and add this to the plot title
+ RESULTS_DESTINATION = os.path.join(
+ iperf_servers[self.server_idx].log_path,
+ 'iperf_client_output_{}.log'.format(test_name))
+
+ PULL_FILE = '{} {}'.format(self.results_filename_phone,
+ RESULTS_DESTINATION)
+ dut.adb.pull(PULL_FILE)
+
+ # Calculate the average throughput
+ if self.use_client_output:
+ iperf_file = RESULTS_DESTINATION
+ else:
+ iperf_file = iperf_servers[self.server_idx].log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+
+ # Get instantaneous rates after measuring starts
+ samples = iperf_result.instantaneous_rates[self.start_meas_time:-1]
+
+ # Convert values to Mbit/s
+ samples = [rate * 8 * (1.024**2) for rate in samples]
+
+ # compute mean, var and max_dev
+ mean = statistics.mean(samples)
+ var = statistics.variance(samples)
+ max_dev = 0
+ for rate in samples:
+ if abs(rate - mean) > max_dev:
+ max_dev = abs(rate - mean)
+
+ log.info('The average throughput is {}. Variance is {} and max '
+ 'deviation is {}.'.format(
+ round(mean, 2), round(var, 2), round(max_dev, 2)))
+
+ except:
+ log.warning('Cannot get iperf result.')
+ mean = 0
+
+ return mean
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerBTBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerBTBaseTest.py
new file mode 100644
index 0000000..056787c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerBTBaseTest.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import time
+import acts_contrib.test_utils.bt.bt_power_test_utils as btputils
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.power.PowerBaseTest as PBT
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory
+from math import copysign
+
+BLE_LOCATION_SCAN_DISABLE = 'settings put secure location_mode 0'
+PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music'
+INIT_ATTEN = [0]
+
+
+def ramp_attenuation(obj_atten, attenuation_target, attenuation_step_max=20,
+ time_wait_in_between=5 ):
+ """Ramp the attenuation up or down for BT tests.
+
+ Ramp the attenuation slowly so it won't have dramatic signal drop to affect
+ Link.
+
+ Args:
+ obj_atten: attenuator object, a single port attenuator
+ attenuation_target: target attenuation level to reach to.
+ attenuation_step_max: max step for attenuation set
+ time_wait_in_between: wait time between attenuation changes
+ """
+ sign = lambda x: copysign(1, x)
+ attenuation_delta = obj_atten.get_atten() - attenuation_target
+ while abs(attenuation_delta) > attenuation_step_max:
+ attenuation_intermediate = obj_atten.get_atten(
+ ) - sign(attenuation_delta) * attenuation_step_max
+ obj_atten.set_atten(attenuation_intermediate)
+ time.sleep(time_wait_in_between)
+ attenuation_delta = obj_atten.get_atten() - attenuation_target
+ obj_atten.set_atten(attenuation_target)
+
+
+class PowerBTBaseTest(PBT.PowerBaseTest):
+ """Base class for BT power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+ def setup_class(self):
+
+ super().setup_class()
+ # Get music file and push it to the phone
+ music_files = self.user_params.get('music_files', [])
+ if music_files:
+ music_src = music_files[0]
+ music_dest = PHONE_MUSIC_FILE_DIRECTORY
+ success = self.dut.push_system_file(music_src, music_dest)
+ if success:
+ self.music_file = os.path.join(PHONE_MUSIC_FILE_DIRECTORY,
+ os.path.basename(music_src))
+ # Initialize media_control class
+ self.media = btputils.MediaControl(self.dut, self.music_file)
+ # Set Attenuator to the initial attenuation
+ if hasattr(self, 'attenuators'):
+ self.set_attenuation(INIT_ATTEN)
+ self.attenuator = self.attenuators[0]
+ # Create the BTOE(Bluetooth-Other-End) device object
+ bt_devices = self.user_params.get('bt_devices', [])
+ if bt_devices:
+ attr, idx = bt_devices.split(':')
+ self.bt_device_controller = getattr(self, attr)[int(idx)]
+ self.bt_device = bt_factory().generate(self.bt_device_controller)
+ else:
+ self.log.error('No BT devices config is provided!')
+ # Turn off screen as all tests will be screen off
+ self.dut.droid.goToSleepNow()
+
+ def setup_test(self):
+
+ super().setup_test()
+ self.unpack_userparams(volume=0.9)
+ # Reset BT to factory defaults
+ self.dut.droid.bluetoothFactoryReset()
+ self.bt_device.reset()
+ self.bt_device.power_on()
+ btutils.enable_bluetooth(self.dut.droid, self.dut.ed)
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ Bring down the AP interface, delete the bridge interface, stop the
+ packet sender, and reset the ethernet interface for the packet sender
+ """
+ super().teardown_test()
+ self.dut.droid.bluetoothFactoryReset()
+ self.dut.adb.shell(BLE_LOCATION_SCAN_DISABLE)
+ if hasattr(self, 'media'):
+ self.media.stop()
+ # Set Attenuator to the initial attenuation
+ if hasattr(self, 'attenuators'):
+ self.set_attenuation(INIT_ATTEN)
+ self.bt_device.reset()
+ self.bt_device.power_off()
+ btutils.disable_bluetooth(self.dut.droid)
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ super().teardown_class()
+ self.dut.droid.bluetoothFactoryReset()
+ self.dut.adb.shell(BLE_LOCATION_SCAN_DISABLE)
+ self.bt_device.reset()
+ self.bt_device.power_off()
+ btutils.disable_bluetooth(self.dut.droid)
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py
new file mode 100644
index 0000000..0419290
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py
@@ -0,0 +1,502 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+import logging
+import math
+import os
+import re
+import time
+
+import acts.controllers.power_monitor as power_monitor_lib
+import acts.controllers.iperf_server as ipf
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts_contrib.test_utils.power.loggers.power_metric_logger import PowerMetricLogger
+from acts_contrib.test_utils.power import plot_utils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+
+RESET_BATTERY_STATS = 'dumpsys batterystats --reset'
+IPERF_TIMEOUT = 180
+THRESHOLD_TOLERANCE_DEFAULT = 0.2
+GET_FROM_PHONE = 'get_from_dut'
+GET_FROM_AP = 'get_from_ap'
+PHONE_BATTERY_VOLTAGE_DEFAULT = 4.2
+MONSOON_MAX_CURRENT = 8.0
+DEFAULT_MONSOON_FREQUENCY = 500
+ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
+MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
+TEMP_FILE = '/sdcard/Download/tmp.log'
+
+
+class ObjNew(object):
+ """Create a random obj with unknown attributes and value.
+
+ """
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+ def __contains__(self, item):
+ """Function to check if one attribute is contained in the object.
+
+ Args:
+ item: the item to check
+ Return:
+ True/False
+ """
+ return hasattr(self, item)
+
+
+class PowerBaseTest(base_test.BaseTestClass):
+ """Base class for all wireless power related tests.
+
+ """
+ def __init__(self, controllers):
+
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.power_result = BlackboxMetricLogger.for_test_case(
+ metric_name='avg_power')
+ self.start_meas_time = 0
+ self.rockbottom_script = None
+ self.img_name = ''
+ self.dut = None
+ self.power_logger = PowerMetricLogger.for_test_case()
+ self.power_monitor = None
+
+ @property
+ def final_test(self):
+ return len(
+ self.results.requested
+ ) > 0 and self.current_test_name == self.results.requested[-1]
+
+ @property
+ def display_name_test_suite(self):
+ return getattr(self, '_display_name_test_suite',
+ self.__class__.__name__)
+
+ @display_name_test_suite.setter
+ def display_name_test_suite(self, name):
+ self._display_name_test_suite = name
+
+ @property
+ def display_name_test_case(self):
+ default_test_name = getattr(self, 'test_name', None)
+ return getattr(self, '_display_name_test_case', default_test_name)
+
+ @display_name_test_case.setter
+ def display_name_test_case(self, name):
+ self._display_name_test_case = name
+
+ def initialize_power_monitor(self):
+ """ Initializes the power monitor object.
+
+ Raises an exception if there are no controllers available.
+ """
+ if hasattr(self, 'monsoons'):
+ self.power_monitor = power_monitor_lib.PowerMonitorMonsoonFacade(
+ self.monsoons[0])
+ self.monsoons[0].set_max_current(8.0)
+ self.monsoons[0].set_voltage(self.mon_voltage)
+ else:
+ raise RuntimeError('No power monitors available.')
+
+ def setup_class(self):
+
+ self.log = logging.getLogger()
+ self.tests = self.get_existing_test_names()
+
+ # Obtain test parameters from user_params
+ TEST_PARAMS = self.TAG + '_params'
+ self.test_params = self.user_params.get(TEST_PARAMS, {})
+ if not self.test_params:
+ self.log.warning(TEST_PARAMS + ' was not found in the user '
+ 'parameters defined in the config file.')
+
+ # Override user_param values with test parameters
+ self.user_params.update(self.test_params)
+
+ # Unpack user_params with default values. All the usages of user_params
+ # as self attributes need to be included either as a required parameter
+ # or as a parameter with a default value.
+ req_params = ['custom_files', 'mon_duration']
+ self.unpack_userparams(req_params,
+ mon_freq=DEFAULT_MONSOON_FREQUENCY,
+ mon_offset=0,
+ bug_report=False,
+ extra_wait=None,
+ iperf_duration=None,
+ pass_fail_tolerance=THRESHOLD_TOLERANCE_DEFAULT,
+ mon_voltage=PHONE_BATTERY_VOLTAGE_DEFAULT)
+
+ # Setup the must have controllers, phone and monsoon
+ self.dut = self.android_devices[0]
+ self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
+ os.makedirs(self.mon_data_path, exist_ok=True)
+
+ # Initialize the power monitor object that will be used to measure
+ self.initialize_power_monitor()
+
+ # Unpack the thresholds file or fail class setup if it can't be found
+ for file in self.custom_files:
+ if 'pass_fail_threshold_' + self.dut.model in file:
+ self.threshold_file = file
+ break
+ else:
+ raise RuntimeError('Required test pass/fail threshold file is '
+ 'missing')
+
+ # Unpack the rockbottom script or fail class setup if it can't be found
+ for file in self.custom_files:
+ if 'rockbottom_' + self.dut.model in file:
+ self.rockbottom_script = file
+ break
+ else:
+ raise RuntimeError('Required rockbottom script is missing.')
+
+ # Unpack optional custom files
+ for file in self.custom_files:
+ if 'attenuator_setting' in file:
+ self.attenuation_file = file
+ elif 'network_config' in file:
+ self.network_file = file
+
+ if hasattr(self, 'attenuators'):
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.atten_level = self.unpack_custom_file(self.attenuation_file)
+ self.threshold = self.unpack_custom_file(self.threshold_file)
+ self.mon_info = self.create_monsoon_info()
+
+ # Sync device time, timezone and country code
+ utils.require_sl4a((self.dut, ))
+ utils.sync_device_time(self.dut)
+ wutils.set_wifi_country_code(self.dut, 'US')
+
+ screen_on_img = self.user_params.get('screen_on_img', [])
+ if screen_on_img:
+ img_src = screen_on_img[0]
+ img_dest = '/sdcard/Pictures/'
+ success = self.dut.push_system_file(img_src, img_dest)
+ if success:
+ self.img_name = os.path.basename(img_src)
+
+ def setup_test(self):
+ """Set up test specific parameters or configs.
+
+ """
+ # Reset result variables
+ self.avg_current = 0
+ self.samples = []
+ self.power_result.metric_value = 0
+
+ # Set the device into rockbottom state
+ self.dut_rockbottom()
+ wutils.reset_wifi(self.dut)
+ wutils.wifi_toggle_state(self.dut, False)
+
+ # Wait for extra time if needed for the first test
+ if self.extra_wait:
+ self.more_wait_first_test()
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ """
+ self.log.info('Tearing down the test case')
+ self.power_monitor.connect_usb()
+ self.power_logger.set_avg_power(self.power_result.metric_value)
+ self.power_logger.set_avg_current(self.avg_current)
+ self.power_logger.set_voltage(self.mon_voltage)
+ self.power_logger.set_testbed(self.testbed_name)
+
+ # If a threshold was provided, log it in the power proto
+ if self.threshold and self.test_name in self.threshold:
+ avg_current_threshold = self.threshold[self.test_name]
+ self.power_logger.set_avg_current_threshold(avg_current_threshold)
+
+ build_id = self.dut.build_info.get('build_id', '')
+ incr_build_id = self.dut.build_info.get('incremental_build_id', '')
+ branch = self.user_params.get('branch', '')
+ target = self.dut.device_info.get('flavor', '')
+
+ self.power_logger.set_branch(branch)
+ self.power_logger.set_build_id(build_id)
+ self.power_logger.set_incremental_build_id(incr_build_id)
+ self.power_logger.set_target(target)
+
+ # Log the display name of the test suite and test case
+ if self.display_name_test_suite:
+ name = self.display_name_test_suite
+ self.power_logger.set_test_suite_display_name(name)
+
+ if self.display_name_test_case:
+ name = self.display_name_test_case
+ self.power_logger.set_test_case_display_name(name)
+
+ # Take Bugreport
+ if self.bug_report:
+ begin_time = utils.get_current_epoch_time()
+ self.dut.take_bug_report(self.test_name, begin_time)
+
+ # Allow the device to cooldown before executing the next test
+ cooldown = self.test_params.get('cooldown', None)
+ if cooldown and not self.final_test:
+ time.sleep(cooldown)
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ self.log.info('Tearing down the test class')
+ if self.power_monitor:
+ self.power_monitor.connect_usb()
+
+ def on_fail(self, test_name, begin_time):
+ self.power_logger.set_pass_fail_status('FAIL')
+
+ def on_pass(self, test_name, begin_time):
+ self.power_logger.set_pass_fail_status('PASS')
+
+ def dut_rockbottom(self):
+ """Set the dut to rockbottom state
+
+ """
+ # The rockbottom script might include a device reboot, so it is
+ # necessary to stop SL4A during its execution.
+ self.dut.stop_services()
+ self.log.info('Executing rockbottom script for ' + self.dut.model)
+ os.chmod(self.rockbottom_script, 0o777)
+ os.system('{} {} {}'.format(self.rockbottom_script, self.dut.serial,
+ self.img_name))
+ # Make sure the DUT is in root mode after coming back
+ self.dut.root_adb()
+ # Restart SL4A
+ self.dut.start_services()
+
+ def unpack_custom_file(self, file, test_specific=True):
+ """Unpack the pass_fail_thresholds from a common file.
+
+ Args:
+ file: the common file containing pass fail threshold.
+ test_specific: if True, returns the JSON element within the file
+ that starts with the test class name.
+ """
+ with open(file, 'r') as f:
+ params = json.load(f)
+ if test_specific:
+ try:
+ return params[self.TAG]
+ except KeyError:
+ pass
+ else:
+ return params
+
+ def decode_test_configs(self, attrs, indices):
+ """Decode the test config/params from test name.
+
+ Remove redundant function calls when tests are similar.
+ Args:
+ attrs: a list of the attrs of the test config obj
+ indices: a list of the location indices of keyword in the test name.
+ """
+ # Decode test parameters for the current test
+ test_params = self.current_test_name.split('_')
+ values = [test_params[x] for x in indices]
+ config_dict = dict(zip(attrs, values))
+ self.test_configs = ObjNew(**config_dict)
+
+ def more_wait_first_test(self):
+ # For the first test, increase the offset for longer wait time
+ if self.current_test_name == self.tests[0]:
+ self.mon_info.offset = self.mon_offset + self.extra_wait
+ else:
+ self.mon_info.offset = self.mon_offset
+
+ def set_attenuation(self, atten_list):
+ """Function to set the attenuator to desired attenuations.
+
+ Args:
+ atten_list: list containing the attenuation for each attenuator.
+ """
+ if len(atten_list) != self.num_atten:
+ raise Exception('List given does not have the correct length')
+ for i in range(self.num_atten):
+ self.attenuators[i].set_atten(atten_list[i])
+
+ def measure_power_and_validate(self):
+ """The actual test flow and result processing and validate.
+
+ """
+ self.collect_power_data()
+ self.pass_fail_check(self.avg_current)
+
+ def collect_power_data(self):
+ """Measure power, plot and take log if needed.
+
+ Returns:
+ A MonsoonResult object.
+ """
+ # Collecting current measurement data and plot
+ samples = self.power_monitor_data_collect_save()
+
+ current = [sample[1] for sample in samples]
+ average_current = sum(current) * 1000 / len(current)
+
+ self.power_result.metric_value = (average_current * self.mon_voltage)
+ self.avg_current = average_current
+
+ plot_title = '{}_{}_{}'.format(self.test_name, self.dut.model,
+ self.dut.build_info['build_id'])
+ plot_utils.current_waveform_plot(samples, self.mon_voltage,
+ self.mon_info.data_path, plot_title)
+
+ return samples
+
+ def pass_fail_check(self, average_current=None):
+ """Check the test result and decide if it passed or failed.
+
+ The threshold is provided in the config file. In this class, result is
+ current in mA.
+ """
+
+ if not self.threshold or self.test_name not in self.threshold:
+ self.log.error("No threshold is provided for the test '{}' in "
+ "the configuration file.".format(self.test_name))
+ return
+
+ current_threshold = self.threshold[self.test_name]
+ if average_current:
+ asserts.assert_true(
+ abs(average_current - current_threshold) / current_threshold <
+ self.pass_fail_tolerance,
+ 'Measured average current in [{}]: {:.2f}mA, which is '
+ 'out of the acceptable range {:.2f}±{:.2f}mA'.format(
+ self.test_name, average_current, current_threshold,
+ self.pass_fail_tolerance * current_threshold))
+ asserts.explicit_pass(
+ 'Measurement finished for [{}]: {:.2f}mA, which is '
+ 'within the acceptable range {:.2f}±{:.2f}'.format(
+ self.test_name, average_current, current_threshold,
+ self.pass_fail_tolerance * current_threshold))
+ else:
+ asserts.fail(
+ 'Something happened, measurement is not complete, test failed')
+
+ def create_monsoon_info(self):
+ """Creates the config dictionary for monsoon
+
+ Returns:
+ mon_info: Dictionary with the monsoon packet config
+ """
+ mon_info = ObjNew(freq=self.mon_freq,
+ duration=self.mon_duration,
+ offset=self.mon_offset,
+ data_path=self.mon_data_path)
+ return mon_info
+
+ def power_monitor_data_collect_save(self):
+ """Current measurement and save the log file.
+
+ Collect current data using Monsoon box and return the path of the
+ log file. Take bug report if requested.
+
+ Returns:
+ A list of tuples in which the first element is a timestamp and the
+ second element is the sampled current in Amperes at that time.
+ """
+
+ tag = '{}_{}_{}'.format(self.test_name, self.dut.model,
+ self.dut.build_info['build_id'])
+
+ data_path = os.path.join(self.mon_info.data_path, '{}.txt'.format(tag))
+
+ # If the specified Monsoon data file already exists (e.g., multiple
+ # measurements in a single test), write the results to a new file with
+ # the postfix "_#".
+ if os.path.exists(data_path):
+ highest_value = 1
+ for filename in os.listdir(os.path.dirname(data_path)):
+ match = re.match(r'{}_(\d+).txt'.format(tag), filename)
+ if match:
+ highest_value = max(highest_value, int(match.group(1)))
+
+ data_path = os.path.join(self.mon_info.data_path,
+ '%s_%s.txt' % (tag, highest_value + 1))
+
+ # Resets the battery status right before the test starts.
+ self.dut.adb.shell(RESET_BATTERY_STATS)
+ self.log.info('Starting power measurement. Duration: {}s. Offset: '
+ '{}s. Voltage: {} V.'.format(self.mon_info.duration,
+ self.mon_info.offset,
+ self.mon_voltage))
+
+ # TODO(b/155426729): Create an accurate host-to-device time difference
+ # measurement.
+ device_time_cmd = 'echo $EPOCHREALTIME'
+ device_time = self.dut.adb.shell(device_time_cmd)
+ host_time = time.time()
+ self.log.debug('device start time %s, host start time %s', device_time,
+ host_time)
+ device_to_host_offset = float(device_time) - host_time
+
+ # Start the power measurement using monsoon.
+ self.dut.stop_services()
+ time.sleep(1)
+ self.power_monitor.disconnect_usb()
+ measurement_args = dict(duration=self.mon_info.duration,
+ measure_after_seconds=self.mon_info.offset,
+ hz=self.mon_info.freq)
+ self.power_monitor.measure(measurement_args=measurement_args,
+ start_time=device_to_host_offset,
+ output_path=data_path)
+ self.power_monitor.connect_usb()
+ self.dut.wait_for_boot_completion()
+ time.sleep(10)
+ self.dut.start_services()
+
+ return self.power_monitor.get_battery_waveform(
+ monsoon_file_path=data_path)
+
+ def process_iperf_results(self):
+ """Get the iperf results and process.
+
+ Returns:
+ throughput: the average throughput during tests.
+ """
+ # Get IPERF results and add this to the plot title
+ RESULTS_DESTINATION = os.path.join(
+ self.iperf_server.log_path,
+ 'iperf_client_output_{}.log'.format(self.current_test_name))
+ self.dut.pull_files(TEMP_FILE, RESULTS_DESTINATION)
+ # Calculate the average throughput
+ if self.use_client_output:
+ iperf_file = RESULTS_DESTINATION
+ else:
+ iperf_file = self.iperf_server.log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+
+ # Compute the throughput in Mbit/s
+ throughput = (math.fsum(
+ iperf_result.instantaneous_rates[self.start_meas_time:-1]
+ ) / len(iperf_result.instantaneous_rates[self.start_meas_time:-1])
+ ) * 8 * (1.024**2)
+
+ self.log.info('The average throughput is {}'.format(throughput))
+ except ValueError:
+ self.log.warning('Cannot get iperf result. Setting to 0')
+ throughput = 0
+ return throughput
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerCoexBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerCoexBaseTest.py
new file mode 100644
index 0000000..8142f82
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerCoexBaseTest.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - The Android Open Source Project
+8
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.power.PowerWiFiBaseTest as PWBT
+from acts import utils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+
+
+class PowerCoexBaseTest(PBtBT.PowerBTBaseTest, PWBT.PowerWiFiBaseTest):
+ """Base class for BT power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+ def coex_test_phone_setup(self, Screen_status, WiFi_status, WiFi_band,
+ BT_status, BLE_status, Cellular_status,
+ Celluar_band):
+ """Setup the phone in desired state for coex tests.
+
+ Args:
+ Screen_status: 'ON' or 'OFF'
+ WiFi_status: 'ON', 'Connected', 'Disconnected', or 'OFF'
+ WiFi_band: '2g', '5g' or None, the band of AP
+ BT_status: 'ON' or 'OFF'
+ BLE_status: 'ON' or 'OFF'
+ Cellular_status: 'ON' or 'OFF'
+ Celluar_band: 'Verizon', 'Tmobile', or 'ATT' for live network,
+ actual band for callbox setup; 'None' when celluar is OFF
+ """
+ # Setup WiFi
+ if WiFi_status == 'ON':
+ wutils.wifi_toggle_state(self.dut, True)
+ elif WiFi_status == 'Connected':
+ self.setup_ap_connection(self.main_network[WiFi_band])
+ elif WiFi_status == 'Disconnected':
+ self.setup_ap_connection(self.main_network[WiFi_band],
+ connect=False)
+
+ # Setup BT/BLE
+ self.phone_setup_for_BT(BT_status, BLE_status, Screen_status)
+
+ # Setup Cellular
+ if Cellular_status == 'ON':
+ self.dut.droid.connectivityToggleAirplaneMode(False)
+ utils.set_mobile_data_always_on(self.dut, True)
+
+ def coex_scan_setup(self, WiFi_scan, BLE_scan_mode, wifi_scan_command):
+ """Setup for scan activities on WiFi, BT/BLE, and cellular.
+
+ Args:
+ WiFi_scan: 'ON', 'OFF' or 'PNO'
+ BLE_scan_mode: 'balanced', 'opportunistic', 'low_power', or 'low_latency'
+ """
+ if WiFi_scan == 'ON':
+ self.dut.adb.shell(wifi_scan_command)
+ if WiFi_scan == 'PNO':
+ self.log.info(
+ 'Set attenuation so device loses connection to trigger PNO scans'
+ )
+ # Set to maximum attenuation 95 dB to cut down connection
+ [self.attenuators[i].set_atten(95) for i in range(self.num_atten)]
+ if BLE_scan_mode is not None:
+ self.start_pmc_ble_scan(BLE_scan_mode, self.mon_info.offset,
+ self.mon_info.duration)
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py
new file mode 100644
index 0000000..63c59db
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import time
+
+from acts import signals
+from acts import utils
+from acts_contrib.test_utils.power.PowerBaseTest import PowerBaseTest
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+
+DEFAULT_WAIT_TIME = 120
+STANDALONE_WAIT_TIME = 1200
+DPO_NV_VALUE = '15DC'
+MDS_TEST_PACKAGE = 'com.google.mdstest'
+MDS_RUNNER = 'com.google.mdstest.instrument.ModemConfigInstrumentation'
+
+
+class PowerGTWGnssBaseTest(PowerBaseTest):
+ """Power GTW Gnss Base test"""
+
+ def setup_class(self):
+ super().setup_class()
+ self.ad = self.android_devices[0]
+ req_params = [
+ 'wifi_network', 'test_location', 'qdsp6m_path',
+ 'calibrate_target'
+ ]
+ self.unpack_userparams(req_param_names=req_params)
+ gutils.disable_xtra_throttle(self.ad)
+
+ def setup_test(self):
+ super().setup_test()
+ # Enable GNSS setting for GNSS standalone mode
+ self.ad.adb.shell('settings put secure location_mode 3')
+
+ def teardown_test(self):
+ begin_time = utils.get_current_epoch_time()
+ self.ad.take_bug_report(self.test_name, begin_time)
+ gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path)
+
+ def baseline_test(self):
+ """Baseline power measurement"""
+ self.ad.droid.goToSleepNow()
+ self.collect_power_data()
+ self.ad.log.info('TestResult AVG_Current %.2f' % self.avg_current)
+
+ def start_gnss_tracking_with_power_data(self,
+ mode='default',
+ is_signal=True,
+ freq=0,
+ lowpower=False,
+ meas=False):
+ """Start GNSS tracking and collect power metrics.
+
+ Args:
+ is_signal: default True, False for no Gnss signal test.
+ freq: an integer to set location update frequency.
+ lowpower: a boolean to set GNSS Low Power Mode.
+ mean: a boolean to set GNSS Measurement registeration.
+ """
+ self.ad.adb.shell('settings put secure location_mode 3')
+ gutils.clear_aiding_data_by_gtw_gpstool(self.ad)
+ gutils.start_gnss_by_gtw_gpstool(self.ad, True, 'gnss', True, freq,
+ lowpower, meas)
+ self.ad.droid.goToSleepNow()
+
+ sv_collecting_time = DEFAULT_WAIT_TIME
+ if mode == 'standalone':
+ sv_collecting_time = STANDALONE_WAIT_TIME
+ self.ad.log.info('Wait %d seconds for %s mode' %
+ (sv_collecting_time, mode))
+ time.sleep(sv_collecting_time)
+
+ samples = self.collect_power_data()
+ self.ad.log.info('TestResult AVG_Current %.2f' % self.avg_current)
+ self.calibrate_avg_current(samples)
+ self.ad.send_keycode('WAKEUP')
+ gutils.start_gnss_by_gtw_gpstool(self.ad, False, 'gnss')
+ if is_signal:
+ gutils.parse_gtw_gpstool_log(
+ self.ad, self.test_location, type='gnss')
+
+ def calibrate_avg_current(self, samples):
+ """Calibrate average current by filtering AP wake up current with target
+ value.
+
+ Args:
+ samples: a list of tuples where the first element is a timestamp
+ and the second element is a current sample.
+ """
+ calibrate_results = [
+ sample[1] * 1000 for sample in samples
+ if sample[1] * 1000 < self.calibrate_target
+ ]
+ avg_current = sum(calibrate_results) / len(calibrate_results)
+ self.ad.log.info('TestResult Calibrate_AVG_Current %.2f' % avg_current)
+
+ def enable_DPO(self, enable):
+ """Enable or disable the DPO option.
+
+ Args:
+ enable: True or False to enable DPO.
+ """
+ self.ad.log.info('Change DPO to new state: %s.' % enable)
+ val = '02' if enable else '00'
+ options = {'request': 'writeNV', 'item': DPO_NV_VALUE, 'data': val}
+ instrument_cmd = gutils.build_instrumentation_call(
+ MDS_TEST_PACKAGE, MDS_RUNNER, options=options)
+ result = self.ad.adb.shell(instrument_cmd)
+ if 'SUCCESS' not in result:
+ self.ad.log.info(result)
+ raise signals.TestFailure('DPO is not able to Turn: %s' % enable)
+ self.dut_rockbottom()
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerGnssBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerGnssBaseTest.py
new file mode 100644
index 0000000..36ab5fe
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerGnssBaseTest.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+
+import os
+
+import acts_contrib.test_utils.power.PowerBaseTest as PBT
+
+from acts import base_test
+from acts.controllers import monsoon
+from bokeh.layouts import column, layout
+from bokeh.models import CustomJS, ColumnDataSource
+from bokeh.models import tools as bokeh_tools
+from bokeh.models.widgets import DataTable, TableColumn
+from bokeh.plotting import figure, output_file, save
+from acts.controllers.monsoon_lib.api.common import PassthroughStates
+from acts.controllers.monsoon_lib.api.common import MonsoonError
+
+LOGTIME_RETRY_COUNT = 3
+RESET_BATTERY_STATS = 'dumpsys batterystats --reset'
+RECOVER_MONSOON_RETRY_COUNT = 3
+MONSOON_RETRY_INTERVAL = 300
+
+class PowerGnssBaseTest(PBT.PowerBaseTest):
+ """
+ Base Class for all GNSS Power related tests
+ """
+
+ def setup_class(self):
+ super().setup_class()
+ req_params = ['customjsfile', 'maskfile']
+ self.unpack_userparams(req_params)
+
+ def collect_power_data(self):
+ """Measure power and plot."""
+ samples = super().collect_power_data()
+ plot_title = '{}_{}_{}_Power'.format(self.test_name, self.dut.model,
+ self.dut.build_info['build_id'])
+ self.monsoon_data_plot_power(samples, self.mon_voltage,
+ self.mon_info.data_path, plot_title)
+ return samples
+
+ def monsoon_data_plot_power(self, samples, voltage, dest_path, plot_title):
+ """Plot the monsoon power data using bokeh interactive plotting tool.
+
+ Args:
+ samples: a list of tuples in which the first element is a timestamp
+ and the second element is the sampled current at that time
+ voltage: the voltage that was used during the measurement
+ dest_path: destination path
+ plot_title: a filename and title for the plot.
+
+ """
+
+ logging.info('Plotting the power measurement data.')
+
+ time_relative = [sample[0] for sample in samples]
+ duration = time_relative[-1] - time_relative[0]
+ current_data = [sample[1] * 1000 for sample in samples]
+ avg_current = sum(current_data) / len(current_data)
+
+ power_data = [current * voltage for current in current_data]
+
+ color = ['navy'] * len(samples)
+
+ # Preparing the data and source link for bokehn java callback
+ source = ColumnDataSource(
+ data=dict(x0=time_relative, y0=power_data, color=color))
+ s2 = ColumnDataSource(
+ data=dict(
+ z0=[duration],
+ y0=[round(avg_current, 2)],
+ x0=[round(avg_current * voltage, 2)],
+ z1=[round(avg_current * voltage * duration, 2)],
+ z2=[round(avg_current * duration, 2)]))
+ # Setting up data table for the output
+ columns = [
+ TableColumn(field='z0', title='Total Duration (s)'),
+ TableColumn(field='y0', title='Average Current (mA)'),
+ TableColumn(field='x0', title='Average Power (4.2v) (mW)'),
+ TableColumn(field='z1', title='Average Energy (mW*s)'),
+ TableColumn(field='z2', title='Normalized Average Energy (mA*s)')
+ ]
+ dt = DataTable(
+ source=s2, columns=columns, width=1300, height=60, editable=True)
+
+ output_file(os.path.join(dest_path, plot_title + '.html'))
+ tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save'
+ # Create a new plot with the datatable above
+ plot = figure(
+ plot_width=1300,
+ plot_height=700,
+ title=plot_title,
+ tools=tools,
+ output_backend='webgl')
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width'))
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height'))
+ plot.line('x0', 'y0', source=source, line_width=2)
+ plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color')
+ plot.xaxis.axis_label = 'Time (s)'
+ plot.yaxis.axis_label = 'Power (mW)'
+ plot.title.text_font_size = {'value': '15pt'}
+ jsscript = open(self.customjsfile, 'r')
+ customjsscript = jsscript.read()
+ # Callback Java scripting
+ source.callback = CustomJS(
+ args=dict(mytable=dt),
+ code=customjsscript)
+
+ # Layout the plot and the datatable bar
+ save(layout([[dt], [plot]]))
+
+ def disconnect_usb(self, ad, sleeptime):
+ """Disconnect usb while device is on sleep and
+ connect the usb again once the sleep time completes
+
+ sleeptime: sleep time where dut is disconnected from usb
+ """
+ self.dut.adb.shell(RESET_BATTERY_STATS)
+ time.sleep(1)
+ for _ in range(LOGTIME_RETRY_COUNT):
+ self.monsoons[0].usb(PassthroughStates.OFF)
+ if not ad.is_connected():
+ time.sleep(sleeptime)
+ self.monsoons[0].usb(PassthroughStates.ON)
+ break
+ else:
+ self.log.error('Test failed after maximum retry')
+ for _ in range(RECOVER_MONSOON_RETRY_COUNT):
+ if self.monsoon_recover():
+ break
+ else:
+ self.log.warning(
+ 'Wait for {} second then try again'.format(
+ MONSOON_RETRY_INTERVAL))
+ time.sleep(MONSOON_RETRY_INTERVAL)
+ else:
+ self.log.error('Failed to recover monsoon')
+
+ def monsoon_recover(self):
+ """Test loop to wait for monsoon recover from unexpected error.
+
+ Wait for a certain time duration, then quit.0
+ Args:
+ mon: monsoon object
+ Returns:
+ True/False
+ """
+ try:
+ self.power_monitor.connect_usb()
+ logging.info('Monsoon recovered from unexpected error')
+ time.sleep(2)
+ return True
+ except MonsoonError:
+ try:
+ self.log.info(self.monsoons[0]._mon.ser.in_waiting)
+ except AttributeError:
+ # This attribute does not exist for HVPMs.
+ pass
+ logging.warning('Unable to recover monsoon from unexpected error')
+ return False
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py
new file mode 100644
index 0000000..7ca573e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import acts_contrib.test_utils.power.PowerBaseTest as PBT
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.power import plot_utils
+
+IPERF_DURATION = 'iperf_duration'
+INITIAL_ATTEN = [0, 0, 90, 90]
+IPERF_TAIL = 5
+
+
+class PowerWiFiBaseTest(PBT.PowerBaseTest):
+ """Base class for WiFi power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+ def setup_class(self):
+
+ super().setup_class()
+ if hasattr(self, 'access_points'):
+ self.access_point = self.access_points[0]
+ self.access_point_main = self.access_points[0]
+ if len(self.access_points) > 1:
+ self.access_point_aux = self.access_points[1]
+ if hasattr(self, 'attenuators'):
+ self.set_attenuation(INITIAL_ATTEN)
+ if hasattr(self, 'network_file'):
+ self.networks = self.unpack_custom_file(self.network_file, False)
+ self.main_network = self.networks['main_network']
+ self.aux_network = self.networks['aux_network']
+ if hasattr(self, 'packet_senders'):
+ self.pkt_sender = self.packet_senders[0]
+ if hasattr(self, 'iperf_servers'):
+ self.iperf_server = self.iperf_servers[0]
+ if self.iperf_duration:
+ self.mon_duration = self.iperf_duration - self.mon_offset - IPERF_TAIL
+ self.create_monsoon_info()
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ Bring down the AP interface, delete the bridge interface, stop the
+ packet sender, and reset the ethernet interface for the packet sender
+ """
+ super().teardown_test()
+ if hasattr(self, 'pkt_sender'):
+ self._safe_teardown('pkt_sender stop sending',
+ self.pkt_sender.stop_sending,
+ ignore_status=True)
+ if hasattr(self, 'brconfigs'):
+ self._safe_teardown('brconfigs', self.access_point.bridge.teardown,
+ self.brconfigs)
+ delattr(self, 'brconfigs')
+ if hasattr(self, 'brconfigs_main'):
+ self._safe_teardown('brconfigs_main',
+ self.access_point_main.bridge.teardown,
+ self.brconfigs_main)
+ delattr(self, 'brconfigs_main')
+ if hasattr(self, 'brconfigs_aux'):
+ self._safe_teardown('brconfigs_aux',
+ self.access_point_aux.bridge.teardown,
+ self.brconfigs_aux)
+ delattr(self, 'brconfigs_aux')
+ if hasattr(self, 'access_points'):
+ for ap in self.access_points:
+ self._safe_teardown('access point {}'.format(ap.identifier),
+ ap.close)
+ if hasattr(self, 'pkt_sender'):
+ self._safe_teardown('pkt_sender reset host interface',
+ wputils.reset_host_interface,
+ self.pkt_sender.interface)
+ if hasattr(self, 'iperf_server'):
+ self._safe_teardown('iperf_server', self.iperf_server.stop);
+
+ def _safe_teardown(self, attr, teardown_method, *arg, **kwargs):
+ """Teardown the object with try block.
+
+ Adds a try block for each teardown step to make sure that each
+ teardown step is executed.
+
+ Args:
+ attr: the teardown attribute description for logging
+ teardown_method: the method for teardown
+ *arg: positional arguments for teardown_method
+ **kwargs: keyword arguments for teardown_method
+ """
+ try:
+ self.log.info('teardown %s with %s', attr, teardown_method.__name__)
+ teardown_method(*arg, **kwargs)
+ except Exception as e:
+ self.log.warning('teardown of %s fails with %s', attr, e)
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ super().teardown_class()
+ if hasattr(self, 'access_points'):
+ for ap in self.access_points:
+ ap.close()
+
+ def setup_ap_connection(self,
+ network,
+ bandwidth=80,
+ connect=True,
+ ap=None):
+ """Setup AP and connect DUT to it.
+
+ Args:
+ network: the network config for the AP to be setup
+ bandwidth: bandwidth of the WiFi network to be setup
+ connect: indicator of if connect dut to the network after setup
+ ap: access point object, default is None to find the main AP
+ Returns:
+ self.brconfigs: dict for bridge interface configs
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ if not ap:
+ if hasattr(self, 'access_points'):
+ self.brconfigs = wputils.ap_setup(self.access_point,
+ network,
+ bandwidth=bandwidth)
+ else:
+ self.brconfigs = wputils.ap_setup(ap, network, bandwidth=bandwidth)
+ if connect:
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ if ap or (not ap and hasattr(self, 'access_points')):
+ return self.brconfigs
+
+ def collect_power_data(self):
+ """Measure power, plot and check pass/fail.
+
+ If IPERF is run, need to pull iperf results and attach it to the plot.
+ """
+ samples = super().collect_power_data()
+ tag = ''
+ if self.iperf_duration:
+ throughput = self.process_iperf_results()
+ plot_title = '{}_{}_{}_RSSI_{0:d}dBm_Throughput_{1:.2f}Mbps'.format(
+ self.test_name, self.dut.model,
+ self.dut.build_info['build_id'], self.RSSI, throughput)
+ plot_utils.current_waveform_plot(samples, self.mon_voltage,
+ self.mon_info.data_path,
+ plot_title)
+ return samples
diff --git a/acts_tests/acts_contrib/test_utils/power/__init__.py b/acts_tests/acts_contrib/test_utils/power/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/__init__.py b/acts_tests/acts_contrib/test_utils/power/cellular/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py
new file mode 100644
index 0000000..a4c2df6
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+
+
+class PowerTelHotspotTest(ctpt.PowerTelTrafficTest):
+ """ Cellular traffic over WiFi tethering power test.
+
+ Treated as a different case of data traffic. Inherits from
+ PowerTelTrafficTest and only needs to make a change in the measurement step.
+ """
+
+ # Class config parameters
+ CONFIG_KEY_WIFI = 'hotspot_network'
+
+ # Test name configuration keywords
+ PARAM_WIFI_BAND = "wifiband"
+ PARAM_2G_BAND = "2g"
+ PARAM_5G_BAND = "5g"
+
+ def __init__(self, controllers):
+ """ Class initialization
+
+ Set attributes to default values.
+ """
+
+ super().__init__(controllers)
+
+ # Initialize values
+ self.wifi_band = None
+ self.iperf_results = None
+
+ def setup_class(self):
+ """ Executed before any test case is started.
+
+ Set country code for client and host devices.
+
+ """
+
+ if not super().setup_class():
+ return False
+
+ # If an SSID and password are indicated in the configuration parameters,
+ # use those. If not, use default parameters and warn the user.
+
+ if hasattr(self, self.CONFIG_KEY_WIFI):
+
+ self.network = getattr(self, self.CONFIG_KEY_WIFI)
+
+ if not (wutils.WifiEnums.SSID_KEY in self.network
+ and wutils.WifiEnums.PWD_KEY in self.network):
+ raise RuntimeError(
+ "The '{}' key in the configuration file needs"
+ " to contain the '{}' and '{}' fields.".format(
+ self.CONFIG_KEY_WIFI, wutils.WifiEnums.SSID_KEY,
+ wutils.WifiEnums.PWD_KEY))
+ else:
+
+ self.log.warning("The configuration file doesn't indicate an SSID "
+ "password for the hotspot. Using default values. "
+ "To configured the SSID and pwd include a the key"
+ " {} containing the '{}' and '{}' fields.".format(
+ self.CONFIG_KEY_WIFI,
+ wutils.WifiEnums.SSID_KEY,
+ wutils.WifiEnums.PWD_KEY))
+
+ self.network = {
+ wutils.WifiEnums.SSID_KEY: "Pixel_1030",
+ wutils.WifiEnums.PWD_KEY: "1234567890"
+ }
+
+ def power_tel_tethering_test(self):
+ """ Measure power and throughput during data transmission.
+
+ Starts WiFi tethering in the DUT and connects a second device. Then
+ the iPerf client is hosted in the second android device.
+
+ """
+ # Country Code set to 00 after toggling airplane mode.
+ # We need to set this right before we setup a hotspot
+ # Set country codes on both devices to US to connect to 5GHz
+ country_code = "US"
+ hotspot_dut = self.dut
+ slave_dut = self.android_devices[1]
+ for dut in [hotspot_dut, slave_dut]:
+ self.log.info("Setting Country Code to %s for SN:%s" %
+ (country_code, dut.serial))
+ wutils.set_wifi_country_code(dut, country_code)
+
+ # Setup tethering
+ wutils.start_wifi_tethering(self.dut,
+ self.network[wutils.WifiEnums.SSID_KEY],
+ self.network[wutils.WifiEnums.PWD_KEY],
+ self.wifi_band)
+
+ wutils.wifi_connect(self.android_devices[1],
+ self.network,
+ check_connectivity=False)
+
+ # Start data traffic
+ iperf_helpers = self.start_tel_traffic(self.android_devices[1])
+
+ # Measure power
+ self.collect_power_data()
+
+ # Wait for iPerf to finish
+ time.sleep(self.IPERF_MARGIN + 2)
+
+ # Collect throughput measurement
+ self.iperf_results = self.get_iperf_results(self.android_devices[1],
+ iperf_helpers)
+
+ # Checks if power is below the required threshold.
+ self.pass_fail_check(self.avg_current)
+
+ def setup_test(self):
+ """ Executed before every test case.
+
+ Parses test configuration from the test name and prepares
+ the simulation for measurement.
+ """
+
+ # Call parent method first to setup simulation
+ if not super().setup_test():
+ return False
+
+ try:
+ values = self.consume_parameter(self.PARAM_WIFI_BAND, 1)
+
+ if values[1] == self.PARAM_2G_BAND:
+ self.wifi_band = WIFI_CONFIG_APBAND_2G
+ elif values[1] == self.PARAM_5G_BAND:
+ self.wifi_band = WIFI_CONFIG_APBAND_5G
+ else:
+ raise ValueError()
+ except:
+ self.log.error(
+ "The test name has to include parameter {} followed by "
+ "either {} or {}.".format(self.PARAM_WIFI_BAND,
+ self.PARAM_2G_BAND,
+ self.PARAM_5G_BAND))
+ return False
+
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py
new file mode 100644
index 0000000..72cb7ee
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+
+
+class PowerTelIdleTest(PWCEL.PowerCellularLabBaseTest):
+ """Cellular idle power test.
+
+ Inherits from PowerCellularLabBaseTest. Tests power consumption during
+ cellular idle scenarios to verify the ability to set power consumption
+ to a minimum during connectivity power tests.
+ """
+
+ TIME_SLOT_WINDOW_SECONDS = 1.280
+ FILTER_CURRENT_THRESHOLD = 20
+
+ def power_tel_idle_test(self, filter_results=False):
+ """ Measures power when the device is on LTE RRC idle state.
+
+ Args:
+ filter_results: when True the reported result is filtered to only
+ samples where average power was below a certain threshold.
+ """
+
+ idle_wait_time = self.simulation.rrc_sc_timer + 30
+
+ # Wait for RRC status change to trigger
+ self.cellular_simulator.wait_until_idle_state(idle_wait_time)
+
+ # Measure power
+ samples = self.collect_power_data()
+
+ # If necessary, replace the test result with the filtered metric
+ if filter_results:
+ self.avg_current = self.filter_for_idle_state(samples)
+ self.power_result.metric_value = self.avg_current * self.mon_voltage
+
+ # Check if power measurement is below the required value
+ self.pass_fail_check(self.avg_current)
+
+ def filter_for_idle_state(self, samples):
+ """ Process results and only take an average of time slots that are
+ below a certain threshold.
+
+ Args:
+ samples: a list of tuples in which the first element is a timestamp
+ and the second element is the sampled current in micro amps at that
+ time.
+ """
+ # Calculate the time slot duration in number of samples
+ slot_length = round(self.mon_freq * self.TIME_SLOT_WINDOW_SECONDS)
+
+ # Transform the currents from samples into milli_amps.
+ milli_amps = [sample[1] * 1000 for sample in samples]
+
+ filtered_slot_averages = []
+ for slot in range(int(len(milli_amps) / slot_length)):
+ # Calculate the average in this time slot
+ slot_start = slot_length * slot
+ slot_end = slot_start + slot_length
+ slot_average = sum(milli_amps[slot_start:slot_end]) / slot_length
+ # Only use time slots in which the average was below the threshold
+ if slot_average <= self.FILTER_CURRENT_THRESHOLD:
+ filtered_slot_averages.append(slot_average)
+
+ if filtered_slot_averages:
+ # Calculate the average from all the filtered slots
+ result = sum(filtered_slot_averages) / len(filtered_slot_averages)
+ self.log.info(
+ "The {} s window average current was below {} mA "
+ "for {} s. During that time the average current was {} mA.".
+ format(
+ self.TIME_SLOT_WINDOW_SECONDS,
+ self.FILTER_CURRENT_THRESHOLD,
+ self.TIME_SLOT_WINDOW_SECONDS *
+ len(filtered_slot_averages), result))
+ return result
+ else:
+ self.log.error("The device was not in idle state for the whole "
+ "duration of the test.")
+ return 0
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py
new file mode 100644
index 0000000..c071c5a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as base_test
+
+
+class PowerTelPDCCHTest(base_test.PowerCellularLabBaseTest):
+ """ PDCCH only power test.
+
+ In this test the UE is only listening and decoding the PDCCH channel. """
+ def power_pdcch_test(self):
+ """ Measures power during PDCCH only.
+
+ There's nothing to do here other than starting the power measurement
+ and deciding for pass or fail, as the base class will handle attaching.
+ Requirements for this test are that mac padding is off and that the
+ inactivity timer is not enabled. """
+
+ # Measure power
+ self.collect_power_data()
+
+ # Check if power measurement is within the required values
+ self.pass_fail_check(self.avg_current)
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py
new file mode 100644
index 0000000..adc49b7
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py
@@ -0,0 +1,421 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import time
+import os
+
+import acts_contrib.test_utils.power.PowerBaseTest as PBT
+import acts.controllers.cellular_simulator as simulator
+from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsu
+from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw
+from acts.controllers.rohdeschwarz_lib import cmx500_cellular_simulator as cmx
+from acts.controllers.cellular_lib import AndroidCellularDut
+from acts.controllers.cellular_lib import GsmSimulation
+from acts.controllers.cellular_lib import LteSimulation
+from acts.controllers.cellular_lib import UmtsSimulation
+from acts.controllers.cellular_lib import LteCaSimulation
+from acts.controllers.cellular_lib import LteImsSimulation
+from acts_contrib.test_utils.tel import tel_test_utils as telutils
+from acts_contrib.test_utils.power import plot_utils
+
+
+class PowerCellularLabBaseTest(PBT.PowerBaseTest):
+ """ Base class for Cellular power related tests.
+
+ Inherits from PowerBaseTest so it has methods to collect power measurements.
+ Provides methods to setup and control the Anritsu simulation.
+
+ """
+
+ # List of test name keywords that indicate the RAT to be used
+
+ PARAM_SIM_TYPE_LTE = "lte"
+ PARAM_SIM_TYPE_LTE_CA = "lteca"
+ PARAM_SIM_TYPE_LTE_IMS = "lteims"
+ PARAM_SIM_TYPE_UMTS = "umts"
+ PARAM_SIM_TYPE_GSM = "gsm"
+
+ # Custom files
+ FILENAME_CALIBRATION_TABLE_UNFORMATTED = 'calibration_table_{}.json'
+
+ # Name of the files in the logs directory that will contain test results
+ # and other information in csv format.
+ RESULTS_SUMMARY_FILENAME = 'cellular_power_results.csv'
+ CALIBRATION_TABLE_FILENAME = 'calibration_table.csv'
+
+ def __init__(self, controllers):
+ """ Class initialization.
+
+ Sets class attributes to None.
+ """
+
+ super().__init__(controllers)
+
+ self.simulation = None
+ self.cellular_simulator = None
+ self.calibration_table = {}
+ self.power_results = {}
+
+ def setup_class(self):
+ """ Executed before any test case is started.
+
+ Sets the device to rockbottom and connects to the cellular instrument.
+
+ Returns:
+ False if connecting to the callbox fails.
+ """
+
+ super().setup_class()
+
+ # Unpack test parameters used in this class
+ self.unpack_userparams(md8475_version=None,
+ md8475a_ip_address=None,
+ cmw500_ip=None,
+ cmw500_port=None,
+ cmx500_ip=None,
+ cmx500_port=None,
+ qxdm_logs=None)
+
+ # Load calibration tables
+ filename_calibration_table = (
+ self.FILENAME_CALIBRATION_TABLE_UNFORMATTED.format(
+ self.testbed_name))
+
+ for file in self.custom_files:
+ if filename_calibration_table in file:
+ self.calibration_table = self.unpack_custom_file(file, False)
+ self.log.info('Loading calibration table from ' + file)
+ self.log.debug(self.calibration_table)
+ break
+
+ # Ensure the calibration table only contains non-negative values
+ self.ensure_valid_calibration_table(self.calibration_table)
+
+ # Turn on airplane mode for all devices, as some might
+ # be unused during the test
+ for ad in self.android_devices:
+ telutils.toggle_airplane_mode(self.log, ad, True)
+
+ # Establish a connection with the cellular simulator equipment
+ try:
+ self.cellular_simulator = self.initialize_simulator()
+ except ValueError:
+ self.log.error('No cellular simulator could be selected with the '
+ 'current configuration.')
+ raise
+ except simulator.CellularSimulatorError:
+ self.log.error('Could not initialize the cellular simulator.')
+ raise
+
+ def initialize_simulator(self):
+ """ Connects to Anritsu Callbox and gets handle object.
+
+ Returns:
+ False if a connection with the callbox could not be started
+ """
+
+ if self.md8475_version:
+
+ self.log.info('Selecting Anrtisu MD8475 callbox.')
+
+ # Verify the callbox IP address has been indicated in the configs
+ if not self.md8475a_ip_address:
+ raise RuntimeError(
+ 'md8475a_ip_address was not included in the test '
+ 'configuration.')
+
+ if self.md8475_version == 'A':
+ return anritsu.MD8475CellularSimulator(self.md8475a_ip_address)
+ elif self.md8475_version == 'B':
+ return anritsu.MD8475BCellularSimulator(
+ self.md8475a_ip_address)
+ else:
+ raise ValueError('Invalid MD8475 version.')
+
+ elif self.cmw500_ip or self.cmw500_port:
+
+ for key in ['cmw500_ip', 'cmw500_port']:
+ if not getattr(self, key):
+ raise RuntimeError('The CMW500 cellular simulator '
+ 'requires %s to be set in the '
+ 'config file.' % key)
+
+ return cmw.CMW500CellularSimulator(self.cmw500_ip,
+ self.cmw500_port)
+ elif self.cmx500_ip or self.cmx500_port:
+ for key in ['cmx500_ip', 'cmx500_port']:
+ if not getattr(self, key):
+ raise RuntimeError('The CMX500 cellular simulator '
+ 'requires %s to be set in the '
+ 'config file.' % key)
+
+ return cmx.CMX500CellularSimulator(self.cmx500_ip,
+ self.cmx500_port)
+
+ else:
+ raise RuntimeError(
+ 'The simulator could not be initialized because '
+ 'a callbox was not defined in the configs file.')
+
+ def setup_test(self):
+ """ Executed before every test case.
+
+ Parses parameters from the test name and sets a simulation up according
+ to those values. Also takes care of attaching the phone to the base
+ station. Because starting new simulations and recalibrating takes some
+ time, the same simulation object is kept between tests and is only
+ destroyed and re instantiated in case the RAT is different from the
+ previous tests.
+
+ Children classes need to call the parent method first. This method will
+ create the list self.parameters with the keywords separated by
+ underscores in the test name and will remove the ones that were consumed
+ for the simulation config. The setup_test methods in the children
+ classes can then consume the remaining values.
+ """
+
+ super().setup_test()
+
+ # Get list of parameters from the test name
+ self.parameters = self.current_test_name.split('_')
+
+ # Remove the 'test' keyword
+ self.parameters.remove('test')
+
+ # Decide what type of simulation and instantiate it if needed
+ if self.consume_parameter(self.PARAM_SIM_TYPE_LTE):
+ self.init_simulation(self.PARAM_SIM_TYPE_LTE)
+ elif self.consume_parameter(self.PARAM_SIM_TYPE_LTE_CA):
+ self.init_simulation(self.PARAM_SIM_TYPE_LTE_CA)
+ elif self.consume_parameter(self.PARAM_SIM_TYPE_LTE_IMS):
+ self.init_simulation(self.PARAM_SIM_TYPE_LTE_IMS)
+ elif self.consume_parameter(self.PARAM_SIM_TYPE_UMTS):
+ self.init_simulation(self.PARAM_SIM_TYPE_UMTS)
+ elif self.consume_parameter(self.PARAM_SIM_TYPE_GSM):
+ self.init_simulation(self.PARAM_SIM_TYPE_GSM)
+ else:
+ self.log.error(
+ "Simulation type needs to be indicated in the test name.")
+ return False
+
+ # Changing cell parameters requires the phone to be detached
+ self.simulation.detach()
+
+ # Parse simulation parameters.
+ # This may throw a ValueError exception if incorrect values are passed
+ # or if required arguments are omitted.
+ try:
+ self.simulation.parse_parameters(self.parameters)
+ except ValueError as error:
+ self.log.error(str(error))
+ return False
+
+ # Wait for new params to settle
+ time.sleep(5)
+
+ # Enable QXDM logger if required
+ if self.qxdm_logs:
+ self.log.info('Enabling the QXDM logger.')
+ telutils.set_qxdm_logger_command(self.dut)
+ telutils.start_qxdm_logger(self.dut)
+
+ # Start the simulation. This method will raise an exception if
+ # the phone is unable to attach.
+ self.simulation.start()
+
+ # Make the device go to sleep
+ self.dut.droid.goToSleepNow()
+
+ return True
+
+ def collect_power_data(self):
+ """ Collect power data using base class method and plot result
+ histogram. """
+
+ samples = super().collect_power_data()
+ plot_title = '{}_{}_{}_histogram'.format(
+ self.test_name, self.dut.model, self.dut.build_info['build_id'])
+ plot_utils.monsoon_histogram_plot(samples, self.mon_info.data_path,
+ plot_title)
+ return samples
+
+ def teardown_test(self):
+ """ Executed after every test case, even if it failed or an exception
+ happened.
+
+ Save results to dictionary so they can be displayed after completing
+ the test batch.
+ """
+ super().teardown_test()
+
+ self.power_results[self.test_name] = self.power_result.metric_value
+
+ # If QXDM logging was enabled pull the results
+ if self.qxdm_logs:
+ self.log.info('Stopping the QXDM logger and pulling results.')
+ telutils.stop_qxdm_logger(self.dut)
+ self.dut.get_qxdm_logs()
+
+ def consume_parameter(self, parameter_name, num_values=0):
+ """ Parses a parameter from the test name.
+
+ Allows the test to get parameters from its name. Deletes parameters from
+ the list after consuming them to ensure that they are not used twice.
+
+ Args:
+ parameter_name: keyword to look up in the test name
+ num_values: number of arguments following the parameter name in the
+ test name
+ Returns:
+ A list containing the parameter name and the following num_values
+ arguments.
+ """
+
+ try:
+ i = self.parameters.index(parameter_name)
+ except ValueError:
+ # parameter_name is not set
+ return []
+
+ return_list = []
+
+ try:
+ for j in range(num_values + 1):
+ return_list.append(self.parameters.pop(i))
+ except IndexError:
+ self.log.error(
+ "Parameter {} has to be followed by {} values.".format(
+ parameter_name, num_values))
+ raise ValueError()
+
+ return return_list
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running.
+
+ Stops the simulation and disconnects from the Anritsu Callbox. Then
+ displays the test results.
+
+ """
+ super().teardown_class()
+
+ try:
+ if self.cellular_simulator:
+ self.cellular_simulator.destroy()
+ except simulator.CellularSimulatorError as e:
+ self.log.error('Error while tearing down the callbox controller. '
+ 'Error message: ' + str(e))
+
+ # Log a summary of results
+ results_table_log = 'Results for cellular power tests:'
+
+ for test_name, value in self.power_results.items():
+ results_table_log += '\n{}\t{}'.format(test_name, value)
+
+ # Save this summary to a csv file in the logs directory
+ self.save_summary_to_file()
+
+ self.log.info(results_table_log)
+
+ def save_summary_to_file(self):
+ """ Creates CSV format files with a summary of results.
+
+ This CSV files can be easily imported in a spreadsheet to analyze the
+ results obtained from the tests.
+ """
+
+ # Save a csv file with the power measurements done in all the tests
+
+ path = os.path.join(self.log_path, self.RESULTS_SUMMARY_FILENAME)
+
+ with open(path, 'w') as csvfile:
+ csvfile.write('test,avg_power')
+ for test_name, value in self.power_results.items():
+ csvfile.write('\n{},{}'.format(test_name, value))
+
+ # Save a csv file with the calibration table for each simulation type
+
+ for sim_type in self.calibration_table:
+
+ path = os.path.join(
+ self.log_path, '{}_{}'.format(sim_type,
+ self.CALIBRATION_TABLE_FILENAME))
+
+ with open(path, 'w') as csvfile:
+ csvfile.write('band,dl_pathloss, ul_pathloss')
+ for band, pathloss in self.calibration_table[sim_type].items():
+ csvfile.write('\n{},{},{}'.format(
+ band, pathloss.get('dl', 'Error'),
+ pathloss.get('ul', 'Error')))
+
+ def init_simulation(self, sim_type):
+ """ Starts a new simulation only if needed.
+
+ Only starts a new simulation if type is different from the one running
+ before.
+
+ Args:
+ type: defines the type of simulation to be started.
+ """
+
+ simulation_dictionary = {
+ self.PARAM_SIM_TYPE_LTE: LteSimulation.LteSimulation,
+ self.PARAM_SIM_TYPE_UMTS: UmtsSimulation.UmtsSimulation,
+ self.PARAM_SIM_TYPE_GSM: GsmSimulation.GsmSimulation,
+ self.PARAM_SIM_TYPE_LTE_CA: LteCaSimulation.LteCaSimulation,
+ self.PARAM_SIM_TYPE_LTE_IMS: LteImsSimulation.LteImsSimulation
+ }
+
+ if not sim_type in simulation_dictionary:
+ raise ValueError("The provided simulation type is invalid.")
+
+ simulation_class = simulation_dictionary[sim_type]
+
+ if isinstance(self.simulation, simulation_class):
+ # The simulation object we already have is enough.
+ return
+
+ if self.simulation:
+ # Make sure the simulation is stopped before loading a new one
+ self.simulation.stop()
+
+ # If the calibration table doesn't have an entry for this simulation
+ # type add an empty one
+ if sim_type not in self.calibration_table:
+ self.calibration_table[sim_type] = {}
+
+ cellular_dut = AndroidCellularDut.AndroidCellularDut(
+ self.dut, self.log)
+ # Instantiate a new simulation
+ self.simulation = simulation_class(self.cellular_simulator, self.log,
+ cellular_dut, self.test_params,
+ self.calibration_table[sim_type])
+
+ def ensure_valid_calibration_table(self, calibration_table):
+ """ Ensures the calibration table has the correct structure.
+
+ A valid calibration table is a nested dictionary with non-negative
+ number values
+
+ """
+ if not isinstance(calibration_table, dict):
+ raise TypeError('The calibration table must be a dictionary')
+ for val in calibration_table.values():
+ if isinstance(val, dict):
+ self.ensure_valid_calibration_table(val)
+ elif not isinstance(val, float) and not isinstance(val, int):
+ raise TypeError('Calibration table value must be a number')
+ elif val < 0.0:
+ raise ValueError('Calibration table contains negative values')
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_traffic_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_traffic_power_test.py
new file mode 100644
index 0000000..21e3dcf
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_traffic_power_test.py
@@ -0,0 +1,580 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+
+import scapy.all as scapy
+
+from acts import asserts
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts_contrib.test_utils.power import IperfHelper as IPH
+from acts_contrib.test_utils.power import plot_utils
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+from acts_contrib.test_utils.tel import tel_test_utils as telutils
+
+
+class PowerTelTrafficTest(PWCEL.PowerCellularLabBaseTest):
+ """ Cellular traffic power test.
+
+ Inherits from PowerCellularLabBaseTest. Parses config specific
+ to this kind of test. Contains methods to start data traffic
+ between a local instance of iPerf and one running in the dut.
+
+ """
+
+ # Keywords for test name parameters
+ PARAM_DIRECTION = 'direction'
+ PARAM_DIRECTION_UL = 'ul'
+ PARAM_DIRECTION_DL = 'dl'
+ PARAM_DIRECTION_DL_UL = 'dlul'
+ PARAM_BANDWIDTH_LIMIT = 'blimit'
+
+ # Iperf waiting time
+ IPERF_MARGIN = 10
+
+ def __init__(self, controllers):
+ """ Class initialization.
+
+ Sets test parameters to initial values.
+ """
+
+ super().__init__(controllers)
+
+ # These variables are passed to iPerf when starting data
+ # traffic with the -b parameter to limit throughput on
+ # the application layer.
+ self.bandwidth_limit_dl = None
+ self.bandwidth_limit_ul = None
+
+ # Throughput obtained from iPerf
+ self.iperf_results = {}
+
+ # Blackbox metrics loggers
+
+ self.dl_tput_logger = BlackboxMetricLogger.for_test_case(
+ metric_name='avg_dl_tput')
+ self.ul_tput_logger = BlackboxMetricLogger.for_test_case(
+ metric_name='avg_ul_tput')
+
+ def setup_class(self):
+ super().setup_class()
+
+ # Unpack test parameters used in this class
+ self.unpack_userparams(tcp_window_fraction=0, tcp_dumps=False)
+
+ # Verify that at least one PacketSender controller has been initialized
+ if not hasattr(self, 'packet_senders'):
+ raise RuntimeError('At least one packet sender controller needs '
+ 'to be defined in the test config files.')
+
+ def setup_test(self):
+ """ Executed before every test case.
+
+ Parses test configuration from the test name and prepares
+ the simulation for measurement.
+ """
+
+ # Reset results at the start of the test
+ self.iperf_results = {}
+
+ # Call parent method first to setup simulation
+ if not super().setup_test():
+ return False
+
+ # Traffic direction
+
+ values = self.consume_parameter(self.PARAM_DIRECTION, 1)
+
+ if not values:
+ self.log.warning("The keyword {} was not included in the testname "
+ "parameters. Setting to {} by default.".format(
+ self.PARAM_DIRECTION,
+ self.PARAM_DIRECTION_DL_UL))
+ self.traffic_direction = self.PARAM_DIRECTION_DL_UL
+ elif values[1] in [
+ self.PARAM_DIRECTION_DL, self.PARAM_DIRECTION_UL,
+ self.PARAM_DIRECTION_DL_UL
+ ]:
+ self.traffic_direction = values[1]
+ else:
+ self.log.error("The test name has to include parameter {} "
+ "followed by {}/{}/{}.".format(
+ self.PARAM_DIRECTION, self.PARAM_DIRECTION_UL,
+ self.PARAM_DIRECTION_DL,
+ self.PARAM_DIRECTION_DL_UL))
+ return False
+
+ # Bandwidth limit
+
+ values = self.consume_parameter(self.PARAM_BANDWIDTH_LIMIT, 2)
+
+ if values:
+ self.bandwidth_limit_dl = values[1]
+ self.bandwidth_limit_ul = values[2]
+ else:
+ self.bandwidth_limit_dl = 0
+ self.bandwidth_limit_ul = 0
+ self.log.error(
+ "No bandwidth limit was indicated in the test parameters. "
+ "Setting to default value of 0 (no limit to bandwidth). To set "
+ "a different value include parameter '{}' followed by two "
+ "strings indicating downlink and uplink bandwidth limits for "
+ "iPerf.".format(self.PARAM_BANDWIDTH_LIMIT))
+
+ # No errors when parsing parameters
+ return True
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ """
+
+ super().teardown_test()
+
+ # Log the throughput values to Blackbox
+ self.dl_tput_logger.metric_value = self.iperf_results.get('DL', 0)
+ self.ul_tput_logger.metric_value = self.iperf_results.get('UL', 0)
+
+ # Log the throughput values to Spanner
+ self.power_logger.set_dl_tput(self.iperf_results.get('DL', 0))
+ self.power_logger.set_ul_tput(self.iperf_results.get('UL', 0))
+
+ try:
+ dl_max_throughput = self.simulation.maximum_downlink_throughput()
+ ul_max_throughput = self.simulation.maximum_uplink_throughput()
+ self.power_logger.set_dl_tput_threshold(dl_max_throughput)
+ self.power_logger.set_ul_tput_threshold(ul_max_throughput)
+ except NotImplementedError as e:
+ self.log.error("%s Downlink/uplink thresholds will not be "
+ "logged in the power proto" % e)
+
+ for ips in self.iperf_servers:
+ ips.stop()
+
+ def power_tel_traffic_test(self):
+ """ Measures power and throughput during data transmission.
+
+ Measurement step in this test. Starts iPerf client in the DUT and then
+ initiates power measurement. After that, DUT is connected again and
+ the result from iPerf is collected. Pass or fail is decided with a
+ threshold value.
+ """
+
+ # Start data traffic
+ iperf_helpers = self.start_tel_traffic(self.dut)
+
+ # Measure power
+ self.collect_power_data()
+
+ # Wait for iPerf to finish
+ time.sleep(self.IPERF_MARGIN + 2)
+
+ # Collect throughput measurement
+ self.iperf_results = self.get_iperf_results(self.dut, iperf_helpers)
+
+ # Check if power measurement is below the required value
+ self.pass_fail_check(self.avg_current)
+
+ return self.avg_current, self.iperf_results
+
+ def get_iperf_results(self, device, iperf_helpers):
+ """ Pulls iperf results from the device.
+
+ Args:
+ device: the device from which iperf results need to be pulled.
+
+ Returns:
+ a dictionary containing DL/UL throughput in Mbit/s.
+ """
+
+ # Pull TCP logs if enabled
+ if self.tcp_dumps:
+ self.log.info('Pulling TCP dumps.')
+ telutils.stop_adb_tcpdump(self.dut)
+ telutils.get_tcpdump_log(self.dut)
+
+ throughput = {}
+
+ for iph in iperf_helpers:
+
+ self.log.info("Getting {} throughput results.".format(
+ iph.traffic_direction))
+
+ iperf_result = iph.process_iperf_results(device, self.log,
+ self.iperf_servers,
+ self.test_name)
+
+ throughput[iph.traffic_direction] = iperf_result
+
+ return throughput
+
+ def check_throughput_results(self, iperf_results):
+ """ Checks throughput results.
+
+ Compares the obtained throughput with the expected value
+ provided by the simulation class.
+
+ """
+
+ for direction, throughput in iperf_results.items():
+ try:
+ if direction == "UL":
+ expected_t = self.simulation.maximum_uplink_throughput()
+ elif direction == "DL":
+ expected_t = self.simulation.maximum_downlink_throughput()
+ else:
+ raise RuntimeError("Unexpected traffic direction value.")
+ except NotImplementedError:
+ # Some simulation classes might not have implemented the max
+ # throughput calculation yet.
+ self.log.debug("Expected throughput is not available for the "
+ "current simulation class.")
+ else:
+
+ self.log.info(
+ "The expected {} throughput is {} Mbit/s.".format(
+ direction, expected_t))
+ asserts.assert_true(
+ 0.90 < throughput / expected_t < 1.10,
+ "{} throughput differed more than 10% from the expected "
+ "value! ({}/{} = {})".format(
+ direction, round(throughput, 3), round(expected_t, 3),
+ round(throughput / expected_t, 3)))
+
+ def pass_fail_check(self, average_current=None):
+ """ Checks power consumption and throughput.
+
+ Uses the base class method to check power consumption. Also, compares
+ the obtained throughput with the expected value provided by the
+ simulation class.
+
+ """
+ self.check_throughput_results(self.iperf_results)
+ super().pass_fail_check(average_current)
+
+ def start_tel_traffic(self, client_host):
+ """ Starts iPerf in the indicated device and initiates traffic.
+
+ Starts the required iperf clients and servers according to the traffic
+ pattern config in the current test.
+
+ Args:
+ client_host: device handler in which to start the iperf client.
+
+ Returns:
+ A list of iperf helpers.
+ """
+ # The iPerf server is hosted in this computer
+ self.iperf_server_address = scapy.get_if_addr(
+ self.packet_senders[0].interface)
+
+ self.log.info('Testing IP connectivity with ping.')
+ if not utils.adb_shell_ping(
+ client_host, count=10, dest_ip=self.iperf_server_address):
+ raise RuntimeError('Ping between DUT and host failed.')
+
+ # Start iPerf traffic
+ iperf_helpers = []
+
+ # If the tcp_window_fraction parameter was set, calculate the TCP
+ # window size as a fraction of the peak throughput.
+ ul_tcp_window = None
+ dl_tcp_window = None
+ if self.tcp_window_fraction == 0:
+ self.log.info("tcp_window_fraction was not indicated. "
+ "Disabling fixed TCP window.")
+ else:
+ try:
+ max_dl_tput = self.simulation.maximum_downlink_throughput()
+ max_ul_tput = self.simulation.maximum_uplink_throughput()
+ dl_tcp_window = max_dl_tput / self.tcp_window_fraction
+ ul_tcp_window = max_ul_tput / self.tcp_window_fraction
+ except NotImplementedError:
+ self.log.error("Maximum downlink/uplink throughput method not "
+ "implemented for %s." %
+ type(self.simulation).__name__)
+
+ if self.traffic_direction in [
+ self.PARAM_DIRECTION_DL, self.PARAM_DIRECTION_DL_UL
+ ]:
+ # Downlink traffic
+ iperf_helpers.append(
+ self.start_iperf_traffic(client_host,
+ server_idx=len(iperf_helpers),
+ traffic_direction='DL',
+ window=dl_tcp_window,
+ bandwidth=self.bandwidth_limit_dl))
+
+ if self.traffic_direction in [
+ self.PARAM_DIRECTION_UL, self.PARAM_DIRECTION_DL_UL
+ ]:
+ # Uplink traffic
+ iperf_helpers.append(
+ self.start_iperf_traffic(client_host,
+ server_idx=len(iperf_helpers),
+ traffic_direction='UL',
+ window=ul_tcp_window,
+ bandwidth=self.bandwidth_limit_ul))
+
+ # Enable TCP logger.
+ if self.tcp_dumps:
+ self.log.info('Enabling TCP logger.')
+ telutils.start_adb_tcpdump(self.dut)
+
+ return iperf_helpers
+
+ def start_iperf_traffic(self,
+ client_host,
+ server_idx,
+ traffic_direction,
+ bandwidth=0,
+ window=None):
+ """Starts iPerf data traffic.
+
+ Starts an iperf client in an android device and a server locally.
+
+ Args:
+ client_host: device handler in which to start the iperf client
+ server_idx: id of the iperf server to connect to
+ traffic_direction: has to be either 'UL' or 'DL'
+ bandwidth: bandwidth limit for data traffic
+ window: the tcp window. if None, no window will be passed to iperf
+
+ Returns:
+ An IperfHelper object for the started client/server pair.
+ """
+
+ # Start the server locally
+ self.iperf_servers[server_idx].start()
+
+ config = {
+ 'traffic_type': 'TCP',
+ 'duration':
+ self.mon_duration + self.mon_offset + self.IPERF_MARGIN,
+ 'start_meas_time': 4,
+ 'server_idx': server_idx,
+ 'port': self.iperf_servers[server_idx].port,
+ 'traffic_direction': traffic_direction,
+ 'window': window
+ }
+
+ # If bandwidth is equal to zero then no bandwidth requirements are set
+ if bandwidth > 0:
+ config['bandwidth'] = bandwidth
+
+ iph = IPH.IperfHelper(config)
+
+ # Start the client in the android device
+ client_host.adb.shell_nb(
+ "nohup >/dev/null 2>&1 sh -c 'iperf3 -c {} {} "
+ "&'".format(self.iperf_server_address, iph.iperf_args))
+
+ self.log.info('{} iPerf started on port {}.'.format(
+ traffic_direction, iph.port))
+
+ return iph
+
+
+class PowerTelRvRTest(PowerTelTrafficTest):
+ """ Gets Range vs Rate curves while measuring power consumption.
+
+ Uses PowerTelTrafficTest as a base class.
+ """
+
+ # Test name configuration keywords
+ PARAM_SWEEP = "sweep"
+ PARAM_SWEEP_UPLINK = "uplink"
+ PARAM_SWEEP_DOWNLINK = "downlink"
+
+ # Sweep values. Need to be set before starting test by test
+ # function or child class.
+ downlink_power_sweep = None
+ uplink_power_sweep = None
+
+ def setup_test(self):
+ """ Executed before every test case.
+
+ Parses test configuration from the test name and prepares
+ the simulation for measurement.
+ """
+
+ # Call parent method first to setup simulation
+ if not super().setup_test():
+ return False
+
+ # Get which power value to sweep from config
+
+ try:
+ values = self.consume_parameter(self.PARAM_SWEEP, 1)
+
+ if values[1] == self.PARAM_SWEEP_UPLINK:
+ self.sweep = self.PARAM_SWEEP_UPLINK
+ elif values[1] == self.PARAM_SWEEP_DOWNLINK:
+ self.sweep = self.PARAM_SWEEP_DOWNLINK
+ else:
+ raise ValueError()
+ except:
+ self.log.error(
+ "The test name has to include parameter {} followed by "
+ "either {} or {}.".format(self.PARAM_SWEEP,
+ self.PARAM_SWEEP_DOWNLINK,
+ self.PARAM_SWEEP_UPLINK))
+ return False
+
+ return True
+
+ def power_tel_rvr_test(self):
+ """ Main function for the RvR test.
+
+ Produces the RvR curve according to the indicated sweep values.
+ """
+
+ if self.sweep == self.PARAM_SWEEP_DOWNLINK:
+ sweep_range = self.downlink_power_sweep
+ elif self.sweep == self.PARAM_SWEEP_UPLINK:
+ sweep_range = self.uplink_power_sweep
+
+ current = []
+ throughput = []
+
+ for pw in sweep_range:
+
+ if self.sweep == self.PARAM_SWEEP_DOWNLINK:
+ self.simulation.set_downlink_rx_power(self.simulation.bts1, pw)
+ elif self.sweep == self.PARAM_SWEEP_UPLINK:
+ self.simulation.set_uplink_tx_power(self.simulation.bts1, pw)
+
+ i, t = self.power_tel_traffic_test()
+ self.log.info("---------------------")
+ self.log.info("{} -- {} --".format(self.sweep, pw))
+ self.log.info("{} ----- {}".format(i, t[0]))
+ self.log.info("---------------------")
+
+ current.append(i)
+ throughput.append(t[0])
+
+ print(sweep_range)
+ print(current)
+ print(throughput)
+
+
+class PowerTelTxPowerSweepTest(PowerTelTrafficTest):
+ """ Gets Average Current vs Tx Power plot.
+
+ Uses PowerTelTrafficTest as a base class.
+ """
+
+ # Test config keywords
+ KEY_TX_STEP = 'step'
+ KEY_UP_TOLERANCE = 'up_tolerance'
+ KEY_DOWN_TOLERANCE = 'down_tolerance'
+
+ # Test name parameters
+ PARAM_TX_POWER_SWEEP = 'sweep'
+
+ def setup_class(self):
+ super().setup_class()
+ self.unpack_userparams(
+ [self.KEY_TX_STEP, self.KEY_UP_TOLERANCE, self.KEY_DOWN_TOLERANCE])
+
+ def setup_test(self):
+ """ Executed before every test case.
+
+ Parses test configuration from the test name and prepares
+ the simulation for measurement.
+ """
+ # Call parent method first to setup simulation
+ if not super().setup_test():
+ return False
+
+ # Determine power range to sweep from test case params
+ try:
+ values = self.consume_parameter(self.PARAM_TX_POWER_SWEEP, 2)
+
+ if len(values) == 3:
+ self.start_dbm = int(values[1].replace('n', '-'))
+ self.end_dbm = int(values[2].replace('n', '-'))
+ else:
+ raise ValueError('Not enough params specified for sweep.')
+ except ValueError as e:
+ self.log.error("Unable to parse test param sweep: {}".format(e))
+ return False
+
+ return True
+
+ def pass_fail_check(self, currents, txs, iperf_results):
+ """ Compares the obtained throughput with the expected
+ value provided by the simulation class. Also, ensures
+ consecutive currents do not increase or decrease beyond
+ specified tolerance
+ """
+ for iperf_result in iperf_results:
+ self.check_throughput_results(iperf_result)
+
+ # x = reference current value, y = next current value, i = index of x
+ for i, (x, y) in enumerate(zip(currents[::], currents[1::])):
+ measured_change = (y - x) / x * 100
+ asserts.assert_true(
+ -self.down_tolerance < measured_change < self.up_tolerance,
+ "Current went from {} to {} ({}%) between {} dBm and {} dBm. "
+ "Tolerance range: -{}% to {}%".format(x, y, measured_change,
+ txs[i], txs[i + 1],
+ self.down_tolerance,
+ self.up_tolerance))
+
+ def create_power_plot(self, currents, txs):
+ """ Creates average current vs tx power plot
+ """
+ title = '{}_{}_{}_tx_power_sweep'.format(
+ self.test_name, self.dut.model, self.dut.build_info['build_id'])
+
+ plot_utils.monsoon_tx_power_sweep_plot(self.mon_info.data_path, title,
+ currents, txs)
+
+ def power_tel_tx_sweep(self):
+ """ Main function for the Tx power sweep test.
+
+ Produces a plot of power consumption vs tx power
+ """
+ currents = []
+ txs = []
+ iperf_results = []
+ for tx in range(self.start_dbm, self.end_dbm + 1, self.step):
+
+ self.simulation.set_uplink_tx_power(tx)
+
+ iperf_helpers = self.start_tel_traffic(self.dut)
+
+ # Measure power
+ self.collect_power_data()
+
+ # Wait for iPerf to finish
+ time.sleep(self.IPERF_MARGIN + 2)
+
+ # Collect and check throughput measurement
+ iperf_result = self.get_iperf_results(self.dut, iperf_helpers)
+
+ currents.append(self.avg_current)
+
+ # Get the actual Tx power as measured from the callbox side
+ measured_tx = self.simulation.get_measured_ul_power()
+
+ txs.append(measured_tx)
+ iperf_results.append(iperf_result)
+
+ self.create_power_plot(currents, txs)
+ self.pass_fail_check(currents, txs, iperf_results)
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py
new file mode 100644
index 0000000..802d8cc
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
+
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call, hangup_call, set_phone_silent_mode
+
+
+class PowerTelVoiceCallTest(PWCEL.PowerCellularLabBaseTest):
+ """ Voice call power test.
+
+ Inherits from PowerCellularLabBaseTest. Contains methods to initiate
+ a voice call from the DUT and pick up from the callbox.
+
+ """
+
+ # Time for the callbox to pick up the call
+ CALL_SETTLING_TIME = 10
+
+ def setup_class(self):
+ """ Executed only once when initializing the class.
+
+ Configs the callbox to pick up the call automatically and
+ sets the phone to silent mode.
+ """
+
+ super().setup_class()
+
+ # Make the callbox pick up the call automatically
+ self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+ self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON, 0)
+
+ # Set voice call volume to minimum
+ set_phone_silent_mode(self.log, self.dut)
+
+ def power_voice_call_test(self):
+ """ Measures power during a voice call.
+
+ Measurement step in this test. Starts the voice call and
+ initiates power measurement. Pass or fail is decided with a
+ threshold value.
+ """
+
+ # Initiate the voice call
+ initiate_call(self.log, self.dut, "+11112223333")
+
+ # Wait for the callbox to pick up
+ time.sleep(self.CALL_SETTLING_TIME)
+
+ # Mute the call
+ self.dut.droid.telecomCallMute()
+
+ # Turn of screen
+ self.dut.droid.goToSleepNow()
+
+ # Measure power
+ self.collect_power_data()
+
+ # End the call
+ hangup_call(self.log, self.dut)
+
+ # Check if power measurement is within the required values
+ self.pass_fail_check(self.avg_current)
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py
new file mode 100644
index 0000000..986878d
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+
+import acts_contrib.test_utils.tel.anritsu_utils as anritsu_utils
+import acts.controllers.anritsu_lib.md8475a as md8475a
+
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call, hangup_call, set_phone_silent_mode
+
+
+class PowerTelVoLTECallTest(PWCEL.PowerCellularLabBaseTest):
+ """ VoLTE call power test.
+
+ Inherits from PowerCellularLabBaseTest. Contains methods to initiate
+ a voice call from the IMS server and pick up on the UE.
+
+ """
+
+ # Waiting time before trying to pick up from the phone
+ CALL_INITIATING_TIME = 10
+
+ def setup_class(self):
+ """ Executed only once when initializing the class. """
+
+ super().setup_class()
+
+ # Set voice call volume to minimum
+ set_phone_silent_mode(self.log, self.dut)
+
+ def power_volte_call_test(self):
+ """ Measures power during a VoLTE call.
+
+ Measurement step in this test. Starts the voice call and
+ initiates power measurement. Pass or fail is decided with a
+ threshold value. """
+
+ # Initiate the voice call
+ self.anritsu.ims_cscf_call_action(
+ anritsu_utils.DEFAULT_IMS_VIRTUAL_NETWORK_ID,
+ md8475a.ImsCscfCall.MAKE.value)
+
+ # Wait for the call to be started
+ time.sleep(self.CALL_INITIATING_TIME)
+
+ # Pickup the call
+ self.dut.adb.shell('input keyevent KEYCODE_CALL')
+
+ # Mute the call
+ self.dut.droid.telecomCallMute()
+
+ # Turn of screen
+ self.dut.droid.goToSleepNow()
+
+ # Measure power
+ self.collect_power_data()
+
+ # End the call
+ hangup_call(self.log, self.dut)
+
+ # Check if power measurement is within the required values
+ self.pass_fail_check()
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/__init__.py b/acts_tests/acts_contrib/test_utils/power/loggers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/power_metric_logger.py b/acts_tests/acts_contrib/test_utils/power/loggers/power_metric_logger.py
new file mode 100644
index 0000000..89deb5c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/power_metric_logger.py
@@ -0,0 +1,88 @@
+# /usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import os
+import acts_contrib.test_utils.power.loggers.protos.power_metric_pb2 as power_protos
+
+from acts.metrics.core import ProtoMetric
+from acts.metrics.logger import MetricLogger
+
+# Initializes the path to the protobuf
+PROTO_PATH = os.path.join(os.path.dirname(__file__), 'protos',
+ 'power_metric.proto')
+
+
+class PowerMetricLogger(MetricLogger):
+ """A logger for gathering Power test metrics
+
+ Attributes:
+ proto: Module used to store Power metrics in a proto
+ """
+ def __init__(self, event):
+ super().__init__(event=event)
+ self.proto = power_protos.PowerMetric()
+
+ def set_dl_tput(self, avg_dl_tput):
+ self.proto.cellular_metric.avg_dl_tput = avg_dl_tput
+
+ def set_ul_tput(self, avg_ul_tput):
+ self.proto.cellular_metric.avg_ul_tput = avg_ul_tput
+
+ def set_dl_tput_threshold(self, avg_dl_tput_threshold):
+ self.proto.cellular_metric.avg_dl_tput_threshold = avg_dl_tput_threshold
+
+ def set_ul_tput_threshold(self, avg_ul_tput_threshold):
+ self.proto.cellular_metric.avg_ul_tput_threshold = avg_ul_tput_threshold
+
+ def set_avg_power(self, avg_power):
+ self.proto.avg_power = avg_power
+
+ def set_avg_current(self, avg_current):
+ self.proto.avg_current = avg_current
+
+ def set_voltage(self, voltage):
+ self.proto.voltage = voltage
+
+ def set_testbed(self, testbed):
+ self.proto.testbed = testbed
+
+ def set_branch(self, branch):
+ self.proto.branch = branch
+
+ def set_build_id(self, build_id):
+ self.proto.build_id = build_id
+
+ def set_incremental_build_id(self, incremental_build_id):
+ self.proto.incremental_build_id = incremental_build_id
+
+ def set_target(self, target):
+ self.proto.target = target
+
+ def set_test_suite_display_name(self, test_suite_display_name):
+ self.proto.test_suite_display_name = test_suite_display_name
+
+ def set_test_case_display_name(self, test_case_display_name):
+ self.proto.test_case_display_name = test_case_display_name
+
+ def set_avg_current_threshold(self, avg_current_threshold):
+ self.proto.avg_current_threshold = avg_current_threshold
+
+ def set_pass_fail_status(self, status):
+ self.proto.pass_fail_status = status
+
+ def end(self, event):
+ metric = ProtoMetric(name='spanner_power_metric', data=self.proto)
+ return self.publisher.publish(metric)
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/protos/__init__.py b/acts_tests/acts_contrib/test_utils/power/loggers/protos/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/protos/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric.proto b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric.proto
new file mode 100644
index 0000000..0e64bf8
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric.proto
@@ -0,0 +1,39 @@
+/* Note: If making any changes to this file be sure to generate a new
+ compiled *_pb2.py file by running the following command from the same dir:
+ $ protoc -I=. --python_out=. power_metric.proto
+
+ Be sure that you are compiling with protoc 3.4.0
+
+ More info can be found at:
+ https://developers.google.com/protocol-buffers/docs/pythontutorial
+*/
+
+syntax = "proto2";
+
+package wireless.android.platform.testing.power.metrics;
+
+/*
+ Power metrics to be uploaded to Spanner
+*/
+message PowerMetric {
+ optional float avg_power = 1; // Required
+ optional string testbed = 2; // Required
+ optional PowerCellularMetric cellular_metric = 3;
+ optional string branch = 4;
+ optional string build_id = 5;
+ optional string target = 6;
+ optional float avg_current = 7;
+ optional float voltage = 8;
+ optional string test_suite_display_name = 9;
+ optional string test_case_display_name = 10;
+ optional string incremental_build_id = 11;
+ optional float avg_current_threshold = 12;
+ optional string pass_fail_status = 13;
+}
+
+message PowerCellularMetric {
+ optional float avg_dl_tput = 1;
+ optional float avg_ul_tput = 2;
+ optional float avg_dl_tput_threshold = 3;
+ optional float avg_ul_tput_threshold = 4;
+}
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py
new file mode 100644
index 0000000..d1682cc
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: power_metric.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='power_metric.proto',
+ package='wireless.android.platform.testing.power.metrics',
+ syntax='proto2',
+ serialized_options=None,
+ create_key=_descriptor._internal_create_key,
+ serialized_pb=b'\n\x12power_metric.proto\x12/wireless.android.platform.testing.power.metrics\"\x80\x03\n\x0bPowerMetric\x12\x11\n\tavg_power\x18\x01 \x01(\x02\x12\x0f\n\x07testbed\x18\x02 \x01(\t\x12]\n\x0f\x63\x65llular_metric\x18\x03 \x01(\x0b\x32\x44.wireless.android.platform.testing.power.metrics.PowerCellularMetric\x12\x0e\n\x06\x62ranch\x18\x04 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x05 \x01(\t\x12\x0e\n\x06target\x18\x06 \x01(\t\x12\x13\n\x0b\x61vg_current\x18\x07 \x01(\x02\x12\x0f\n\x07voltage\x18\x08 \x01(\x02\x12\x1f\n\x17test_suite_display_name\x18\t \x01(\t\x12\x1e\n\x16test_case_display_name\x18\n \x01(\t\x12\x1c\n\x14incremental_build_id\x18\x0b \x01(\t\x12\x1d\n\x15\x61vg_current_threshold\x18\x0c \x01(\x02\x12\x18\n\x10pass_fail_status\x18\r \x01(\t\"}\n\x13PowerCellularMetric\x12\x13\n\x0b\x61vg_dl_tput\x18\x01 \x01(\x02\x12\x13\n\x0b\x61vg_ul_tput\x18\x02 \x01(\x02\x12\x1d\n\x15\x61vg_dl_tput_threshold\x18\x03 \x01(\x02\x12\x1d\n\x15\x61vg_ul_tput_threshold\x18\x04 \x01(\x02'
+)
+
+
+
+
+_POWERMETRIC = _descriptor.Descriptor(
+ name='PowerMetric',
+ full_name='wireless.android.platform.testing.power.metrics.PowerMetric',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='avg_power', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_power', index=0,
+ number=1, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='testbed', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.testbed', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='cellular_metric', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.cellular_metric', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='branch', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.branch', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='build_id', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.build_id', index=4,
+ number=5, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='target', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.target', index=5,
+ number=6, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='avg_current', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_current', index=6,
+ number=7, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='voltage', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.voltage', index=7,
+ number=8, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='test_suite_display_name', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.test_suite_display_name', index=8,
+ number=9, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='test_case_display_name', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.test_case_display_name', index=9,
+ number=10, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='incremental_build_id', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.incremental_build_id', index=10,
+ number=11, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='avg_current_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_current_threshold', index=11,
+ number=12, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='pass_fail_status', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.pass_fail_status', index=12,
+ number=13, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=b"".decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=72,
+ serialized_end=456,
+)
+
+
+_POWERCELLULARMETRIC = _descriptor.Descriptor(
+ name='PowerCellularMetric',
+ full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='avg_dl_tput', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_dl_tput', index=0,
+ number=1, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='avg_ul_tput', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_ul_tput', index=1,
+ number=2, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='avg_dl_tput_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_dl_tput_threshold', index=2,
+ number=3, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='avg_ul_tput_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_ul_tput_threshold', index=3,
+ number=4, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=458,
+ serialized_end=583,
+)
+
+_POWERMETRIC.fields_by_name['cellular_metric'].message_type = _POWERCELLULARMETRIC
+DESCRIPTOR.message_types_by_name['PowerMetric'] = _POWERMETRIC
+DESCRIPTOR.message_types_by_name['PowerCellularMetric'] = _POWERCELLULARMETRIC
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+PowerMetric = _reflection.GeneratedProtocolMessageType('PowerMetric', (_message.Message,), {
+ 'DESCRIPTOR' : _POWERMETRIC,
+ '__module__' : 'power_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.power.metrics.PowerMetric)
+ })
+_sym_db.RegisterMessage(PowerMetric)
+
+PowerCellularMetric = _reflection.GeneratedProtocolMessageType('PowerCellularMetric', (_message.Message,), {
+ 'DESCRIPTOR' : _POWERCELLULARMETRIC,
+ '__module__' : 'power_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.power.metrics.PowerCellularMetric)
+ })
+_sym_db.RegisterMessage(PowerCellularMetric)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/power/plot_utils.py b/acts_tests/acts_contrib/test_utils/power/plot_utils.py
new file mode 100644
index 0000000..aed43cc
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/plot_utils.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import logging
+import numpy
+import math
+
+from bokeh.layouts import layout
+from bokeh.models import CustomJS, ColumnDataSource
+from bokeh.models import tools as bokeh_tools
+from bokeh.models.widgets import DataTable, TableColumn
+from bokeh.plotting import figure, output_file, save
+
+
+def current_waveform_plot(samples, voltage, dest_path, plot_title):
+ """Plot the current data using bokeh interactive plotting tool.
+
+ Plotting power measurement data with bokeh to generate interactive plots.
+ You can do interactive data analysis on the plot after generating with the
+ provided widgets, which make the debugging much easier. To realize that,
+ bokeh callback java scripting is used. View a sample html output file:
+ https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA
+
+ Args:
+ samples: a list of tuples in which the first element is a timestamp and
+ the second element is the sampled current in milli amps at that time.
+ voltage: the voltage that was used during the measurement.
+ dest_path: destination path.
+ plot_title: a filename and title for the plot.
+ Returns:
+ plot: the plotting object of bokeh, optional, will be needed if multiple
+ plots will be combined to one html file.
+ dt: the datatable object of bokeh, optional, will be needed if multiple
+ datatables will be combined to one html file.
+ """
+ logging.info('Plotting the power measurement data.')
+
+ time_relative = [sample[0] for sample in samples]
+ duration = time_relative[-1] - time_relative[0]
+ current_data = [sample[1] * 1000 for sample in samples]
+ avg_current = sum(current_data) / len(current_data)
+
+ color = ['navy'] * len(samples)
+
+ # Preparing the data and source link for bokehn java callback
+ source = ColumnDataSource(
+ data=dict(x=time_relative, y=current_data, color=color))
+ s2 = ColumnDataSource(
+ data=dict(a=[duration],
+ b=[round(avg_current, 2)],
+ c=[round(avg_current * voltage, 2)],
+ d=[round(avg_current * voltage * duration, 2)],
+ e=[round(avg_current * duration, 2)]))
+ # Setting up data table for the output
+ columns = [
+ TableColumn(field='a', title='Total Duration (s)'),
+ TableColumn(field='b', title='Average Current (mA)'),
+ TableColumn(field='c', title='Average Power (4.2v) (mW)'),
+ TableColumn(field='d', title='Average Energy (mW*s)'),
+ TableColumn(field='e', title='Normalized Average Energy (mA*s)')
+ ]
+ dt = DataTable(source=s2,
+ columns=columns,
+ width=1300,
+ height=60,
+ editable=True)
+
+ output_file(os.path.join(dest_path, plot_title + '.html'))
+ tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save'
+ # Create a new plot with the datatable above
+ plot = figure(plot_width=1300,
+ plot_height=700,
+ title=plot_title,
+ tools=tools)
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width'))
+ plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height'))
+ plot.line('x', 'y', source=source, line_width=2)
+ plot.circle('x', 'y', source=source, size=0.5, fill_color='color')
+ plot.xaxis.axis_label = 'Time (s)'
+ plot.yaxis.axis_label = 'Current (mA)'
+ plot.title.text_font_size = {'value': '15pt'}
+
+ # Callback JavaScript
+ source.selected.js_on_change(
+ "indices",
+ CustomJS(args=dict(source=source, mytable=dt),
+ code="""
+ const inds = source.selected.indices;
+ const d1 = source.data;
+ const d2 = mytable.source.data;
+ var ym = 0
+ var ts = 0
+ var min=d1['x'][inds[0]]
+ var max=d1['x'][inds[0]]
+ d2['a'] = []
+ d2['b'] = []
+ d2['c'] = []
+ d2['d'] = []
+ d2['e'] = []
+ if (inds.length==0) {return;}
+ for (var i = 0; i < inds.length; i++) {
+ ym += d1['y'][inds[i]]
+ d1['color'][inds[i]] = "red"
+ if (d1['x'][inds[i]] < min) {
+ min = d1['x'][inds[i]]}
+ if (d1['x'][inds[i]] > max) {
+ max = d1['x'][inds[i]]}
+ }
+ ym /= inds.length
+ ts = max - min
+ d2['a'].push(Math.round(ts*1000.0)/1000.0)
+ d2['b'].push(Math.round(ym*100.0)/100.0)
+ d2['c'].push(Math.round(ym*4.2*100.0)/100.0)
+ d2['d'].push(Math.round(ym*4.2*ts*100.0)/100.0)
+ d2['e'].push(Math.round(ym*ts*100.0)/100.0)
+ source.change.emit();
+ mytable.change.emit();
+ """))
+
+ # Layout the plot and the datatable bar
+ save(layout([[dt], [plot]]))
+ return plot, dt
+
+
+def monsoon_histogram_plot(samples, dest_path, plot_title):
+ """ Creates a histogram from a monsoon result object.
+
+ Args:
+ samples: a list of tuples in which the first element is a timestamp and
+ the second element is the sampled current in milli amps at that time.
+ dest_path: destination path
+ plot_title: a filename and title for the plot.
+ Returns:
+ a tuple of arrays containing the values of the histogram and the
+ bin edges.
+ """
+ milli_amps = [sample[1] * 1000 for sample in samples]
+ hist, edges = numpy.histogram(milli_amps,
+ bins=math.ceil(max(milli_amps)),
+ range=(0, max(milli_amps)))
+
+ output_file(os.path.join(dest_path, plot_title + '.html'))
+
+ plot = figure(title=plot_title,
+ y_axis_type='log',
+ background_fill_color='#fafafa')
+
+ plot.quad(top=hist,
+ bottom=0,
+ left=edges[:-1],
+ right=edges[1:],
+ fill_color='navy')
+
+ plot.y_range.start = 0
+ plot.xaxis.axis_label = 'Instantaneous current [mA]'
+ plot.yaxis.axis_label = 'Count'
+ plot.grid.grid_line_color = 'white'
+
+ save(plot)
+
+ return hist, edges
+
+
+def monsoon_tx_power_sweep_plot(dest_path, plot_title, currents, txs):
+ """ Creates average current vs tx power plot
+
+ Args:
+ dest_path: destination path
+ plot_title: a filename and title for the plot.
+ currents: List of average currents measured during power sweep
+ txs: List of uplink input power levels specified for each measurement
+ """
+
+ output_file(os.path.join(dest_path, plot_title + '.html'))
+
+ plot = figure(title=plot_title,
+ y_axis_label='Average Current [mA]',
+ x_axis_label='Tx Power [dBm]',
+ background_fill_color='#fafafa')
+
+ plot.line(txs, currents)
+ plot.circle(txs, currents, fill_color='white', size=8)
+ plot.y_range.start = 0
+
+ save(plot)
diff --git a/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
new file mode 100644
index 0000000..d9f922c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
@@ -0,0 +1,580 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+ Base Class for Defining Common Telephony Test Functionality
+"""
+
+import logging
+import os
+import re
+import shutil
+import time
+
+from acts import asserts
+from acts import logger as acts_logger
+from acts import signals
+from acts.base_test import BaseTestClass
+from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
+from acts.controllers.android_device import DEFAULT_SDM_LOG_PATH
+from acts.keys import Config
+from acts import records
+from acts import utils
+
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
+ initial_set_up_for_subid_infomation
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
+ set_default_sub_for_all_services
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_test_utils import build_id_override
+from acts_contrib.test_utils.tel.tel_test_utils import disable_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import enable_connectivity_metrics
+from acts_contrib.test_utils.tel.tel_test_utils import enable_radio_log_on
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import force_connectivity_metrics_upload
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_screen_shot_log
+from acts_contrib.test_utils.tel.tel_test_utils import get_sim_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_tcpdump_log
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import print_radio_info
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import recover_build_id
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import set_phone_screen_on
+from acts_contrib.test_utils.tel.tel_test_utils import set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_test_utils import set_qxdm_logger_command
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import start_sdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import start_sdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import start_tcpdumps
+from acts_contrib.test_utils.tel.tel_test_utils import stop_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import stop_sdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import stop_sdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import stop_tcpdumps
+from acts_contrib.test_utils.tel.tel_test_utils import synchronize_device_time
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sims_ready_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import activate_wfc_on_device
+from acts_contrib.test_utils.tel.tel_test_utils import install_googleaccountutil_apk
+from acts_contrib.test_utils.tel.tel_test_utils import add_google_account
+from acts_contrib.test_utils.tel.tel_test_utils import install_googlefi_apk
+from acts_contrib.test_utils.tel.tel_test_utils import activate_google_fi_account
+from acts_contrib.test_utils.tel.tel_test_utils import check_google_fi_activated
+from acts_contrib.test_utils.tel.tel_test_utils import check_fi_apk_installed
+from acts_contrib.test_utils.tel.tel_test_utils import phone_switch_to_msim_mode
+from acts_contrib.test_utils.tel.tel_test_utils import activate_esim_using_suw
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import SINGLE_SIM_CONFIG, MULTI_SIM_CONFIG
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+
+
+class TelephonyBaseTest(BaseTestClass):
+ # Use for logging in the test cases to facilitate
+ # faster log lookup and reduce ambiguity in logging.
+ @staticmethod
+ def tel_test_wrap(fn):
+ def _safe_wrap_test_case(self, *args, **kwargs):
+ test_id = "%s:%s:%s" % (self.__class__.__name__, self.test_name,
+ self.log_begin_time.replace(' ', '-'))
+ self.test_id = test_id
+ self.result_detail = ""
+ self.testsignal_details = ""
+ self.testsignal_extras = {}
+ tries = int(self.user_params.get("telephony_auto_rerun", 1))
+ for ad in self.android_devices:
+ ad.log_path = self.log_path
+ for i in range(tries + 1):
+ result = True
+ if i > 0:
+ log_string = "[Test Case] RERUN %s" % self.test_name
+ self.log.info(log_string)
+ self._teardown_test(self.test_name)
+ self._setup_test(self.test_name)
+ try:
+ result = fn(self, *args, **kwargs)
+ except signals.TestFailure as e:
+ self.testsignal_details = e.details
+ self.testsignal_extras = e.extras
+ result = False
+ except signals.TestSignal:
+ raise
+ except Exception as e:
+ self.log.exception(e)
+ asserts.fail(self.result_detail)
+ if result is False:
+ if i < tries:
+ continue
+ else:
+ break
+ if self.user_params.get("check_crash", True):
+ new_crash = ad.check_crash_report(self.test_name,
+ self.begin_time, True)
+ if new_crash:
+ msg = "Find new crash reports %s" % new_crash
+ ad.log.error(msg)
+ self.result_detail = "%s %s %s" % (self.result_detail,
+ ad.serial, msg)
+ result = False
+ if result is not False:
+ asserts.explicit_pass(self.result_detail)
+ else:
+ if self.result_detail:
+ asserts.fail(self.result_detail)
+ else:
+ asserts.fail(self.testsignal_details, self.testsignal_extras)
+
+ return _safe_wrap_test_case
+
+ def setup_class(self):
+ super().setup_class()
+ self.wifi_network_ssid = self.user_params.get(
+ "wifi_network_ssid") or self.user_params.get(
+ "wifi_network_ssid_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
+ self.wifi_network_pass = self.user_params.get(
+ "wifi_network_pass") or self.user_params.get(
+ "wifi_network_pass_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
+
+ self.log_path = getattr(logging, "log_path", None)
+ self.qxdm_log = self.user_params.get("qxdm_log", True)
+ self.sdm_log = self.user_params.get("sdm_log", False)
+ self.enable_radio_log_on = self.user_params.get(
+ "enable_radio_log_on", False)
+ self.cbrs_esim = self.user_params.get("cbrs_esim", False)
+ self.account_util = self.user_params.get("account_util", None)
+ self.save_passing_logs = self.user_params.get("save_passing_logs", False)
+ if isinstance(self.account_util, list):
+ self.account_util = self.account_util[0]
+ self.fi_util = self.user_params.get("fi_util", None)
+ if isinstance(self.fi_util, list):
+ self.fi_util = self.fi_util[0]
+ tasks = [(self._init_device, [ad]) for ad in self.android_devices]
+ multithread_func(self.log, tasks)
+ self.skip_reset_between_cases = self.user_params.get(
+ "skip_reset_between_cases", True)
+ self.log_path = getattr(logging, "log_path", None)
+ self.sim_config = {
+ "config":SINGLE_SIM_CONFIG,
+ "number_of_sims":1
+ }
+
+ for ad in self.android_devices:
+ if hasattr(ad, "dsds"):
+ self.sim_config = {
+ "config":MULTI_SIM_CONFIG,
+ "number_of_sims":2
+ }
+ break
+ if "anritsu_md8475a_ip_address" in self.user_params:
+ return
+ qxdm_log_mask_cfg = self.user_params.get("qxdm_log_mask_cfg", None)
+ if isinstance(qxdm_log_mask_cfg, list):
+ qxdm_log_mask_cfg = qxdm_log_mask_cfg[0]
+ if qxdm_log_mask_cfg and "dev/null" in qxdm_log_mask_cfg:
+ qxdm_log_mask_cfg = None
+ sim_conf_file = self.user_params.get("sim_conf_file")
+ if not sim_conf_file:
+ self.log.info("\"sim_conf_file\" is not provided test bed config!")
+ else:
+ if isinstance(sim_conf_file, list):
+ sim_conf_file = sim_conf_file[0]
+ # If the sim_conf_file is not a full path, attempt to find it
+ # relative to the config file.
+ if not os.path.isfile(sim_conf_file):
+ sim_conf_file = os.path.join(
+ self.user_params[Config.key_config_path.value],
+ sim_conf_file)
+ if not os.path.isfile(sim_conf_file):
+ self.log.error("Unable to load user config %s ",
+ sim_conf_file)
+
+ tasks = [(self._setup_device, [ad, sim_conf_file, qxdm_log_mask_cfg])
+ for ad in self.android_devices]
+ return multithread_func(self.log, tasks)
+
+ def _init_device(self, ad):
+ synchronize_device_time(ad)
+ ad.log_path = self.log_path
+ print_radio_info(ad)
+ unlock_sim(ad)
+ ad.wakeup_screen()
+ ad.adb.shell("input keyevent 82")
+
+ def wait_for_sim_ready(self,ad):
+ wait_for_sim_ready_on_sim_config = {
+ SINGLE_SIM_CONFIG : lambda:wait_for_sim_ready_by_adb(self.log,ad),
+ MULTI_SIM_CONFIG : lambda:wait_for_sims_ready_by_adb(self.log,ad)
+ }
+ if not wait_for_sim_ready_on_sim_config[self.sim_config["config"]]:
+ raise signals.TestAbortClass("unable to load the SIM")
+
+ def _setup_device(self, ad, sim_conf_file, qxdm_log_mask_cfg=None):
+ ad.qxdm_log = getattr(ad, "qxdm_log", self.qxdm_log)
+ ad.sdm_log = getattr(ad, "sdm_log", self.sdm_log)
+ if self.user_params.get("enable_connectivity_metrics", False):
+ enable_connectivity_metrics(ad)
+ if self.user_params.get("build_id_override", False):
+ build_postfix = self.user_params.get("build_id_postfix",
+ "LAB_TEST")
+ build_id_override(
+ ad,
+ new_build_id=self.user_params.get("build_id_override_with",
+ None),
+ postfix=build_postfix)
+ if self.enable_radio_log_on:
+ enable_radio_log_on(ad)
+ list_of_models = ["sdm", "msm", "kon", "lit"]
+ if any(model in ad.model for model in list_of_models):
+ phone_mode = "ssss"
+ if hasattr(ad, "mtp_dsds"):
+ phone_mode = "dsds"
+ if ad.adb.getprop("persist.radio.multisim.config") != phone_mode:
+ ad.adb.shell("setprop persist.radio.multisim.config %s" \
+ % phone_mode)
+ reboot_device(ad)
+
+ stop_qxdm_logger(ad)
+ if ad.qxdm_log:
+ qxdm_log_mask = getattr(ad, "qxdm_log_mask", None)
+ if qxdm_log_mask_cfg:
+ qxdm_mask_path = self.user_params.get("qxdm_log_path",
+ DEFAULT_QXDM_LOG_PATH)
+ ad.adb.shell("mkdir %s" % qxdm_mask_path, ignore_status=True)
+ ad.log.info("Push %s to %s", qxdm_log_mask_cfg, qxdm_mask_path)
+ ad.adb.push("%s %s" % (qxdm_log_mask_cfg, qxdm_mask_path))
+ mask_file_name = os.path.split(qxdm_log_mask_cfg)[-1]
+ qxdm_log_mask = os.path.join(qxdm_mask_path, mask_file_name)
+ set_qxdm_logger_command(ad, mask=qxdm_log_mask)
+ start_qxdm_logger(ad, utils.get_current_epoch_time())
+ elif ad.sdm_log:
+ start_sdm_logger(ad)
+ else:
+ disable_qxdm_logger(ad)
+ if not unlock_sim(ad):
+ raise signals.TestAbortClass("unable to unlock the SIM")
+
+ # If device is setup already, skip the following setup procedures
+ if getattr(ad, "telephony_test_setup", None):
+ return True
+
+ # eSIM enablement
+ if hasattr(ad, "fi_esim"):
+ if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
+ self.wifi_network_pass):
+ ad.log.error("Failed to connect to wifi")
+ if check_google_fi_activated(ad):
+ ad.log.info("Google Fi is already Activated")
+ else:
+ install_googleaccountutil_apk(ad, self.account_util)
+ add_google_account(ad)
+ install_googlefi_apk(ad, self.fi_util)
+ if not activate_google_fi_account(ad):
+ ad.log.error("Failed to activate Fi")
+ check_google_fi_activated(ad)
+ if hasattr(ad, "dsds"):
+ sim_mode = ad.droid.telephonyGetPhoneCount()
+ if sim_mode == 1:
+ ad.log.info("Phone in Single SIM Mode")
+ if not phone_switch_to_msim_mode(ad):
+ ad.log.error("Failed to switch to Dual SIM Mode")
+ return False
+ elif sim_mode == 2:
+ ad.log.info("Phone already in Dual SIM Mode")
+ if get_sim_state(ad) in (SIM_STATE_ABSENT, SIM_STATE_UNKNOWN):
+ ad.log.info("Device has no or unknown SIM in it")
+ # eSIM needs activation
+ activate_esim_using_suw(ad)
+ ensure_phone_idle(self.log, ad)
+ elif self.user_params.get("Attenuator"):
+ ad.log.info("Device in chamber room")
+ ensure_phone_idle(self.log, ad)
+ setup_droid_properties(self.log, ad, sim_conf_file)
+ else:
+ self.wait_for_sim_ready(ad)
+ ensure_phone_default_state(self.log, ad)
+ setup_droid_properties(self.log, ad, sim_conf_file)
+
+ if hasattr(ad, "dsds"):
+ default_slot = getattr(ad, "default_slot", 0)
+ if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
+ ad.log.info("Slot %s is the default slot.", default_slot)
+ set_default_sub_for_all_services(ad, default_slot)
+ else:
+ ad.log.warning("Slot %s is NOT a valid slot. Slot %s will be used by default.",
+ default_slot, 1-default_slot)
+ set_default_sub_for_all_services(ad, 1-default_slot)
+
+ # Activate WFC on Verizon, AT&T and Canada operators as per # b/33187374 &
+ # b/122327716
+ activate_wfc_on_device(self.log, ad)
+
+ # Sub ID setup
+ initial_set_up_for_subid_infomation(self.log, ad)
+
+
+ #try:
+ # ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_ENABLED)
+ #except Exception:
+ # pass
+
+ # Disable Emergency alerts
+ # Set chrome browser start with no-first-run verification and
+ # disable-fre. Give permission to read from and write to storage.
+ for cmd in ("pm disable com.android.cellbroadcastreceiver",
+ "pm grant com.android.chrome "
+ "android.permission.READ_EXTERNAL_STORAGE",
+ "pm grant com.android.chrome "
+ "android.permission.WRITE_EXTERNAL_STORAGE",
+ "rm /data/local/chrome-command-line",
+ "am set-debug-app --persistent com.android.chrome",
+ 'echo "chrome --no-default-browser-check --no-first-run '
+ '--disable-fre" > /data/local/tmp/chrome-command-line'):
+ ad.adb.shell(cmd, ignore_status=True)
+
+ # Curl for 2016/7 devices
+ if not getattr(ad, "curl_capable", False):
+ try:
+ out = ad.adb.shell("/data/curl --version")
+ if not out or "not found" in out:
+ if int(ad.adb.getprop("ro.product.first_api_level")) >= 25:
+ tel_data = self.user_params.get("tel_data", "tel_data")
+ if isinstance(tel_data, list):
+ tel_data = tel_data[0]
+ curl_file_path = os.path.join(tel_data, "curl")
+ if not os.path.isfile(curl_file_path):
+ curl_file_path = os.path.join(
+ self.user_params[Config.key_config_path.value],
+ curl_file_path)
+ if os.path.isfile(curl_file_path):
+ ad.log.info("Pushing Curl to /data dir")
+ ad.adb.push("%s /data" % (curl_file_path))
+ ad.adb.shell(
+ "chmod 777 /data/curl", ignore_status=True)
+ else:
+ setattr(ad, "curl_capable", True)
+ except Exception:
+ ad.log.info("Failed to push curl on this device")
+
+ # Ensure that a test class starts from a consistent state that
+ # improves chances of valid network selection and facilitates
+ # logging.
+ try:
+ if not set_phone_screen_on(self.log, ad):
+ self.log.error("Failed to set phone screen-on time.")
+ return False
+ if not set_phone_silent_mode(self.log, ad):
+ self.log.error("Failed to set phone silent mode.")
+ return False
+ ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+ PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
+ ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+ PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
+ ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+ PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
+ except Exception as e:
+ self.log.error("Failure with %s", e)
+ setattr(ad, "telephony_test_setup", True)
+ return True
+
+ def _teardown_device(self, ad):
+ try:
+ stop_qxdm_logger(ad)
+ stop_sdm_logger(ad)
+ except Exception as e:
+ self.log.error("Failure with %s", e)
+ try:
+ ad.droid.disableDevicePassword()
+ except Exception as e:
+ self.log.error("Failure with %s", e)
+ if self.user_params.get("enable_connectivity_metrics", False):
+ if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
+ self.wifi_network_pass):
+ ad.log.error("Failed to connect to wifi")
+ force_connectivity_metrics_upload(ad)
+ time.sleep(30)
+ try:
+ ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_DISABLED)
+ except Exception as e:
+ self.log.error("Failure with %s", e)
+ try:
+ if self.user_params.get("build_id_override",
+ False) and self.user_params.get(
+ "recover_build_id", False):
+ recover_build_id(ad)
+ except Exception as e:
+ self.log.error("Failure with %s", e)
+
+ def teardown_class(self):
+ tasks = [(self._teardown_device, [ad]) for ad in self.android_devices]
+ multithread_func(self.log, tasks)
+ return True
+
+ def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ if not self.user_params.get("qxdm_log_mask_cfg", None):
+ if "wfc" in self.test_name:
+ for ad in self.android_devices:
+ if not getattr(ad, "qxdm_logger_command", None) or (
+ "IMS_DS_CNE_LnX_Golden.cfg" not in getattr(
+ ad, "qxdm_logger_command", "")):
+ set_qxdm_logger_command(
+ ad, "IMS_DS_CNE_LnX_Golden.cfg")
+ else:
+ for ad in self.android_devices:
+ if not getattr(ad, "qxdm_logger_command", None) or (
+ "IMS_DS_CNE_LnX_Golden.cfg" in getattr(
+ ad, "qxdm_logger_command", "")):
+ set_qxdm_logger_command(ad, None)
+ start_qxdm_loggers(self.log, self.android_devices, self.begin_time)
+ if getattr(self, "sdm_log", False):
+ start_sdm_loggers(self.log, self.android_devices)
+ if getattr(self, "tcpdump_log", False) or "wfc" in self.test_name:
+ mask = getattr(self, "tcpdump_mask", "all")
+ interface = getattr(self, "tcpdump_interface", "wlan0")
+ start_tcpdumps(
+ self.android_devices,
+ begin_time=self.begin_time,
+ interface=interface,
+ mask=mask)
+ else:
+ stop_tcpdumps(self.android_devices)
+ for ad in self.android_devices:
+ if self.skip_reset_between_cases:
+ ensure_phone_idle(self.log, ad)
+ else:
+ ensure_phone_default_state(self.log, ad)
+ for session in ad._sl4a_manager.sessions.values():
+ ed = session.get_event_dispatcher()
+ ed.clear_all_events()
+ output = ad.adb.logcat("-t 1")
+ match = re.search(r"\d+-\d+\s\d+:\d+:\d+.\d+", output)
+ if match:
+ ad.test_log_begin_time = match.group(0)
+
+ def teardown_test(self):
+ stop_tcpdumps(self.android_devices)
+
+ def on_fail(self, test_name, begin_time):
+ self._take_bug_report(test_name, begin_time)
+
+ def on_pass(self, test_name, begin_time):
+ if self.save_passing_logs:
+ self._take_bug_report(test_name, begin_time)
+
+ def _ad_take_extra_logs(self, ad, test_name, begin_time):
+ ad.adb.wait_for_device()
+ result = True
+
+ try:
+ # get tcpdump and screen shot log
+ get_tcpdump_log(ad, test_name, begin_time)
+ get_screen_shot_log(ad, test_name, begin_time)
+ except Exception as e:
+ ad.log.error("Exception error %s", e)
+ result = False
+
+ try:
+ ad.check_crash_report(test_name, begin_time, log_crash_report=True)
+ except Exception as e:
+ ad.log.error("Failed to check crash report for %s with error %s",
+ test_name, e)
+ result = False
+
+ extra_qxdm_logs_in_seconds = self.user_params.get(
+ "extra_qxdm_logs_in_seconds", 60 * 3)
+ if getattr(ad, "qxdm_log", True):
+ # Gather qxdm log modified 3 minutes earlier than test start time
+ if begin_time:
+ qxdm_begin_time = begin_time - 1000 * extra_qxdm_logs_in_seconds
+ else:
+ qxdm_begin_time = None
+ try:
+ time.sleep(10)
+ ad.get_qxdm_logs(test_name, qxdm_begin_time)
+ except Exception as e:
+ ad.log.error("Failed to get QXDM log for %s with error %s",
+ test_name, e)
+ result = False
+ if getattr(ad, "sdm_log", False):
+ # Gather sdm log modified 3 minutes earlier than test start time
+ if begin_time:
+ sdm_begin_time = begin_time - 1000 * extra_qxdm_logs_in_seconds
+ else:
+ sdm_begin_time = None
+ try:
+ time.sleep(10)
+ ad.get_sdm_logs(test_name, sdm_begin_time)
+ except Exception as e:
+ ad.log.error("Failed to get SDM log for %s with error %s",
+ test_name, e)
+ result = False
+
+ return result
+
+ def _take_bug_report(self, test_name, begin_time):
+ if self._skip_bug_report(test_name):
+ return
+ dev_num = getattr(self, "number_of_devices", None) or len(
+ self.android_devices)
+ tasks = [(self._ad_take_bugreport, (ad, test_name, begin_time))
+ for ad in self.android_devices[:dev_num]]
+ tasks.extend([(self._ad_take_extra_logs, (ad, test_name, begin_time))
+ for ad in self.android_devices[:dev_num]])
+ run_multithread_func(self.log, tasks)
+ for ad in self.android_devices[:dev_num]:
+ if getattr(ad, "reboot_to_recover", False):
+ reboot_device(ad)
+ ad.reboot_to_recover = False
+ # Zip log folder
+ if not self.user_params.get("zip_log", False): return
+ src_dir = os.path.join(self.log_path, test_name)
+ os.makedirs(src_dir, exist_ok=True)
+ file_name = "%s_%s" % (src_dir, begin_time)
+ self.log.info("Zip folder %s to %s.zip", src_dir, file_name)
+ shutil.make_archive(file_name, "zip", src_dir)
+ shutil.rmtree(src_dir)
+
+ def _block_all_test_cases(self, tests, reason='Failed class setup'):
+ """Over-write _block_all_test_cases in BaseTestClass."""
+ for (i, (test_name, test_func)) in enumerate(tests):
+ signal = signals.TestFailure(reason)
+ record = records.TestResultRecord(test_name, self.TAG)
+ record.test_begin()
+ # mark all test cases as FAIL
+ record.test_fail(signal)
+ self.results.add_record(record)
+ # only gather bug report for the first test case
+ if i == 0:
+ self.on_fail(test_name, record.begin_time)
+
+ def get_stress_test_number(self):
+ """Gets the stress_test_number param from user params.
+
+ Gets the stress_test_number param. If absent, returns default 100.
+ """
+ return int(self.user_params.get("stress_test_number", 100))
diff --git a/acts_tests/acts_contrib/test_utils/tel/__init__.py b/acts_tests/acts_contrib/test_utils/tel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py b/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py
new file mode 100644
index 0000000..04a9e35
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py
@@ -0,0 +1,2769 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+
+from queue import Empty
+from datetime import datetime
+from acts.controllers.anritsu_lib import band_constants
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuUtils
+from acts.controllers.anritsu_lib.md8475a import BtsNumber
+from acts.controllers.anritsu_lib.md8475a import BtsNwNameEnable
+from acts.controllers.anritsu_lib.md8475a import BtsServiceState
+from acts.controllers.anritsu_lib.md8475a import BtsTechnology
+from acts.controllers.anritsu_lib.md8475a import CsfbType
+from acts.controllers.anritsu_lib.md8475a import ImsCscfCall
+from acts.controllers.anritsu_lib.md8475a import ImsCscfStatus
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import ReturnToEUTRAN
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.controllers.anritsu_lib.md8475a import TestProcedure
+from acts.controllers.anritsu_lib.md8475a import TestPowerControl
+from acts.controllers.anritsu_lib.md8475a import TestMeasurement
+from acts.controllers.anritsu_lib.md8475a import Switch
+from acts.controllers.anritsu_lib.md8475a import BtsPacketRate
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_REMOTE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_LONG
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_defines import EventCmasReceived
+from acts_contrib.test_utils.tel.tel_defines import EventEtwsReceived
+from acts_contrib.test_utils.tel.tel_defines import EventSmsDeliverSuccess
+from acts_contrib.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts_contrib.test_utils.tel.tel_defines import EventSmsReceived
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_idle
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
+
+# Timers
+# Time to wait after registration before sending a command to Anritsu
+# to ensure the phone has sufficient time to reconfigure based on new
+# network in Anritsu
+WAIT_TIME_ANRITSU_REG_AND_OPER = 10
+# Time to wait after registration to ensure the phone
+# has sufficient time to reconfigure based on new network in Anritsu
+WAIT_TIME_ANRITSU_REG_AND_CALL = 10
+# Max time to wait for Anritsu's virtual phone state change
+MAX_WAIT_TIME_VIRTUAL_PHONE_STATE = 45
+# Time to wait for Anritsu's IMS CSCF state change
+MAX_WAIT_TIME_IMS_CSCF_STATE = 30
+# Time to wait for before aSRVCC
+WAIT_TIME_IN_ALERT = 5
+
+# SIM card names
+P0250Ax = "P0250Ax"
+VzW12349 = "VzW12349"
+P0135Ax = "P0135Ax"
+FiTMO = "FiTMO"
+FiSPR = "FiSPR"
+FiUSCC = "FiUSCC"
+
+# Test PLMN information
+TEST_PLMN_LTE_NAME = "MD8475A_LTE"
+TEST_PLMN_WCDMA_NAME = "MD8475A_WCDMA"
+TEST_PLMN_GSM_NAME = "MD8475A_GSM"
+TEST_PLMN_1X_NAME = "MD8475A_1X"
+TEST_PLMN_1_MCC = "001"
+TEST_PLMN_1_MNC = "01"
+DEFAULT_MCC = "310"
+DEFAULT_MNC = "260"
+DEFAULT_RAC = 1
+DEFAULT_LAC = 1
+VzW_MCC = "311"
+VzW_MNC = "480"
+TMO_MCC = "310"
+TMO_MNC = "260"
+Fi_TMO_MCC = "310"
+Fi_TMO_MNC = "260"
+Fi_SPR_MCC = "310"
+Fi_SPR_MNC = "120"
+Fi_USCC_MCC = "311"
+Fi_USCC_MNC = "580"
+
+# IP address information for internet sharing
+#GATEWAY_IPV4_ADDR = "192.168.137.1"
+#UE_IPV4_ADDR_1 = "192.168.137.2"
+#UE_IPV4_ADDR_2 = "192.168.137.3"
+#UE_IPV4_ADDR_3 = "192.168.137.4"
+#DNS_IPV4_ADDR = "192.168.137.1"
+#CSCF_IPV4_ADDR = "192.168.137.1"
+
+# Default IP address in Smart Studio, work for Internet Sharing with and
+# without WLAN ePDG server. Remember to add 192.168.1.2 to Ethernet 0
+# on MD8475A after turn on Windows' Internet Coonection Sharing
+GATEWAY_IPV4_ADDR = "192.168.1.2"
+UE_IPV4_ADDR_1 = "192.168.1.1"
+UE_IPV4_ADDR_2 = "192.168.1.11"
+UE_IPV4_ADDR_3 = "192.168.1.21"
+UE_IPV6_ADDR_1 = "2001:0:0:1::1"
+UE_IPV6_ADDR_2 = "2001:0:0:2::1"
+UE_IPV6_ADDR_3 = "2001:0:0:3::1"
+DNS_IPV4_ADDR = "192.168.1.12"
+CSCF_IPV4_ADDR = "192.168.1.2"
+CSCF_IPV6_ADDR = "2001:0:0:1::2"
+CSCF_IPV6_ADDR_2 = "2001:0:0:2::2"
+CSCF_IPV6_ADDR_3 = "2001:0:0:3::2"
+
+# Google Fi IP Config:
+
+Fi_GATEWAY_IPV4_ADDR_Data = "100.107.235.94"
+Fi_GATEWAY_IPV6_ADDR_Data = "fe80::aef2:c5ff:fe71:4b9"
+Fi_GATEWAY_IPV4_ADDR_IMS_911 = "192.168.1.2"
+Fi_GATEWAY_IPV6_ADDR_IMS_911 = "2001:0:0:1::2"
+
+Fi_UE_IPV4_ADDR_Data = "100.107.235.81"
+Fi_UE_IPV4_ADDR_IMS = "192.168.1.1"
+Fi_UE_IPV4_ADDR_911 = "192.168.1.11"
+Fi_UE_IPV6_ADDR_Data = "2620::1000:1551:1140:c0f9:d6a8:44eb"
+Fi_UE_IPV6_ADDR_IMS = "2001:0:0:1::1"
+Fi_UE_IPV6_ADDR_911 = "2001:0:0:2::1"
+
+Fi_DNS_IPV4_ADDR_Pri = "8.8.8.8"
+Fi_DNS_IPV4_ADDR_Sec = "8.8.8.4"
+Fi_DNS_IPV6_ADDR = "2001:4860:4860::8888"
+
+Fi_CSCF_IPV4_ADDR_Data = "192.168.1.2"
+Fi_CSCF_IPV6_ADDR_Data = "2001:0:0:1::2"
+Fi_CSCF_IPV4_ADDR_IMS = "192.168.1.2"
+Fi_CSCF_IPV6_ADDR_IMS = "2001:0:0:1::3"
+Fi_CSCF_IPV4_ADDR_911 = "192.168.1.12"
+Fi_CSCF_IPV6_ADDR_911 = "2001:0:0:2::2"
+
+# Default Cell Parameters
+DEFAULT_OUTPUT_LEVEL = -30
+DEFAULT_1X_OUTPUT_LEVEL = -35
+DEFAULT_INPUT_LEVEL = 0
+DEFAULT_LTE_BAND = [2, 4]
+Fi_LTE_TMO_BAND = [4]
+Fi_LTE_SPR_BAND = [25]
+Fi_LTE_USCC_BAND = [12]
+Fi_GSM_TMO_BAND = band_constants.GSM_BAND_PGSM900
+DEFAULT_WCDMA_BAND = 1
+DEFAULT_WCDMA_PACKET_RATE = BtsPacketRate.WCDMA_DLHSAUTO_REL7_ULHSAUTO
+DEFAULT_GSM_BAND = band_constants.GSM_BAND_GSM850
+
+#Google Fi CDMA Bands
+
+Fi_USCC1X_MCC = 209
+Fi_USCC1X_BAND = 1
+Fi_USCC1X_CH = 600
+Fi_USCC1X_SID = 5
+Fi_USCC1X_NID = 21
+
+Fi_SPR1X_MCC = 320
+Fi_SPR1X_BAND = 1
+Fi_SPR1X_CH = 600
+Fi_SPR1X_SID = 4183
+Fi_SPR1X_NID = 233
+
+Fi_EVDO_BAND = 1
+Fi_EVDO_CH = 625
+Fi_EVDO_SECTOR_ID = "00000000,00000000,00000000,00000000"
+
+DEFAULT_CDMA1X_BAND = 0
+DEFAULT_CDMA1X_CH = 356
+DEFAULT_CDMA1X_SID = 0
+DEFAULT_CDMA1X_NID = 65535
+DEFAULT_EVDO_BAND = 0
+DEFAULT_EVDO_CH = 356
+DEFAULT_EVDO_SECTOR_ID = "00000000,00000000,00000000,00000000"
+VzW_CDMA1x_BAND = 1
+VzW_CDMA1x_CH = 150
+VzW_CDMA1X_SID = 26
+VzW_CDMA1X_NID = 65535
+VzW_EVDO_BAND = 0
+VzW_EVDO_CH = 384
+VzW_EVDO_SECTOR_ID = "12345678,00000000,00000000,00000000"
+DEFAULT_T_MODE = "TM1"
+DEFAULT_DL_ANTENNA = 1
+
+# CMAS Message IDs
+CMAS_MESSAGE_PRESIDENTIAL_ALERT = hex(0x1112)
+CMAS_MESSAGE_EXTREME_IMMEDIATE_OBSERVED = hex(0x1113)
+CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY = hex(0x1114)
+CMAS_MESSAGE_EXTREME_EXPECTED_OBSERVED = hex(0x1115)
+CMAS_MESSAGE_EXTREME_EXPECTED_LIKELY = hex(0x1116)
+CMAS_MESSAGE_SEVERE_IMMEDIATE_OBSERVED = hex(0x1117)
+CMAS_MESSAGE_SEVERE_IMMEDIATE_LIKELY = hex(0x1118)
+CMAS_MESSAGE_SEVERE_EXPECTED_OBSERVED = hex(0x1119)
+CMAS_MESSAGE_SEVERE_EXPECTED_LIKELY = hex(0x111A)
+CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY = hex(0x111B)
+CMAS_MESSAGE_MONTHLY_TEST = hex(0x111C)
+CMAS_MESSAGE_CMAS_EXECERCISE = hex(0x111D)
+
+# ETWS Message IDs
+ETWS_WARNING_EARTHQUAKE = hex(0x1100)
+ETWS_WARNING_TSUNAMI = hex(0x1101)
+ETWS_WARNING_EARTHQUAKETSUNAMI = hex(0x1102)
+ETWS_WARNING_TEST_MESSAGE = hex(0x1103)
+ETWS_WARNING_OTHER_EMERGENCY = hex(0x1104)
+
+# C2K CMAS Message Constants
+CMAS_C2K_CATEGORY_PRESIDENTIAL = "Presidential"
+CMAS_C2K_CATEGORY_EXTREME = "Extreme"
+CMAS_C2K_CATEGORY_SEVERE = "Severe"
+CMAS_C2K_CATEGORY_AMBER = "AMBER"
+CMAS_C2K_CATEGORY_CMASTEST = "CMASTest"
+
+CMAS_C2K_PRIORITY_NORMAL = "Normal"
+CMAS_C2K_PRIORITY_INTERACTIVE = "Interactive"
+CMAS_C2K_PRIORITY_URGENT = "Urgent"
+CMAS_C2K_PRIORITY_EMERGENCY = "Emergency"
+
+CMAS_C2K_RESPONSETYPE_SHELTER = "Shelter"
+CMAS_C2K_RESPONSETYPE_EVACUATE = "Evacuate"
+CMAS_C2K_RESPONSETYPE_PREPARE = "Prepare"
+CMAS_C2K_RESPONSETYPE_EXECUTE = "Execute"
+CMAS_C2K_RESPONSETYPE_MONITOR = "Monitor"
+CMAS_C2K_RESPONSETYPE_AVOID = "Avoid"
+CMAS_C2K_RESPONSETYPE_ASSESS = "Assess"
+CMAS_C2K_RESPONSETYPE_NONE = "None"
+
+CMAS_C2K_SEVERITY_EXTREME = "Extreme"
+CMAS_C2K_SEVERITY_SEVERE = "Severe"
+
+CMAS_C2K_URGENCY_IMMEDIATE = "Immediate"
+CMAS_C2K_URGENCY_EXPECTED = "Expected"
+
+CMAS_C2K_CERTIANTY_OBSERVED = "Observed"
+CMAS_C2K_CERTIANTY_LIKELY = "Likely"
+
+#PDN Numbers
+PDN_NO_1 = 1
+PDN_NO_2 = 2
+PDN_NO_3 = 3
+PDN_NO_4 = 4
+PDN_NO_5 = 5
+
+# IMS Services parameters
+DEFAULT_VNID = 1
+NDP_NIC_NAME = '"Intel(R) 82577LM Gigabit Network Connection"'
+CSCF_Monitoring_UA_URI = '"sip:+11234567890@test.3gpp.com"'
+TMO_CSCF_Monitoring_UA_URI = '"sip:001010123456789@msg.lab.t-mobile.com"'
+CSCF_Virtual_UA_URI = '"sip:+11234567891@test.3gpp.com"'
+TMO_CSCF_Virtual_UA_URI = '"sip:0123456789@ims.mnc01.mcc001.3gppnetwork.org"'
+CSCF_HOSTNAME = '"ims.mnc01.mcc001.3gppnetwork.org"'
+TMO_USERLIST_NAME = "310260123456789@msg.lab.t-mobile.com"
+VZW_USERLIST_NAME = "001010123456789@test.3gpp.com"
+
+# Google Fi IMS Services parameters
+Fi_CSCF_Monitoring_UA_URI = '"sip:310260971239432@ims.mnc260.mcc310.3gppnetwork.org"'
+Fi_CSCF_Virtual_UA_URI = '"sip:0123456789@msg.pc.t-mobile.com"'
+Fi_CSCF_HOSTNAME = '"ims.mnc260.mcc310.3gppnetwork.org"'
+Fi_USERLIST_NAME = "310260971239432@msg.pc.t-mobile.com"
+
+#Cell Numbers
+CELL_1 = 1
+CELL_2 = 2
+
+# default ims virtual network id for Anritsu ims call test.
+DEFAULT_IMS_VIRTUAL_NETWORK_ID = 1
+
+def cb_serial_number():
+ """ CMAS/ETWS serial number generator """
+ i = 0x3000
+ while True:
+ yield i
+ i += 1
+
+
+def set_usim_parameters(anritsu_handle, sim_card):
+ """ set USIM parameters in MD8475A simulationn parameter
+
+ Args:
+ anritsu_handle: anritusu device object.
+ sim_card : "P0250Ax" or "12349"
+
+ Returns:
+ None
+ """
+ if sim_card == P0250Ax:
+ anritsu_handle.usim_key = "000102030405060708090A0B0C0D0E0F"
+ elif sim_card == P0135Ax:
+ anritsu_handle.usim_key = "00112233445566778899AABBCCDDEEFF"
+ elif sim_card == VzW12349:
+ anritsu_handle.usim_key = "465B5CE8B199B49FAA5F0A2EE238A6BC"
+ anritsu_handle.send_command("IMSI 311480012345678")
+ anritsu_handle.send_command("SECURITY3G MILENAGE")
+ anritsu_handle.send_command(
+ "MILENAGEOP 5F1D289C5D354D0A140C2548F5F3E3BA")
+ elif sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ anritsu_handle.usim_key = "000102030405060708090A0B0C0D0E0F"
+
+
+def save_anritsu_log_files(anritsu_handle, test_name, user_params):
+ """ saves the anritsu smart studio log files
+ The logs should be saved in Anritsu system. Need to provide
+ log folder path in Anritsu system
+
+ Args:
+ anritsu_handle: anritusu device object.
+ test_name: test case name
+ user_params : user supplied parameters list
+
+ Returns:
+ None
+ """
+ md8475a_log_folder = user_params["anritsu_log_file_path"]
+ file_name = getfilenamewithtimestamp(test_name)
+ seq_logfile = "{}\\{}_seq.csv".format(md8475a_log_folder, file_name)
+ msg_logfile = "{}\\{}_msg.csv".format(md8475a_log_folder, file_name)
+ trace_logfile = "{}\\{}_trace.lgex".format(md8475a_log_folder, file_name)
+ anritsu_handle.save_sequence_log(seq_logfile)
+ anritsu_handle.save_message_log(msg_logfile)
+ anritsu_handle.save_trace_log(trace_logfile, "BINARY", 1, 0, 0)
+ anritsu_handle.clear_sequence_log()
+ anritsu_handle.clear_message_log()
+
+
+def getfilenamewithtimestamp(test_name):
+ """ Gets the test name appended with current time
+
+ Args:
+ test_name : test case name
+
+ Returns:
+ string of test name appended with current time
+ """
+ time_stamp = datetime.now().strftime("%m-%d-%Y_%H-%M-%S")
+ return "{}_{}".format(test_name, time_stamp)
+
+
+def _init_lte_bts(bts, user_params, cell_no, sim_card):
+ """ initializes the LTE BTS
+ All BTS parameters should be set here
+
+ Args:
+ bts: BTS object.
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ None
+ """
+ bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+ bts.nw_fullname = TEST_PLMN_LTE_NAME
+ bts.mcc = get_lte_mcc(user_params, cell_no, sim_card)
+ bts.mnc = get_lte_mnc(user_params, cell_no, sim_card)
+ bts.band = get_lte_band(user_params, cell_no, sim_card)
+ bts.transmode = get_transmission_mode(user_params, cell_no)
+ bts.dl_antenna = get_dl_antenna(user_params, cell_no)
+ bts.output_level = DEFAULT_OUTPUT_LEVEL
+ bts.input_level = DEFAULT_INPUT_LEVEL
+
+
+def _init_wcdma_bts(bts, user_params, cell_no, sim_card):
+ """ initializes the WCDMA BTS
+ All BTS parameters should be set here
+
+ Args:
+ bts: BTS object.
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ None
+ """
+ bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+ bts.nw_fullname = TEST_PLMN_WCDMA_NAME
+ bts.mcc = get_wcdma_mcc(user_params, cell_no, sim_card)
+ bts.mnc = get_wcdma_mnc(user_params, cell_no, sim_card)
+ bts.band = get_wcdma_band(user_params, cell_no)
+ bts.rac = get_wcdma_rac(user_params, cell_no)
+ bts.lac = get_wcdma_lac(user_params, cell_no)
+ bts.output_level = DEFAULT_OUTPUT_LEVEL
+ bts.input_level = DEFAULT_INPUT_LEVEL
+ bts.packet_rate = DEFAULT_WCDMA_PACKET_RATE
+
+
+def _init_gsm_bts(bts, user_params, cell_no, sim_card):
+ """ initializes the GSM BTS
+ All BTS parameters should be set here
+
+ Args:
+ bts: BTS object.
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ None
+ """
+ bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+ bts.nw_fullname = TEST_PLMN_GSM_NAME
+ bts.mcc = get_gsm_mcc(user_params, cell_no, sim_card)
+ bts.mnc = get_gsm_mnc(user_params, cell_no, sim_card)
+ bts.band = get_gsm_band(user_params, cell_no, sim_card)
+ bts.rac = get_gsm_rac(user_params, cell_no)
+ bts.lac = get_gsm_lac(user_params, cell_no)
+ bts.output_level = DEFAULT_OUTPUT_LEVEL
+ bts.input_level = DEFAULT_INPUT_LEVEL
+
+
+def _init_1x_bts(bts, user_params, cell_no, sim_card):
+ """ initializes the 1X BTS
+ All BTS parameters should be set here
+
+ Args:
+ bts: BTS object.
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ None
+ """
+ bts.sector1_mcc = get_1x_mcc(user_params, cell_no, sim_card)
+ bts.band = get_1x_band(user_params, cell_no, sim_card)
+ bts.dl_channel = get_1x_channel(user_params, cell_no, sim_card)
+ bts.sector1_sid = get_1x_sid(user_params, cell_no, sim_card)
+ bts.sector1_nid = get_1x_nid(user_params, cell_no, sim_card)
+ bts.output_level = DEFAULT_1X_OUTPUT_LEVEL
+
+
+def _init_evdo_bts(bts, user_params, cell_no, sim_card):
+ """ initializes the EVDO BTS
+ All BTS parameters should be set here
+
+ Args:
+ bts: BTS object.
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ None
+ """
+ bts.band = get_evdo_band(user_params, cell_no, sim_card)
+ bts.dl_channel = get_evdo_channel(user_params, cell_no, sim_card)
+ bts.evdo_sid = get_evdo_sid(user_params, cell_no, sim_card)
+ bts.output_level = DEFAULT_1X_OUTPUT_LEVEL
+
+
+def _init_PDN(anritsu_handle,
+ sim_card,
+ pdn,
+ ipv4,
+ ipv6,
+ ims_binding,
+ vnid_number=DEFAULT_VNID):
+ """ initializes the PDN parameters
+ All PDN parameters should be set here
+
+ Args:
+ anritsu_handle: anritusu device object.
+ pdn: pdn object
+ ip_address : UE IP address
+ ims_binding: to bind with IMS VNID(1) or not
+
+ Returns:
+ None
+ """
+ # Setting IP address for internet connection sharing
+ # Google Fi _init_PDN
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn.ue_address_ipv4 = ipv4
+ pdn.ue_address_ipv6 = ipv6
+ if ims_binding:
+ pdn.pdn_ims = Switch.ENABLE
+ pdn.pdn_vnid = vnid_number
+ pdn.pdn_DG_selection = 'USER'
+ pdn.pdn_gateway_ipv4addr = Fi_GATEWAY_IPV4_ADDR_IMS_911
+ pdn.pdn_gateway_ipv6addr = Fi_GATEWAY_IPV6_ADDR_IMS_911
+
+ else:
+ anritsu_handle.gateway_ipv4addr = Fi_GATEWAY_IPV4_ADDR_Data
+ anritsu_handle.gateway_ipv6addr = Fi_GATEWAY_IPV6_ADDR_Data
+ pdn.primary_dns_address_ipv4 = Fi_DNS_IPV4_ADDR_Pri
+ pdn.secondary_dns_address_ipv4 = Fi_DNS_IPV4_ADDR_Sec
+ pdn.dns_address_ipv6 = Fi_DNS_IPV6_ADDR
+ pdn.cscf_address_ipv4 = Fi_CSCF_IPV4_ADDR_Data
+ pdn.cscf_address_ipv6 = Fi_CSCF_IPV6_ADDR_Data
+ # Pixel Lab _init_PDN_
+ else:
+ anritsu_handle.gateway_ipv4addr = GATEWAY_IPV4_ADDR
+ pdn.ue_address_ipv4 = ipv4
+ pdn.ue_address_ipv6 = ipv6
+ if ims_binding:
+ pdn.pdn_ims = Switch.ENABLE
+ pdn.pdn_vnid = vnid_number
+ else:
+ pdn.primary_dns_address_ipv4 = DNS_IPV4_ADDR
+ pdn.secondary_dns_address_ipv4 = DNS_IPV4_ADDR
+ pdn.cscf_address_ipv4 = CSCF_IPV4_ADDR
+
+
+def _init_IMS(anritsu_handle,
+ vnid,
+ sim_card=None,
+ ipv4_address=CSCF_IPV4_ADDR,
+ ipv6_address=CSCF_IPV6_ADDR,
+ ip_type="IPV4V6",
+ auth=False):
+ """ initializes the IMS VNID parameters
+ All IMS parameters should be set here
+
+ Args:
+ anritsu_handle: anritusu device object.
+ vnid: IMS Services object
+
+ Returns:
+ None
+ """
+ # vnid.sync = Switch.ENABLE # supported in 6.40a release
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ vnid.cscf_address_ipv4 = ipv4_address
+ vnid.cscf_address_ipv6 = ipv6_address
+ vnid.imscscf_iptype = ip_type
+ vnid.dns = Switch.DISABLE
+ vnid.ndp_nic = NDP_NIC_NAME
+ vnid.ndp_prefix = ipv6_address
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ vnid.cscf_monitoring_ua = Fi_CSCF_Monitoring_UA_URI
+ vnid.cscf_virtual_ua = Fi_CSCF_Virtual_UA_URI
+ vnid.cscf_host_name = Fi_CSCF_HOSTNAME
+ vnid.cscf_ims_authentication = "ENABLE"
+ if auth:
+ vnid.cscf_ims_authentication = "ENABLE"
+ vnid.fi_cscf_userslist_add = Fi_USERLIST_NAME
+ else:
+ vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
+ vnid.psap = Switch.ENABLE
+ vnid.psap_auto_answer = Switch.ENABLE
+ else:
+ vnid.cscf_address_ipv4 = CSCF_IPV4_ADDR
+ vnid.cscf_address_ipv6 = ipv6_address
+ vnid.imscscf_iptype = ip_type
+ vnid.dns = Switch.DISABLE
+ vnid.ndp_nic = NDP_NIC_NAME
+ vnid.ndp_prefix = ipv6_address
+ if sim_card == P0135Ax:
+ vnid.cscf_monitoring_ua = TMO_CSCF_Monitoring_UA_URI
+ vnid.cscf_virtual_ua = TMO_CSCF_Virtual_UA_URI
+ vnid.cscf_host_name = CSCF_HOSTNAME
+ vnid.cscf_precondition = "ENABLE"
+ vnid.cscf_ims_authentication = "DISABLE"
+ if auth:
+ vnid.cscf_ims_authentication = "ENABLE"
+ vnid.tmo_cscf_userslist_add = TMO_USERLIST_NAME
+ elif sim_card == VzW12349:
+ vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
+ vnid.cscf_virtual_ua = CSCF_Virtual_UA_URI
+ vnid.cscf_ims_authentication = "DISABLE"
+ if auth:
+ vnid.cscf_ims_authentication = "ENABLE"
+ vnid.vzw_cscf_userslist_add = VZW_USERLIST_NAME
+ else:
+ vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
+ vnid.psap = Switch.ENABLE
+ vnid.psap_auto_answer = Switch.ENABLE
+
+
+def set_system_model_lte_lte(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE and LTE simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and Wcdma BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+ BtsTechnology.LTE)
+ # setting BTS parameters
+ lte1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ lte2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_lte_bts(lte1_bts, user_params, CELL_1, sim_card)
+ _init_lte_bts(lte2_bts, user_params, CELL_2, sim_card)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ # _init_IMS(
+ # anritsu_handle,
+ # vnid1,
+ # sim_card,
+ # ipv4_address=CSCF_IPV4_ADDR,
+ # ipv6_address=CSCF_IPV6_ADDR,
+ # auth=False)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ else:
+ _init_lte_bts(lte1_bts, user_params, CELL_1, sim_card)
+ _init_lte_bts(lte2_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle, sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte1_bts, lte2_bts]
+
+
+def set_system_model_wcdma_wcdma(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for WCDMA and WCDMA simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and Wcdma BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.WCDMA,
+ BtsTechnology.WCDMA)
+ # setting BTS parameters
+ wcdma1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ wcdma2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_wcdma_bts(wcdma1_bts, user_params, CELL_1, sim_card)
+ _init_wcdma_bts(wcdma2_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data, Fi_UE_IPV6_ADDR_Data,
+ False)
+ else:
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [wcdma1_bts, wcdma2_bts]
+
+
+def set_system_model_lte_wcdma(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE and WCDMA simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and Wcdma BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE, BtsTechnology.WCDMA)
+ # setting BTS parameters
+ lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+ _init_wcdma_bts(wcdma_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+ # Initialize PDN IP address.
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ return [lte_bts, wcdma_bts]
+ else:
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle, sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte_bts, wcdma_bts]
+
+
+def set_system_model_lte_gsm(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE and GSM simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and Wcdma BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE, BtsTechnology.GSM)
+ # setting BTS parameters
+ lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+ _init_gsm_bts(gsm_bts, user_params, CELL_2, sim_card)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+ # Initialize PDN IP address.
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ return [lte_bts, gsm_bts]
+ else:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle, sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte_bts, gsm_bts]
+
+
+def set_system_model_lte_1x(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE and 1x simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and 1x BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+ BtsTechnology.CDMA1X)
+ # setting BTS parameters
+ lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+ _init_1x_bts(cdma1x_bts, user_params, CELL_2, sim_card)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+ # Initialize PDN IP address.
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ return [lte_bts, cdma1x_bts]
+ else:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle, sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte_bts, cdma1x_bts]
+
+
+def set_system_model_lte_evdo(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE and EVDO simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte and 1x BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE, BtsTechnology.EVDO)
+ # setting BTS parameters
+ lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ evdo_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+ _init_evdo_bts(evdo_bts, user_params, CELL_2, sim_card)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+ # Initialize PDN IP address.
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ return [lte_bts, evdo_bts]
+ else:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle, sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle, sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte_bts, evdo_bts]
+
+
+def set_system_model_wcdma_gsm(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for WCDMA and GSM simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Wcdma and Gsm BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.WCDMA, BtsTechnology.GSM)
+ # setting BTS parameters
+ wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_wcdma_bts(wcdma_bts, user_params, CELL_1, sim_card)
+ _init_gsm_bts(gsm_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle, sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle, sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [wcdma_bts, gsm_bts]
+
+
+def set_system_model_gsm_gsm(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for GSM and GSM simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Wcdma and Gsm BTS objects
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.GSM, BtsTechnology.GSM)
+ # setting BTS parameters
+ gsm1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ gsm2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_gsm_bts(gsm1_bts, user_params, CELL_1, sim_card)
+ _init_gsm_bts(gsm2_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [gsm1_bts, gsm2_bts]
+
+
+def set_system_model_lte(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for LTE simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Lte BTS object
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.LTE)
+ # setting Fi BTS parameters
+ lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ pdn4 = anritsu_handle.get_PDN(PDN_NO_4)
+ pdn5 = anritsu_handle.get_PDN(PDN_NO_5)
+ # Initialize PDN IP address.
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle,sim_card, pdn2, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle,sim_card, pdn3, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ _init_PDN(anritsu_handle, sim_card, pdn4, Fi_UE_IPV4_ADDR_IMS,
+ Fi_UE_IPV6_ADDR_IMS,
+ True)
+ _init_PDN(anritsu_handle, sim_card, pdn5, Fi_UE_IPV4_ADDR_911,
+ Fi_UE_IPV6_ADDR_911,
+ True)
+ vnid1 = anritsu_handle.get_IMS(1)
+ vnid2 = anritsu_handle.get_IMS(2)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_IMS,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_IMS,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv4_address=Fi_CSCF_IPV4_ADDR_911,
+ ipv6_address=Fi_CSCF_IPV6_ADDR_911,
+ auth=False)
+ return [lte_bts]
+ else:
+ # setting BTS parameters
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+ pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+ # Initialize PDN IP address for internet connection sharing
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+ _init_PDN(anritsu_handle,sim_card, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+ _init_PDN(anritsu_handle,sim_card, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+ vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
+ return [lte_bts]
+
+
+def set_system_model_wcdma(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for WCDMA simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Wcdma BTS object
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.WCDMA)
+ # setting BTS parameters
+ wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ _init_wcdma_bts(wcdma_bts, user_params, CELL_1, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [wcdma_bts]
+
+
+def set_system_model_gsm(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for GSM simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Gsm BTS object
+ """
+ anritsu_handle.set_simulation_model(BtsTechnology.GSM)
+ # setting BTS parameters
+ gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ _init_gsm_bts(gsm_bts, user_params, CELL_1, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [gsm_bts]
+
+
+def set_system_model_1x(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for CDMA 1X simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Cdma 1x BTS object
+ """
+ PDN_ONE = 1
+ anritsu_handle.set_simulation_model(BtsTechnology.CDMA1X)
+ # setting BTS parameters
+ cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ _init_1x_bts(cdma1x_bts, user_params, CELL_1, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [cdma1x_bts]
+
+
+def set_system_model_1x_evdo(anritsu_handle, user_params, sim_card):
+ """ Configures Anritsu system for CDMA 1X simulation
+
+ Args:
+ anritsu_handle: anritusu device object.
+ user_params: pointer to user supplied parameters
+
+ Returns:
+ Cdma 1x BTS object
+ """
+ PDN_ONE = 1
+ anritsu_handle.set_simulation_model(BtsTechnology.CDMA1X,
+ BtsTechnology.EVDO)
+ # setting BTS parameters
+ cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+ evdo_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+ _init_1x_bts(cdma1x_bts, user_params, CELL_1, sim_card)
+ _init_evdo_bts(evdo_bts, user_params, CELL_2, sim_card)
+ pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+ # Initialize PDN IP address for internet connection sharing
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ _init_PDN(anritsu_handle,sim_card, pdn1, Fi_UE_IPV4_ADDR_Data,
+ Fi_UE_IPV6_ADDR_Data, False)
+ else:
+ _init_PDN(anritsu_handle,sim_card, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+ return [cdma1x_bts]
+
+
+def wait_for_bts_state(log, btsnumber, state, timeout=30):
+ """ Waits for BTS to be in the specified state ("IN" or "OUT")
+
+ Args:
+ btsnumber: BTS number.
+ state: expected state
+
+ Returns:
+ True for success False for failure
+ """
+ # state value are "IN" and "OUT"
+ status = False
+ sleep_interval = 1
+ wait_time = timeout
+
+ if state == "IN":
+ service_state = BtsServiceState.SERVICE_STATE_IN
+ elif state == "OUT":
+ service_state = BtsServiceState.SERVICE_STATE_OUT
+ else:
+ log.info("wrong state value")
+ return status
+
+ if btsnumber.service_state is service_state:
+ log.info("BTS state is already in {}".format(state))
+ return True
+
+ # set to desired service state
+ btsnumber.service_state = service_state
+
+ while wait_time > 0:
+ if service_state == btsnumber.service_state:
+ status = True
+ break
+ time.sleep(sleep_interval)
+ wait_time = wait_time - sleep_interval
+
+ if not status:
+ log.info("Timeout: Expected BTS state is not received.")
+ return status
+
+
+class _CallSequenceException(Exception):
+ pass
+
+
+def call_mo_setup_teardown(
+ log,
+ ad,
+ anritsu_handle,
+ callee_number,
+ teardown_side=CALL_TEARDOWN_PHONE,
+ is_emergency=False,
+ wait_time_in_call=WAIT_TIME_IN_CALL_LONG,
+ is_ims_call=False,
+ ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+ """ Makes a MO call and tear down the call
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritsu object.
+ callee_number: Number to be called.
+ teardown_side: the side to end the call (Phone or remote).
+ is_emergency: is the call an emergency call.
+ wait_time_in_call: Time to wait when phone in call.
+ is_ims_call: is the call expected to be ims call.
+ ims_virtual_network_id: ims virtual network id.
+
+ Returns:
+ True for success False for failure
+ """
+
+ log.info("Making Call to " + callee_number)
+ virtual_phone_handle = anritsu_handle.get_VirtualPhone()
+
+ try:
+ # for an IMS call we either check CSCF or *nothing* (no virtual phone).
+ if is_ims_call:
+ # we only need pre-call registration in a non-emergency case
+ if not is_emergency:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not idle.")
+ else:
+ if not wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise _CallSequenceException("Virtual Phone not idle.")
+
+ if not initiate_call(log, ad, callee_number, is_emergency):
+ raise _CallSequenceException("Initiate call failed.")
+
+ if is_ims_call:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CALLING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not calling.")
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CONNECTED.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not connected.")
+ else:
+ # check Virtual phone answered the call
+ if not wait_for_virtualphone_state(
+ log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS):
+ raise _CallSequenceException("Virtual Phone not in call.")
+
+ time.sleep(wait_time_in_call)
+
+ if not ad.droid.telecomIsInCall():
+ raise _CallSequenceException("Call ended before delay_in_call.")
+
+ if teardown_side is CALL_TEARDOWN_REMOTE:
+ log.info("Disconnecting the call from Remote")
+ if is_ims_call:
+ anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+ ImsCscfCall.END.value)
+ else:
+ virtual_phone_handle.set_voice_on_hook()
+ if not wait_for_droid_not_in_call(log, ad,
+ MAX_WAIT_TIME_CALL_DROP):
+ raise _CallSequenceException("DUT call not drop.")
+ else:
+ log.info("Disconnecting the call from DUT")
+ if not hangup_call(log, ad, is_emergency):
+ raise _CallSequenceException(
+ "Error in Hanging-Up Call on DUT.")
+
+ if is_ims_call:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("Phone IMS status is not idle.")
+ else:
+ if not wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise _CallSequenceException(
+ "Virtual Phone not idle after hangup.")
+ return True
+
+ except _CallSequenceException as e:
+ log.error(e)
+ return False
+ finally:
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+
+def handover_tc(log,
+ anritsu_handle,
+ wait_time=0,
+ s_bts=BtsNumber.BTS1,
+ t_bts=BtsNumber.BTS2,
+ timeout=60):
+ """ Setup and perform a handover test case in MD8475A
+
+ Args:
+ anritsu_handle: Anritsu object.
+ s_bts: Serving (originating) BTS
+ t_bts: Target (destination) BTS
+ wait_time: time to wait before handover
+
+ Returns:
+ True for success False for failure
+ """
+ log.info("Starting HO test case procedure")
+ log.info("Serving BTS = {}, Target BTS = {}".format(s_bts, t_bts))
+ time.sleep(wait_time)
+ ho_tc = anritsu_handle.get_AnritsuTestCases()
+ ho_tc.procedure = TestProcedure.PROCEDURE_HO
+ ho_tc.bts_direction = (s_bts, t_bts)
+ ho_tc.power_control = TestPowerControl.POWER_CONTROL_DISABLE
+ ho_tc.measurement_LTE = TestMeasurement.MEASUREMENT_DISABLE
+ anritsu_handle.start_testcase()
+ status = anritsu_handle.get_testcase_status()
+ timer = 0
+ while status == "0":
+ time.sleep(1)
+ status = anritsu_handle.get_testcase_status()
+ timer += 1
+ if timer > timeout:
+ return "Handover Test Case time out in {} sec!".format(timeout)
+ return status
+
+
+def make_ims_call(log,
+ ad,
+ anritsu_handle,
+ callee_number,
+ is_emergency=False,
+ check_ims_reg=True,
+ check_ims_calling=True,
+ mo=True,
+ ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+ """ Makes a MO call after IMS registred
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritsu object.
+ callee_number: Number to be called.
+ check_ims_reg: check if Anritsu cscf server state is "SIPIDLE".
+ check_ims_calling: check if Anritsu cscf server state is "CALLING".
+ mo: Mobile originated call
+ ims_virtual_network_id: ims virtual network id.
+
+ Returns:
+ True for success False for failure
+ """
+
+ try:
+ # confirm ims registration
+ if check_ims_reg:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("IMS/CSCF status is not idle.")
+ if mo: # make MO call
+ log.info("Making Call to " + callee_number)
+ if not initiate_call(log, ad, callee_number, is_emergency):
+ raise _CallSequenceException("Initiate call failed.")
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CALLING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not calling.")
+ else: # make MT call
+ log.info("Making IMS Call to UE from MD8475A...")
+ anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+ ImsCscfCall.MAKE.value)
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.RINGING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not ringing.")
+ # answer the call on the UE
+ if not wait_and_answer_call(log, ad):
+ raise _CallSequenceException("UE Answer call Fail")
+
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CONNECTED.value):
+ raise _CallSequenceException(
+ "MD8475A IMS status is not connected.")
+ return True
+
+ except _CallSequenceException as e:
+ log.error(e)
+ return False
+
+
+def tear_down_call(log,
+ ad,
+ anritsu_handle,
+ ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+ """ Check and End a VoLTE call
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritsu object.
+ ims_virtual_network_id: ims virtual network id.
+
+ Returns:
+ True for success False for failure
+ """
+ try:
+ # end the call from phone
+ log.info("Disconnecting the call from DUT")
+ if not hangup_call(log, ad):
+ raise _CallSequenceException("Error in Hanging-Up Call on DUT.")
+ # confirm if CSCF status is back to idle
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("IMS/CSCF status is not idle.")
+ return True
+
+ except _CallSequenceException as e:
+ log.error(e)
+ return False
+ finally:
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+
+# This procedure is for VoLTE mobility test cases
+def ims_call_ho(log,
+ ad,
+ anritsu_handle,
+ callee_number,
+ is_emergency=False,
+ check_ims_reg=True,
+ check_ims_calling=True,
+ mo=True,
+ wait_time_in_volte=WAIT_TIME_IN_CALL_FOR_IMS,
+ ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+ """ Makes a MO call after IMS registred, then handover
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritsu object.
+ callee_number: Number to be called.
+ check_ims_reg: check if Anritsu cscf server state is "SIPIDLE".
+ check_ims_calling: check if Anritsu cscf server state is "CALLING".
+ mo: Mobile originated call
+ wait_time_in_volte: Time for phone in VoLTE call, not used for SRLTE
+ ims_virtual_network_id: ims virtual network id.
+
+ Returns:
+ True for success False for failure
+ """
+
+ try:
+ # confirm ims registration
+ if check_ims_reg:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("IMS/CSCF status is not idle.")
+ if mo: # make MO call
+ log.info("Making Call to " + callee_number)
+ if not initiate_call(log, ad, callee_number, is_emergency):
+ raise _CallSequenceException("Initiate call failed.")
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CALLING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not calling.")
+ else: # make MT call
+ log.info("Making IMS Call to UE from MD8475A...")
+ anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+ ImsCscfCall.MAKE.value)
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.RINGING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not ringing.")
+ # answer the call on the UE
+ if not wait_and_answer_call(log, ad):
+ raise _CallSequenceException("UE Answer call Fail")
+
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CONNECTED.value):
+ raise _CallSequenceException("Phone IMS status is not connected.")
+ log.info(
+ "Wait for {} seconds before handover".format(wait_time_in_volte))
+ time.sleep(wait_time_in_volte)
+
+ # Once VoLTE call is connected, then Handover
+ log.info("Starting handover procedure...")
+ result = handover_tc(anritsu_handle, BtsNumber.BTS1, BtsNumber.BTS2)
+ log.info("Handover procedure ends with result code {}".format(result))
+ log.info(
+ "Wait for {} seconds after handover".format(wait_time_in_volte))
+ time.sleep(wait_time_in_volte)
+
+ # check if the phone stay in call
+ if not ad.droid.telecomIsInCall():
+ raise _CallSequenceException("Call ended before delay_in_call.")
+ # end the call from phone
+ log.info("Disconnecting the call from DUT")
+ if not hangup_call(log, ad, is_emergency):
+ raise _CallSequenceException("Error in Hanging-Up Call on DUT.")
+ # confirm if CSCF status is back to idle
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("IMS/CSCF status is not idle.")
+
+ return True
+
+ except _CallSequenceException as e:
+ log.error(e)
+ return False
+ finally:
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+
+# This procedure is for SRLTE CSFB and SRVCC test cases
+def ims_call_cs_teardown(
+ log,
+ ad,
+ anritsu_handle,
+ callee_number,
+ teardown_side=CALL_TEARDOWN_PHONE,
+ is_emergency=False,
+ check_ims_reg=True,
+ check_ims_calling=True,
+ srvcc=None,
+ mo=True,
+ wait_time_in_volte=WAIT_TIME_IN_CALL_FOR_IMS,
+ wait_time_in_cs=WAIT_TIME_IN_CALL,
+ wait_time_in_alert=WAIT_TIME_IN_ALERT,
+ ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+ """ Makes a MO call after IMS registred, transit to CS, tear down the call
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritsu object.
+ callee_number: Number to be called.
+ teardown_side: the side to end the call (Phone or remote).
+ is_emergency: to make emergency call on the phone.
+ check_ims_reg: check if Anritsu cscf server state is "SIPIDLE".
+ check_ims_calling: check if Anritsu cscf server state is "CALLING".
+ srvcc: is the test case a SRVCC call.
+ mo: Mobile originated call
+ wait_time_in_volte: Time for phone in VoLTE call, not used for SRLTE
+ wait_time_in_cs: Time for phone in CS call.
+ ims_virtual_network_id: ims virtual network id.
+
+ Returns:
+ True for success False for failure
+ """
+
+ virtual_phone_handle = anritsu_handle.get_VirtualPhone()
+
+ try:
+ # confirm ims registration
+ if check_ims_reg:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.SIPIDLE.value):
+ raise _CallSequenceException("IMS/CSCF status is not idle.")
+ # confirm virtual phone in idle
+ if not wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise _CallSequenceException("Virtual Phone not idle.")
+ if mo: # make MO call
+ log.info("Making Call to " + callee_number)
+ if not initiate_call(log, ad, callee_number, is_emergency):
+ raise _CallSequenceException("Initiate call failed.")
+ else: # make MT call
+ log.info("Making IMS Call to UE from MD8475A...")
+ anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+ ImsCscfCall.MAKE.value)
+ # if check ims calling is required
+ if check_ims_calling:
+ if mo:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.CALLING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not calling.")
+ else:
+ if not wait_for_ims_cscf_status(log, anritsu_handle,
+ ims_virtual_network_id,
+ ImsCscfStatus.RINGING.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not ringing.")
+
+ # if SRVCC, check if VoLTE call is connected, then Handover
+ if srvcc != None:
+ if srvcc == "InCall":
+ if not wait_for_ims_cscf_status(
+ log, anritsu_handle, ims_virtual_network_id,
+ ImsCscfStatus.CONNECTED.value):
+ raise _CallSequenceException(
+ "Phone IMS status is not connected.")
+ # stay in call for "wait_time_in_volte" seconds
+ time.sleep(wait_time_in_volte)
+ elif srvcc == "Alert":
+ # ring for WAIT_TIME_IN_ALERT seconds
+ time.sleep(WAIT_TIME_IN_ALERT)
+ # SRVCC by handover test case procedure
+ srvcc_tc = anritsu_handle.get_AnritsuTestCases()
+ srvcc_tc.procedure = TestProcedure.PROCEDURE_HO
+ srvcc_tc.bts_direction = (BtsNumber.BTS1, BtsNumber.BTS2)
+ srvcc_tc.power_control = TestPowerControl.POWER_CONTROL_DISABLE
+ srvcc_tc.measurement_LTE = TestMeasurement.MEASUREMENT_DISABLE
+ anritsu_handle.start_testcase()
+ time.sleep(5)
+ if not mo:
+ # answer the call on the UE
+ if not wait_and_answer_call(log, ad):
+ raise _CallSequenceException("UE Answer call Fail")
+ # check if Virtual phone in the call
+ if not wait_for_virtualphone_state(
+ log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS):
+ raise _CallSequenceException("Virtual Phone not in call.")
+ # stay in call for "wait_time_in_cs" seconds
+ time.sleep(wait_time_in_cs)
+ # check if the phone stay in call
+ if not ad.droid.telecomIsInCall():
+ raise _CallSequenceException("Call ended before delay_in_call.")
+ # end the call
+ if teardown_side is CALL_TEARDOWN_REMOTE:
+ log.info("Disconnecting the call from Remote")
+ virtual_phone_handle.set_voice_on_hook()
+ if not wait_for_droid_not_in_call(log, ad,
+ MAX_WAIT_TIME_CALL_DROP):
+ raise _CallSequenceException("DUT call not drop.")
+ else:
+ log.info("Disconnecting the call from DUT")
+ if not hangup_call(log, ad, is_emergency):
+ raise _CallSequenceException(
+ "Error in Hanging-Up Call on DUT.")
+ # confirm if virtual phone status is back to idle
+ if not wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise _CallSequenceException(
+ "Virtual Phone not idle after hangup.")
+ return True
+
+ except _CallSequenceException as e:
+ log.error(e)
+ return False
+ finally:
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+
+def call_mt_setup_teardown(log,
+ ad,
+ virtual_phone_handle,
+ caller_number=None,
+ teardown_side=CALL_TEARDOWN_PHONE,
+ rat=""):
+ """ Makes a call from Anritsu Virtual phone to device and tear down the call
+
+ Args:
+ ad: Android device object.
+ virtual_phone_handle: Anritsu virtual phone handle
+ caller_number = Caller number
+ teardown_side = specifiy the side to end the call (Phone or remote)
+
+ Returns:
+ True for success False for failure
+ """
+ log.info("Receive MT Call - Making a call to the phone from remote")
+ try:
+ if not wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise Exception("Virtual Phone is not in a state to start call")
+ if caller_number is not None:
+ if rat == RAT_1XRTT:
+ virtual_phone_handle.id_c2k = caller_number
+ else:
+ virtual_phone_handle.id = caller_number
+ virtual_phone_handle.set_voice_off_hook()
+
+ if not wait_and_answer_call(log, ad, caller_number):
+ raise Exception("Answer call Fail")
+
+ time.sleep(WAIT_TIME_IN_CALL)
+
+ if not ad.droid.telecomIsInCall():
+ raise Exception("Call ended before delay_in_call.")
+ except Exception:
+ return False
+
+ if ad.droid.telecomIsInCall():
+ if teardown_side is CALL_TEARDOWN_REMOTE:
+ log.info("Disconnecting the call from Remote")
+ virtual_phone_handle.set_voice_on_hook()
+ else:
+ log.info("Disconnecting the call from Phone")
+ ad.droid.telecomEndCall()
+
+ wait_for_virtualphone_state(log, virtual_phone_handle,
+ VirtualPhoneStatus.STATUS_IDLE)
+ ensure_phone_idle(log, ad)
+
+ return True
+
+
+def wait_for_sms_deliver_success(log, ad, time_to_wait=60):
+ sms_deliver_event = EventSmsDeliverSuccess
+ sleep_interval = 2
+ status = False
+ event = None
+
+ try:
+ event = ad.ed.pop_event(sms_deliver_event, time_to_wait)
+ status = True
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+ return status
+
+
+def wait_for_sms_sent_success(log, ad, time_to_wait=60):
+ sms_sent_event = EventSmsSentSuccess
+ sleep_interval = 2
+ status = False
+ event = None
+
+ try:
+ event = ad.ed.pop_event(sms_sent_event, time_to_wait)
+ log.info(event)
+ status = True
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+ return status
+
+
+def wait_for_incoming_sms(log, ad, time_to_wait=60):
+ sms_received_event = EventSmsReceived
+ sleep_interval = 2
+ status = False
+ event = None
+
+ try:
+ event = ad.ed.pop_event(sms_received_event, time_to_wait)
+ log.info(event)
+ status = True
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+ return status, event
+
+
+def verify_anritsu_received_sms(log, vp_handle, receiver_number, message, rat):
+ if rat == RAT_1XRTT:
+ receive_sms = vp_handle.receiveSms_c2k()
+ else:
+ receive_sms = vp_handle.receiveSms()
+
+ if receive_sms == "NONE":
+ return False
+ split = receive_sms.split('&')
+ text = ""
+ if rat == RAT_1XRTT:
+ # TODO: b/26296388 There is some problem when retrieving message with é
+ # from Anritsu.
+ return True
+ for i in range(len(split)):
+ if split[i].startswith('Text='):
+ text = split[i][5:]
+ text = AnritsuUtils.gsm_decode(text)
+ break
+ # TODO: b/26296388 Verify Phone number
+ if text != message:
+ log.error("Wrong message received")
+ return False
+ return True
+
+
+def sms_mo_send(log, ad, vp_handle, receiver_number, message, rat=""):
+ try:
+ if not wait_for_virtualphone_state(log, vp_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise Exception("Virtual Phone is not in a state to receive SMS")
+ log.info("Sending SMS to " + receiver_number)
+ ad.droid.smsSendTextMessage(receiver_number, message, False)
+ log.info("Waiting for SMS sent event")
+ test_status = wait_for_sms_sent_success(log, ad)
+ if not test_status:
+ raise Exception("Failed to send SMS")
+ if not verify_anritsu_received_sms(log, vp_handle, receiver_number,
+ message, rat):
+ raise Exception("Anritsu didn't receive message")
+ except Exception as e:
+ log.error("Exception :" + str(e))
+ return False
+ return True
+
+
+def sms_mt_receive_verify(log, ad, vp_handle, sender_number, message, rat=""):
+ ad.droid.smsStartTrackingIncomingMessage()
+ try:
+ if not wait_for_virtualphone_state(log, vp_handle,
+ VirtualPhoneStatus.STATUS_IDLE):
+ raise Exception("Virtual Phone is not in a state to receive SMS")
+ log.info("Waiting for Incoming SMS from " + sender_number)
+ if rat == RAT_1XRTT:
+ vp_handle.sendSms_c2k(sender_number, message)
+ else:
+ vp_handle.sendSms(sender_number, message)
+ test_status, event = wait_for_incoming_sms(log, ad)
+ if not test_status:
+ raise Exception("Failed to receive SMS")
+ log.info("Incoming SMS: Sender " + event['data']['Sender'])
+ log.info("Incoming SMS: Message " + event['data']['Text'])
+ if event['data']['Sender'] != sender_number:
+ raise Exception("Wrong sender Number")
+ if event['data']['Text'] != message:
+ raise Exception("Wrong message")
+ except Exception as e:
+ log.error("exception: " + str(e))
+ return False
+ finally:
+ ad.droid.smsStopTrackingIncomingMessage()
+ return True
+
+
+def wait_for_ims_cscf_status(log,
+ anritsu_handle,
+ virtual_network_id,
+ status,
+ timeout=MAX_WAIT_TIME_IMS_CSCF_STATE):
+ """ Wait for IMS CSCF to be in expected state.
+
+ Args:
+ log: log object
+ anritsu_handle: anritsu object
+ virtual_network_id: virtual network id to be monitored
+ status: expected status
+ timeout: wait time
+ """
+ sleep_interval = 1
+ wait_time = timeout
+ while wait_time > 0:
+ if status == anritsu_handle.get_ims_cscf_status(virtual_network_id):
+ return True
+ time.sleep(sleep_interval)
+ wait_time = wait_time - sleep_interval
+ return False
+
+
+def wait_for_virtualphone_state(log,
+ vp_handle,
+ state,
+ timeout=MAX_WAIT_TIME_VIRTUAL_PHONE_STATE):
+ """ Waits for Anritsu Virtual phone to be in expected state
+
+ Args:
+ ad: Android device object.
+ vp_handle: Anritus virtual phone handle
+ state = expected state
+
+ Returns:
+ True for success False for failure
+ """
+ status = False
+ sleep_interval = 1
+ wait_time = timeout
+ while wait_time > 0:
+ if vp_handle.status == state:
+ log.info(vp_handle.status)
+ status = True
+ break
+ time.sleep(sleep_interval)
+ wait_time = wait_time - sleep_interval
+
+ if not status:
+ log.info("Timeout: Expected state is not received.")
+ return status
+
+
+# There is a difference between CMAS/ETWS message formation in LTE/WCDMA and CDMA 1X
+# LTE and CDMA : 3GPP
+# CDMA 1X: 3GPP2
+# hence different functions
+def cmas_receive_verify_message_lte_wcdma(
+ log, ad, anritsu_handle, serial_number, message_id, warning_message):
+ """ Makes Anritsu to send a CMAS message and phone and verifies phone
+ receives the message on LTE/WCDMA
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritus device object
+ serial_number = serial number of CMAS message
+ message_id = CMAS message ID
+ warning_message = CMAS warning message
+
+ Returns:
+ True for success False for failure
+ """
+ status = False
+ event = None
+ ad.droid.smsStartTrackingGsmEmergencyCBMessage()
+ anritsu_handle.send_cmas_lte_wcdma(
+ hex(serial_number), message_id, warning_message)
+ try:
+ log.info("Waiting for CMAS Message")
+ event = ad.ed.pop_event(EventCmasReceived, 60)
+ status = True
+ log.info(event)
+ if warning_message != event['data']['message']:
+ log.info("Wrong warning messgae received")
+ status = False
+ if message_id != hex(event['data']['serviceCategory']):
+ log.info("Wrong warning messgae received")
+ status = False
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+
+ ad.droid.smsStopTrackingGsmEmergencyCBMessage()
+ return status
+
+
+def cmas_receive_verify_message_cdma1x(
+ log,
+ ad,
+ anritsu_handle,
+ message_id,
+ service_category,
+ alert_text,
+ response_type=CMAS_C2K_RESPONSETYPE_SHELTER,
+ severity=CMAS_C2K_SEVERITY_EXTREME,
+ urgency=CMAS_C2K_URGENCY_IMMEDIATE,
+ certainty=CMAS_C2K_CERTIANTY_OBSERVED):
+ """ Makes Anritsu to send a CMAS message and phone and verifies phone
+ receives the message on CDMA 1X
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritus device object
+ serial_number = serial number of CMAS message
+ message_id = CMAS message ID
+ warning_message = CMAS warning message
+
+ Returns:
+ True for success False for failure
+ """
+ status = False
+ event = None
+ ad.droid.smsStartTrackingCdmaEmergencyCBMessage()
+ anritsu_handle.send_cmas_etws_cdma1x(message_id, service_category,
+ alert_text, response_type, severity,
+ urgency, certainty)
+ try:
+ log.info("Waiting for CMAS Message")
+ event = ad.ed.pop_event(EventCmasReceived, 60)
+ status = True
+ log.info(event)
+ if alert_text != event['data']['message']:
+ log.info("Wrong alert messgae received")
+ status = False
+
+ if event['data']['cmasResponseType'].lower() != response_type.lower():
+ log.info("Wrong response type received")
+ status = False
+
+ if event['data']['cmasUrgency'].lower() != urgency.lower():
+ log.info("Wrong cmasUrgency received")
+ status = False
+
+ if event['data']['cmasSeverity'].lower() != severity.lower():
+ log.info("Wrong cmasSeverity received")
+ status = False
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+
+ ad.droid.smsStopTrackingCdmaEmergencyCBMessage()
+ return status
+
+
+def etws_receive_verify_message_lte_wcdma(
+ log, ad, anritsu_handle, serial_number, message_id, warning_message):
+ """ Makes Anritsu to send a ETWS message and phone and verifies phone
+ receives the message on LTE/WCDMA
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritus device object
+ serial_number = serial number of ETWS message
+ message_id = ETWS message ID
+ warning_message = ETWS warning message
+
+ Returns:
+ True for success False for failure
+ """
+ status = False
+ event = None
+ if message_id == ETWS_WARNING_EARTHQUAKE:
+ warning_type = "Earthquake"
+ elif message_id == ETWS_WARNING_EARTHQUAKETSUNAMI:
+ warning_type = "EarthquakeandTsunami"
+ elif message_id == ETWS_WARNING_TSUNAMI:
+ warning_type = "Tsunami"
+ elif message_id == ETWS_WARNING_TEST_MESSAGE:
+ warning_type = "test"
+ elif message_id == ETWS_WARNING_OTHER_EMERGENCY:
+ warning_type = "other"
+ ad.droid.smsStartTrackingGsmEmergencyCBMessage()
+ anritsu_handle.send_etws_lte_wcdma(
+ hex(serial_number), message_id, warning_type, warning_message, "ON",
+ "ON")
+ try:
+ log.info("Waiting for ETWS Message")
+ event = ad.ed.pop_event(EventEtwsReceived, 60)
+ status = True
+ log.info(event)
+ # TODO: b/26296388 Event data verification
+ except Empty:
+ log.info("Timeout: Expected event is not received.")
+
+ ad.droid.smsStopTrackingGsmEmergencyCBMessage()
+ return status
+
+
+def etws_receive_verify_message_cdma1x(log, ad, anritsu_handle, serial_number,
+ message_id, warning_message):
+ """ Makes Anritsu to send a ETWS message and phone and verifies phone
+ receives the message on CDMA1X
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritus device object
+ serial_number = serial number of ETWS message
+ message_id = ETWS message ID
+ warning_message = ETWS warning message
+
+ Returns:
+ True for success False for failure
+ """
+ status = False
+ event = None
+ # TODO: b/26296388 need to add logic to check etws.
+ return status
+
+
+def read_ue_identity(log, ad, anritsu_handle, identity_type):
+ """ Get the UE identity IMSI, IMEI, IMEISV
+
+ Args:
+ ad: Android device object.
+ anritsu_handle: Anritus device object
+ identity_type: Identity type(IMSI/IMEI/IMEISV)
+
+ Returns:
+ Requested Identity value
+ """
+ return anritsu_handle.get_ue_identity(identity_type)
+
+
+def get_transmission_mode(user_params, cell_no):
+ """ Returns the TRANSMODE to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ TM to be used
+ """
+ key = "cell{}_transmission_mode".format(cell_no)
+ transmission_mode = user_params.get(key, DEFAULT_T_MODE)
+ return transmission_mode
+
+
+def get_dl_antenna(user_params, cell_no):
+ """ Returns the DL ANTENNA to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ number of DL ANTENNAS to be used
+ """
+ key = "cell{}_dl_antenna".format(cell_no)
+ dl_antenna = user_params.get(key, DEFAULT_DL_ANTENNA)
+ return dl_antenna
+
+
+def get_lte_band(user_params, cell_no, sim_card):
+ """ Returns the LTE BAND to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ LTE BAND to be used
+ """
+ key = "cell{}_lte_band".format(cell_no)
+ if sim_card == FiTMO:
+ band = Fi_LTE_TMO_BAND[cell_no - 1]
+ elif sim_card == FiSPR:
+ band = Fi_LTE_SPR_BAND[cell_no - 1]
+ elif sim_card == FiUSCC:
+ band = Fi_LTE_USCC_BAND[cell_no - 1]
+ else:
+ band = DEFAULT_LTE_BAND[cell_no - 1]
+ return user_params.get(key, band)
+
+
+def get_wcdma_band(user_params, cell_no):
+ """ Returns the WCDMA BAND to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ WCDMA BAND to be used
+ """
+ key = "cell{}_wcdma_band".format(cell_no)
+ wcdma_band = user_params.get(key, DEFAULT_WCDMA_BAND)
+ return wcdma_band
+
+
+def get_gsm_band(user_params, cell_no, sim_card):
+ """ Returns the GSM BAND to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ GSM BAND to be used
+ """
+ key = "cell{}_gsm_band".format(cell_no)
+ if sim_card == FiTMO:
+ gsm_band = Fi_GSM_TMO_BAND
+ else:
+ gsm_band = user_params.get(key, DEFAULT_GSM_BAND)
+ return gsm_band
+
+
+def get_1x_band(user_params, cell_no, sim_card):
+ """ Returns the 1X BAND to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ 1X BAND to be used
+ """
+ key = "cell{}_1x_band".format(cell_no)
+ if sim_card == FiSPR:
+ band = Fi_SPR1X_BAND
+ elif sim_card == FiUSCC:
+ band = Fi_USCC1X_BAND
+ elif sim_card == VzW12349:
+ band = VzW_CDMA1x_BAND
+ else:
+ band = DEFAULT_CDMA1X_BAND
+ return user_params.get(key, band)
+
+
+def get_evdo_band(user_params, cell_no, sim_card):
+ """ Returns the EVDO BAND to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ EVDO BAND to be used
+ """
+ key = "cell{}_evdo_band".format(cell_no)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ band = Fi_EVDO_BAND
+ elif sim_card == VzW12349:
+ band = VzW_EVDO_BAND
+ else:
+ band = DEFAULT_EVDO_BAND
+ return user_params.get(key, band)
+
+
+def get_wcdma_rac(user_params, cell_no):
+ """ Returns the WCDMA RAC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ WCDMA RAC to be used
+ """
+ key = "cell{}_wcdma_rac".format(cell_no)
+ try:
+ wcdma_rac = user_params[key]
+ except KeyError:
+ wcdma_rac = DEFAULT_RAC
+ return wcdma_rac
+
+
+def get_gsm_rac(user_params, cell_no):
+ """ Returns the GSM RAC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ GSM RAC to be used
+ """
+ key = "cell{}_gsm_rac".format(cell_no)
+ try:
+ gsm_rac = user_params[key]
+ except KeyError:
+ gsm_rac = DEFAULT_RAC
+ return gsm_rac
+
+
+def get_wcdma_lac(user_params, cell_no):
+ """ Returns the WCDMA LAC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ WCDMA LAC to be used
+ """
+ key = "cell{}_wcdma_lac".format(cell_no)
+ try:
+ wcdma_lac = user_params[key]
+ except KeyError:
+ wcdma_lac = DEFAULT_LAC
+ return wcdma_lac
+
+
+def get_gsm_lac(user_params, cell_no):
+ """ Returns the GSM LAC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ GSM LAC to be used
+ """
+ key = "cell{}_gsm_lac".format(cell_no)
+ try:
+ gsm_lac = user_params[key]
+ except KeyError:
+ gsm_lac = DEFAULT_LAC
+ return gsm_lac
+
+
+def get_lte_mcc(user_params, cell_no, sim_card):
+ """ Returns the LTE MCC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ LTE MCC to be used
+ """
+
+ key = "cell{}_lte_mcc".format(cell_no)
+ if sim_card == FiTMO:
+ mcc = Fi_TMO_MCC
+ elif sim_card == FiSPR:
+ mcc = Fi_SPR_MCC
+ elif sim_card == FiUSCC:
+ mcc = Fi_USCC_MCC
+ elif sim_card == VzW12349:
+ mcc = VzW_MCC
+ else:
+ mcc = DEFAULT_MCC
+ return user_params.get(key, mcc)
+
+
+def get_lte_mnc(user_params, cell_no, sim_card):
+ """ Returns the LTE MNC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ LTE MNC to be used
+ """
+ key = "cell{}_lte_mnc".format(cell_no)
+ if sim_card == FiTMO:
+ mnc = Fi_TMO_MNC
+ elif sim_card == FiSPR:
+ mnc = Fi_SPR_MNC
+ elif sim_card == FiUSCC:
+ mnc = Fi_USCC_MNC
+ elif sim_card == VzW12349:
+ mnc = VzW_MNC
+ else:
+ mnc = DEFAULT_MNC
+ return user_params.get(key, mnc)
+
+
+def get_wcdma_mcc(user_params, cell_no, sim_card):
+ """ Returns the WCDMA MCC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ WCDMA MCC to be used
+ """
+ key = "cell{}_wcdma_mcc".format(cell_no)
+ mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+ return user_params.get(key, mcc)
+
+
+def get_wcdma_mnc(user_params, cell_no, sim_card):
+ """ Returns the WCDMA MNC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ WCDMA MNC to be used
+ """
+ key = "cell{}_wcdma_mnc".format(cell_no)
+ mnc = VzW_MNC if sim_card == VzW12349 else DEFAULT_MNC
+ return user_params.get(key, mnc)
+
+
+def get_gsm_mcc(user_params, cell_no, sim_card):
+ """ Returns the GSM MCC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ GSM MCC to be used
+ """
+ key = "cell{}_gsm_mcc".format(cell_no)
+ mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+ return user_params.get(key, mcc)
+
+
+def get_gsm_mnc(user_params, cell_no, sim_card):
+ """ Returns the GSM MNC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ GSM MNC to be used
+ """
+ key = "cell{}_gsm_mnc".format(cell_no)
+ mnc = VzW_MNC if sim_card == VzW12349 else DEFAULT_MNC
+ return user_params.get(key, mnc)
+
+
+def get_1x_mcc(user_params, cell_no, sim_card):
+ """ Returns the 1X MCC to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ 1X MCC to be used
+ """
+ key = "cell{}_1x_mcc".format(cell_no)
+ if sim_card == FiSPR:
+ mcc = Fi_SPR1X_MCC
+ elif sim_card == FiUSCC:
+ mcc = Fi_USCC1X_MCC
+ elif sim_card == VzW12349:
+ mcc = VzW_MCC
+ else:
+ mcc = DEFAULT_MCC
+ return user_params.get(key, mcc)
+
+
+def get_1x_channel(user_params, cell_no, sim_card):
+ """ Returns the 1X Channel to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ 1X Channel to be used
+ """
+ key = "cell{}_1x_channel".format(cell_no)
+ if sim_card == FiSPR:
+ ch = Fi_SPR1X_CH
+ elif sim_card == FiUSCC:
+ ch = Fi_USCC1X_CH
+ elif sim_card == VzW12349:
+ ch = VzW_CDMA1x_CH
+ else:
+ ch = DEFAULT_CDMA1X_CH
+ return user_params.get(key, ch)
+
+
+def get_1x_sid(user_params, cell_no, sim_card):
+ """ Returns the 1X SID to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ 1X SID to be used
+ """
+ key = "cell{}_1x_sid".format(cell_no)
+ if sim_card == FiSPR:
+ sid = Fi_SPR1X_SID
+ elif sim_card == FiUSCC:
+ sid = Fi_USCC1X_SID
+ elif sim_card == VzW12349:
+ sid = VzW_CDMA1X_SID
+ else:
+ sid = DEFAULT_CDMA1X_SID
+ return user_params.get(key, sid)
+
+
+def get_1x_nid(user_params, cell_no, sim_card):
+ """ Returns the 1X NID to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ 1X NID to be used
+ """
+ key = "cell{}_1x_nid".format(cell_no)
+ if sim_card == FiSPR:
+ nid = Fi_SPR1X_NID
+ elif sim_card == FiUSCC:
+ nid = Fi_USCC1X_NID
+ elif sim_card == VzW12349:
+ nid = VzW_CDMA1X_NID
+ else:
+ nid = DEFAULT_CDMA1X_NID
+ return user_params.get(key, nid)
+
+
+def get_evdo_channel(user_params, cell_no, sim_card):
+ """ Returns the EVDO Channel to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ EVDO Channel to be used
+ """
+ key = "cell{}_evdo_channel".format(cell_no)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ ch = Fi_EVDO_CH
+ elif sim_card == VzW12349:
+ ch = VzW_EVDO_CH
+ else:
+ ch = DEFAULT_EVDO_CH
+ return user_params.get(key, ch)
+
+
+def get_evdo_sid(user_params, cell_no, sim_card):
+ """ Returns the EVDO SID to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ EVDO SID to be used
+ """
+ key = "cell{}_evdo_sid".format(cell_no)
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ sid = Fi_EVDO_SECTOR_ID
+ elif sim_card == VzW12349:
+ sid = VzW_EVDO_SECTOR_ID
+ else:
+ sid = DEFAULT_EVDO_SECTOR_ID
+ return user_params.get(key, sid)
+
+
+def get_csfb_type(user_params):
+ """ Returns the CSFB Type to be used from the user specified parameters
+ or default value
+
+ Args:
+ user_params: pointer to user supplied parameters
+ cell_no: specify the cell number this BTS is configured
+ Anritsu supports two cells. so cell_1 or cell_2
+
+ Returns:
+ CSFB Type to be used
+ """
+ try:
+ csfb_type = user_params["csfb_type"]
+ except KeyError:
+ csfb_type = CsfbType.CSFB_TYPE_REDIRECTION
+ return csfb_type
+
+
+def set_post_sim_params(anritsu_handle, user_params, sim_card):
+ if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
+ anritsu_handle.send_command("PDNCHECKAPN 1,h2g2")
+ anritsu_handle.send_command("PDNCHECKAPN 2,n.nv.ispsn")
+ anritsu_handle.send_command("PDNCHECKAPN 3,fast.t-mobile.com")
+ anritsu_handle.send_command("PDNCHECKAPN 4,ims")
+ anritsu_handle.send_command("PDNCHECKAPN 5,*")
+ anritsu_handle.send_command("PDNIMS 1,DISABLE")
+ anritsu_handle.send_command("PDNIMS 2,DISABLE")
+ anritsu_handle.send_command("PDNIMS 3,DISABLE")
+ anritsu_handle.send_command("PDNIMS 4,ENABLE")
+ anritsu_handle.send_command("PDNVNID 4,1")
+ anritsu_handle.send_command("PDNIMS 5,ENABLE")
+ anritsu_handle.send_command("PDNVNID 5,2")
+ if sim_card == P0135Ax:
+ anritsu_handle.send_command("PDNCHECKAPN 1,ims")
+ anritsu_handle.send_command("PDNCHECKAPN 2,fast.t-mobile.com")
+ anritsu_handle.send_command("PDNIMS 1,ENABLE")
+ anritsu_handle.send_command("PDNVNID 1,1")
+ anritsu_handle.send_command("PDNIMS 2,ENABLE")
+ anritsu_handle.send_command("PDNIMS 3,ENABLE")
+ anritsu_handle.send_command("PDNVNID 3,1")
+ if sim_card == VzW12349:
+ anritsu_handle.send_command("PDNCHECKAPN 1,IMS")
+ anritsu_handle.send_command("PDNCHECKAPN 2,VZWINTERNET")
+ anritsu_handle.send_command("PDNIMS 1,ENABLE")
+ anritsu_handle.send_command("PDNVNID 1,1")
+ anritsu_handle.send_command("PDNIMS 3,ENABLE")
+ anritsu_handle.send_command("PDNVNID 3,1")
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/__init__.py b/acts_tests/acts_contrib/test_utils/tel/loggers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/__init__.py b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric.proto b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric.proto
new file mode 100644
index 0000000..7984c7c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric.proto
@@ -0,0 +1,41 @@
+/* Note: If making any changes to this file be sure to generate a new
+ compiled *_pb2.py file by running the following command from this
+ directory:
+ $ protoc -I=. --python_out=. telephony_metric.proto
+
+ Be sure that you are compiling with protoc 3.4.0
+
+ More info can be found at:
+ https://developers.google.com/protocol-buffers/docs/pythontutorial
+*/
+
+syntax = "proto2";
+
+package wireless.android.platform.testing.telephony.metrics;
+
+message TelephonyVoiceTestResult {
+
+ enum CallResult {
+ UNAVAILABLE_NETWORK_TYPE = -2;
+ CALL_SETUP_FAILURE = -1;
+ SUCCESS = 0;
+ INITIATE_FAILED = 1;
+ NO_RING_EVENT_OR_ANSWER_FAILED = 2;
+ NO_CALL_ID_FOUND = 3;
+ CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT = 4;
+ AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT = 5;
+ AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED = 6;
+ CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT = 7;
+ CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED = 8;
+ CALL_HANGUP_FAIL = 9;
+ CALL_ID_CLEANUP_FAIL = 10;
+}
+
+ optional CallResult result = 1;
+ optional float call_setup_time_latency = 2;
+}
+
+message TelephonyVoiceStressResult {
+ repeated TelephonyVoiceTestResult results = 1;
+}
+
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py
new file mode 100644
index 0000000..e191960
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: telephony_metric.proto
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='telephony_metric.proto',
+ package='wireless.android.platform.testing.telephony.metrics',
+ syntax='proto2',
+ serialized_options=None,
+ create_key=_descriptor._internal_create_key,
+ serialized_pb=b'\n\x16telephony_metric.proto\x12\x33wireless.android.platform.testing.telephony.metrics\"\xf6\x04\n\x18TelephonyVoiceTestResult\x12h\n\x06result\x18\x01 \x01(\x0e\x32X.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.CallResult\x12\x1f\n\x17\x63\x61ll_setup_time_latency\x18\x02 \x01(\x02\"\xce\x03\n\nCallResult\x12%\n\x18UNAVAILABLE_NETWORK_TYPE\x10\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x1f\n\x12\x43\x41LL_SETUP_FAILURE\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x0b\n\x07SUCCESS\x10\x00\x12\x13\n\x0fINITIATE_FAILED\x10\x01\x12\"\n\x1eNO_RING_EVENT_OR_ANSWER_FAILED\x10\x02\x12\x14\n\x10NO_CALL_ID_FOUND\x10\x03\x12.\n*CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT\x10\x04\x12/\n+AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT\x10\x05\x12*\n&AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED\x10\x06\x12\x31\n-CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT\x10\x07\x12,\n(CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED\x10\x08\x12\x14\n\x10\x43\x41LL_HANGUP_FAIL\x10\t\x12\x18\n\x14\x43\x41LL_ID_CLEANUP_FAIL\x10\n\"|\n\x1aTelephonyVoiceStressResult\x12^\n\x07results\x18\x01 \x03(\x0b\x32M.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult'
+)
+
+
+
+_TELEPHONYVOICETESTRESULT_CALLRESULT = _descriptor.EnumDescriptor(
+ name='CallResult',
+ full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.CallResult',
+ filename=None,
+ file=DESCRIPTOR,
+ create_key=_descriptor._internal_create_key,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='UNAVAILABLE_NETWORK_TYPE', index=0, number=-2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_SETUP_FAILURE', index=1, number=-1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='SUCCESS', index=2, number=0,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='INITIATE_FAILED', index=3, number=1,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='NO_RING_EVENT_OR_ANSWER_FAILED', index=4, number=2,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='NO_CALL_ID_FOUND', index=5, number=3,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT', index=6, number=4,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT', index=7, number=5,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED', index=8, number=6,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT', index=9, number=7,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED', index=10, number=8,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_HANGUP_FAIL', index=11, number=9,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ _descriptor.EnumValueDescriptor(
+ name='CALL_ID_CLEANUP_FAIL', index=12, number=10,
+ serialized_options=None,
+ type=None,
+ create_key=_descriptor._internal_create_key),
+ ],
+ containing_type=None,
+ serialized_options=None,
+ serialized_start=248,
+ serialized_end=710,
+)
+_sym_db.RegisterEnumDescriptor(_TELEPHONYVOICETESTRESULT_CALLRESULT)
+
+
+_TELEPHONYVOICETESTRESULT = _descriptor.Descriptor(
+ name='TelephonyVoiceTestResult',
+ full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='result', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.result', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=-2,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ _descriptor.FieldDescriptor(
+ name='call_setup_time_latency', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.call_setup_time_latency', index=1,
+ number=2, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _TELEPHONYVOICETESTRESULT_CALLRESULT,
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=80,
+ serialized_end=710,
+)
+
+
+_TELEPHONYVOICESTRESSRESULT = _descriptor.Descriptor(
+ name='TelephonyVoiceStressResult',
+ full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ create_key=_descriptor._internal_create_key,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='results', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult.results', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ serialized_options=None,
+ is_extendable=False,
+ syntax='proto2',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=712,
+ serialized_end=836,
+)
+
+_TELEPHONYVOICETESTRESULT.fields_by_name['result'].enum_type = _TELEPHONYVOICETESTRESULT_CALLRESULT
+_TELEPHONYVOICETESTRESULT_CALLRESULT.containing_type = _TELEPHONYVOICETESTRESULT
+_TELEPHONYVOICESTRESSRESULT.fields_by_name['results'].message_type = _TELEPHONYVOICETESTRESULT
+DESCRIPTOR.message_types_by_name['TelephonyVoiceTestResult'] = _TELEPHONYVOICETESTRESULT
+DESCRIPTOR.message_types_by_name['TelephonyVoiceStressResult'] = _TELEPHONYVOICESTRESSRESULT
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+TelephonyVoiceTestResult = _reflection.GeneratedProtocolMessageType('TelephonyVoiceTestResult', (_message.Message,), {
+ 'DESCRIPTOR' : _TELEPHONYVOICETESTRESULT,
+ '__module__' : 'telephony_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult)
+ })
+_sym_db.RegisterMessage(TelephonyVoiceTestResult)
+
+TelephonyVoiceStressResult = _reflection.GeneratedProtocolMessageType('TelephonyVoiceStressResult', (_message.Message,), {
+ 'DESCRIPTOR' : _TELEPHONYVOICESTRESSRESULT,
+ '__module__' : 'telephony_metric_pb2'
+ # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult)
+ })
+_sym_db.RegisterMessage(TelephonyVoiceStressResult)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py
new file mode 100644
index 0000000..9869390
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py
@@ -0,0 +1,49 @@
+# /usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import base64
+import os
+import time
+
+from acts.metrics.core import ProtoMetric
+from acts.metrics.logger import MetricLogger
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
+
+# Initializes the path to the protobuf
+PROTO_PATH = os.path.join(os.path.dirname(__file__),
+ 'protos',
+ 'telephony_metric.proto')
+
+
+class TelephonyMetricLogger(MetricLogger):
+ """A logger for gathering Telephony test metrics
+
+ Attributes:
+ proto: Module used to store Telephony metrics in a proto
+ """
+
+ def __init__(self, event):
+ super().__init__(event=event)
+ self.proto = TelephonyVoiceTestResult()
+
+ def set_result(self, result_type):
+ self.proto.result = result_type
+
+ def end(self, event):
+ metric = ProtoMetric(name='telephony_voice_test_result',
+ data=self.proto)
+ return self.publisher.publish(metric)
+
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_atten_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_atten_utils.py
new file mode 100644
index 0000000..8e47a89
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_atten_utils.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import math
+from acts_contrib.test_utils.tel.tel_defines import ATTEN_MAX_VALUE
+from acts_contrib.test_utils.tel.tel_defines import ATTEN_MIN_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+
+
+def get_atten(log, atten_obj):
+ """Get attenuator current attenuation value.
+
+ Args:
+ log: log object.
+ atten_obj: attenuator object.
+ Returns:
+ Current attenuation value.
+ """
+ return atten_obj.get_atten()
+
+
+def set_atten(log, atten_obj, target_atten, step_size=0, time_per_step=0):
+ """Set attenuator attenuation value.
+
+ Args:
+ log: log object.
+ atten_obj: attenuator object.
+ target_atten: target attenuation value.
+ step_size: step size (in unit of dBm) for 'attenuation value setting'.
+ This is optional. Default value is 0. If step_size is 0, it means
+ the setting will be done in only one step.
+ time_per_step: delay time (in unit of second) per step when setting
+ the attenuation value.
+ This is optional. Default value is 0.
+ Returns:
+ True is no error happened. Otherwise false.
+ """
+ try:
+ print_name = atten_obj.path
+ except AttributeError:
+ print_name = str(atten_obj)
+
+ current_atten = get_atten(log, atten_obj)
+ info = "set_atten {} from {} to {}".format(print_name, current_atten,
+ target_atten)
+ if step_size > 0:
+ info += ", step size {}, time per step {}s.".format(step_size,
+ time_per_step)
+ log.info(info)
+ try:
+ delta = target_atten - current_atten
+ if step_size > 0:
+ number_of_steps = int(abs(delta) / step_size)
+ while number_of_steps > 0:
+ number_of_steps -= 1
+ current_atten += math.copysign(step_size,
+ (target_atten - current_atten))
+ atten_obj.set_atten(current_atten)
+ time.sleep(time_per_step)
+ atten_obj.set_atten(target_atten)
+ except Exception as e:
+ log.error("set_atten error happened: {}".format(e))
+ return False
+ return True
+
+
+def set_rssi(log,
+ atten_obj,
+ calibration_rssi,
+ target_rssi,
+ step_size=0,
+ time_per_step=0):
+ """Set RSSI value by changing attenuation.
+
+ Args:
+ log: log object.
+ atten_obj: attenuator object.
+ calibration_rssi: RSSI calibration information.
+ target_rssi: target RSSI value.
+ step_size: step size (in unit of dBm) for 'RSSI value setting'.
+ This is optional. Default value is 0. If step_size is 0, it means
+ the setting will be done in only one step.
+ time_per_step: delay time (in unit of second) per step when setting
+ the attenuation value.
+ This is optional. Default value is 0.
+ Returns:
+ True is no error happened. Otherwise false.
+ """
+ try:
+ print_name = atten_obj.path
+ except AttributeError:
+ print_name = str(atten_obj)
+
+ if target_rssi == MAX_RSSI_RESERVED_VALUE:
+ target_atten = ATTEN_MIN_VALUE
+ elif target_rssi == MIN_RSSI_RESERVED_VALUE:
+ target_atten = ATTEN_MAX_VALUE
+ else:
+ log.info("set_rssi {} to {}.".format(print_name, target_rssi))
+ target_atten = calibration_rssi - target_rssi
+
+ if target_atten < 0:
+ log.info("set_rssi: WARNING - you are setting an unreachable RSSI.")
+ log.info(
+ "max RSSI value on {} is {}. Setting attenuation to 0.".format(
+ print_name, calibration_rssi))
+ target_atten = 0
+ if not set_atten(log, atten_obj, target_atten, step_size, time_per_step):
+ log.error("set_rssi to {}failed".format(target_rssi))
+ return False
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py
new file mode 100644
index 0000000..bac587e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py
@@ -0,0 +1,755 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import random
+import re
+
+from acts.utils import rand_ascii_str
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
+ get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import start_youtube_video
+from acts_contrib.test_utils.tel.tel_test_utils import start_wifi_tethering
+from acts_contrib.test_utils.tel.tel_test_utils import stop_wifi_tethering
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import rat_generation_from_rat
+from acts_contrib.test_utils.tel.tel_test_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_data_attach_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts_contrib.test_utils.tel.tel_test_utils import get_wifi_usage
+from acts_contrib.test_utils.tel.tel_test_utils import check_is_wifi_connected
+
+def wifi_tethering_cleanup(log, provider, client_list):
+ """Clean up steps for WiFi Tethering.
+
+ Make sure provider turn off tethering.
+ Make sure clients reset WiFi and turn on cellular data.
+
+ Args:
+ log: log object.
+ provider: android object provide WiFi tethering.
+ client_list: a list of clients using tethered WiFi.
+
+ Returns:
+ True if no error happened. False otherwise.
+ """
+ for client in client_list:
+ client.droid.telephonyToggleDataConnection(True)
+ set_wifi_to_default(log, client)
+ if not stop_wifi_tethering(log, provider):
+ provider.log.error("Provider stop WiFi tethering failed.")
+ return False
+ if provider.droid.wifiIsApEnabled():
+ provider.log.error("Provider WiFi tethering is still enabled.")
+ return False
+ return True
+
+
+def wifi_tethering_setup_teardown(log,
+ provider,
+ client_list,
+ ap_band=WIFI_CONFIG_APBAND_2G,
+ check_interval=30,
+ check_iteration=4,
+ do_cleanup=True,
+ ssid=None,
+ password=None):
+ """Test WiFi Tethering.
+
+ Turn off WiFi on clients.
+ Turn off data and reset WiFi on clients.
+ Verify no Internet access on clients.
+ Turn on WiFi tethering on provider.
+ Clients connect to provider's WiFI.
+ Verify Internet on provider and clients.
+ Tear down WiFi tethering setup and clean up.
+
+ Args:
+ log: log object.
+ provider: android object provide WiFi tethering.
+ client_list: a list of clients using tethered WiFi.
+ ap_band: setup WiFi tethering on 2G or 5G.
+ This is optional, default value is WIFI_CONFIG_APBAND_2G
+ check_interval: delay time between each around of Internet connection check.
+ This is optional, default value is 30 (seconds).
+ check_iteration: check Internet connection for how many times in total.
+ This is optional, default value is 4 (4 times).
+ do_cleanup: after WiFi tethering test, do clean up to tear down tethering
+ setup or not. This is optional, default value is True.
+ ssid: use this string as WiFi SSID to setup tethered WiFi network.
+ This is optional. Default value is None.
+ If it's None, a random string will be generated.
+ password: use this string as WiFi password to setup tethered WiFi network.
+ This is optional. Default value is None.
+ If it's None, a random string will be generated.
+
+ Returns:
+ True if no error happened. False otherwise.
+ """
+ log.info("--->Start wifi_tethering_setup_teardown<---")
+ log.info("Provider: {}".format(provider.serial))
+ if not provider.droid.connectivityIsTetheringSupported():
+ provider.log.error(
+ "Provider does not support tethering. Stop tethering test.")
+ return False
+
+ if ssid is None:
+ ssid = rand_ascii_str(10)
+ if password is None:
+ password = rand_ascii_str(8)
+
+ # No password
+ if password == "":
+ password = None
+
+ try:
+ for client in client_list:
+ log.info("Client: {}".format(client.serial))
+ wifi_toggle_state(log, client, False)
+ client.droid.telephonyToggleDataConnection(False)
+ log.info("WiFI Tethering: Verify client have no Internet access.")
+ for client in client_list:
+ if not verify_internet_connection(
+ log, client, expected_state=False):
+ client.log.error("Turn off Data on client fail")
+ return False
+
+ provider.log.info(
+ "Provider turn on WiFi tethering. SSID: %s, password: %s", ssid,
+ password)
+
+ if not start_wifi_tethering(log, provider, ssid, password, ap_band):
+ provider.log.error("Provider start WiFi tethering failed.")
+ return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+ provider.log.info("Provider check Internet connection.")
+ if not verify_internet_connection(log, provider):
+ return False
+ for client in client_list:
+ client.log.info(
+ "Client connect to WiFi and verify AP band correct.")
+ if not ensure_wifi_connected(log, client, ssid, password):
+ client.log.error("Client connect to WiFi failed.")
+ return False
+
+ wifi_info = client.droid.wifiGetConnectionInfo()
+ if ap_band == WIFI_CONFIG_APBAND_5G:
+ if wifi_info["is_24ghz"]:
+ client.log.error("Expected 5g network. WiFi Info: %s",
+ wifi_info)
+ return False
+ else:
+ if wifi_info["is_5ghz"]:
+ client.log.error("Expected 2g network. WiFi Info: %s",
+ wifi_info)
+ return False
+
+ client.log.info("Client check Internet connection.")
+ if (not wait_for_wifi_data_connection(log, client, True)
+ or not verify_internet_connection(log, client)):
+ client.log.error("No WiFi Data on client")
+ return False
+
+ if not tethering_check_internet_connection(
+ log, provider, client_list, check_interval, check_iteration):
+ return False
+
+ finally:
+ if (do_cleanup
+ and (not wifi_tethering_cleanup(log, provider, client_list))):
+ return False
+ return True
+
+
+def tethering_check_internet_connection(log, provider, client_list,
+ check_interval, check_iteration):
+ """During tethering test, check client(s) and provider Internet connection.
+
+ Do the following for <check_iteration> times:
+ Delay <check_interval> seconds.
+ Check Tethering provider's Internet connection.
+ Check each client's Internet connection.
+
+ Args:
+ log: log object.
+ provider: android object provide WiFi tethering.
+ client_list: a list of clients using tethered WiFi.
+ check_interval: delay time between each around of Internet connection check.
+ check_iteration: check Internet connection for how many times in total.
+
+ Returns:
+ True if no error happened. False otherwise.
+ """
+ for i in range(1, check_iteration + 1):
+ result = True
+ time.sleep(check_interval)
+ provider.log.info(
+ "Provider check Internet connection after %s seconds.",
+ check_interval * i)
+ if not verify_internet_connection(log, provider):
+ result = False
+ continue
+ for client in client_list:
+ client.log.info(
+ "Client check Internet connection after %s seconds.",
+ check_interval * i)
+ if not verify_internet_connection(log, client):
+ result = False
+ break
+ if result: return result
+ return False
+
+
+def wifi_cell_switching(log, ad, wifi_network_ssid, wifi_network_pass, nw_gen):
+ """Test data connection network switching when phone on <nw_gen>.
+
+ Ensure phone is on <nw_gen>
+ Ensure WiFi can connect to live network,
+ Airplane mode is off, data connection is on, WiFi is on.
+ Turn off WiFi, verify data is on cell and browse to google.com is OK.
+ Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+ Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+ Args:
+ log: log object.
+ ad: android object.
+ wifi_network_ssid: ssid for live wifi network.
+ wifi_network_pass: password for live wifi network.
+ nw_gen: network generation the phone should be camped on.
+
+ Returns:
+ True if pass.
+ """
+ try:
+
+ if not ensure_network_generation_for_subscription(
+ log, ad, get_default_data_sub_id(ad), nw_gen,
+ MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ ad.log.error("Device failed to register in %s", nw_gen)
+ return False
+
+ start_youtube_video(ad)
+ # Ensure WiFi can connect to live network
+ ad.log.info("Make sure phone can connect to live network by WIFI")
+ if not ensure_wifi_connected(log, ad, wifi_network_ssid,
+ wifi_network_pass):
+ ad.log.error("WiFi connect fail.")
+ return False
+ log.info("Phone connected to WIFI.")
+
+ log.info("Step1 Airplane Off, WiFi On, Data On.")
+ toggle_airplane_mode(log, ad, False)
+ wifi_toggle_state(log, ad, True)
+ ad.droid.telephonyToggleDataConnection(True)
+ if (not wait_for_wifi_data_connection(log, ad, True)
+ or not verify_internet_connection(log, ad)):
+ ad.log.error("Data is not on WiFi")
+ return False
+
+ log.info("Step2 WiFi is Off, Data is on Cell.")
+ wifi_toggle_state(log, ad, False)
+ if (not wait_for_cell_data_connection(log, ad, True)
+ or not verify_internet_connection(log, ad)):
+ ad.log.error("Data did not return to cell")
+ return False
+
+ log.info("Step3 WiFi is On, Data is on WiFi.")
+ wifi_toggle_state(log, ad, True)
+ if (not wait_for_wifi_data_connection(log, ad, True)
+ or not verify_internet_connection(log, ad)):
+ ad.log.error("Data did not return to WiFi")
+ return False
+
+ log.info("Step4 WiFi is Off, Data is on Cell.")
+ wifi_toggle_state(log, ad, False)
+ if (not wait_for_cell_data_connection(log, ad, True)
+ or not verify_internet_connection(log, ad)):
+ ad.log.error("Data did not return to cell")
+ return False
+ return True
+
+ finally:
+ ad.force_stop_apk("com.google.android.youtube")
+ wifi_toggle_state(log, ad, False)
+
+
+def airplane_mode_test(log, ad, retries=3):
+ """ Test airplane mode basic on Phone and Live SIM.
+
+ Ensure phone attach, data on, WiFi off and verify Internet.
+ Turn on airplane mode to make sure detach.
+ Turn off airplane mode to make sure attach.
+ Verify Internet connection.
+
+ Args:
+ log: log object.
+ ad: android object.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ if not ensure_phones_idle(log, [ad]):
+ log.error("Failed to return phones to idle.")
+ return False
+
+ try:
+ ad.droid.telephonyToggleDataConnection(True)
+ wifi_toggle_state(log, ad, False)
+
+ ad.log.info("Step1: disable airplane mode and ensure attach")
+ if not toggle_airplane_mode(log, ad, False):
+ ad.log.error("Failed initial attach")
+ return False
+
+ if not wait_for_state(
+ get_service_state_by_adb,
+ "IN_SERVICE",
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK,
+ log,
+ ad):
+ ad.log.error("Current service state is not 'IN_SERVICE'.")
+ return False
+
+ time.sleep(30)
+ if not ad.droid.connectivityNetworkIsConnected():
+ ad.log.error("Network is NOT connected!")
+ return False
+
+ if not wait_for_cell_data_connection(log, ad, True):
+ ad.log.error("Failed to enable data connection.")
+ return False
+
+ if not verify_internet_connection(log, ad, retries=3):
+ ad.log.error("Data not available on cell.")
+ return False
+
+ log.info("Step2: enable airplane mode and ensure detach")
+ if not toggle_airplane_mode(log, ad, True):
+ ad.log.error("Failed to enable Airplane Mode")
+ return False
+ if not wait_for_cell_data_connection(log, ad, False):
+ ad.log.error("Failed to disable cell data connection")
+ return False
+
+ if not verify_internet_connection(log, ad, expected_state=False):
+ ad.log.error("Data available in airplane mode.")
+ return False
+
+ log.info("Step3: disable airplane mode and ensure attach")
+ if not toggle_airplane_mode(log, ad, False):
+ ad.log.error("Failed to disable Airplane Mode")
+ return False
+
+ if not wait_for_state(
+ get_service_state_by_adb,
+ "IN_SERVICE",
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK,
+ log,
+ ad):
+ ad.log.error("Current service state is not 'IN_SERVICE'.")
+ return False
+
+ time.sleep(30)
+ if not ad.droid.connectivityNetworkIsConnected():
+ ad.log.warning("Network is NOT connected!")
+
+ if not wait_for_cell_data_connection(log, ad, True):
+ ad.log.error("Failed to enable cell data connection")
+ return False
+
+ if not verify_internet_connection(log, ad, retries=3):
+ ad.log.warning("Data not available on cell")
+ return False
+ return True
+ finally:
+ toggle_airplane_mode(log, ad, False)
+
+
+def data_connectivity_single_bearer(log, ad, nw_gen):
+ """Test data connection: single-bearer (no voice).
+
+ Turn off airplane mode, enable Cellular Data.
+ Ensure phone data generation is expected.
+ Verify Internet.
+ Disable Cellular Data, verify Internet is inaccessible.
+ Enable Cellular Data, verify Internet.
+
+ Args:
+ log: log object.
+ ad: android object.
+ nw_gen: network generation the phone should on.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ensure_phones_idle(log, [ad])
+ wait_time = MAX_WAIT_TIME_NW_SELECTION
+ if getattr(ad, 'roaming', False):
+ wait_time = 2 * wait_time
+ if not ensure_network_generation_for_subscription(
+ log, ad, get_default_data_sub_id(ad), nw_gen,
+ MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ ad.log.error("Device failed to connect to %s in %s seconds.", nw_gen,
+ wait_time)
+ return False
+
+ try:
+ log.info("Step1 Airplane Off, Data On.")
+ toggle_airplane_mode(log, ad, False)
+ ad.droid.telephonyToggleDataConnection(True)
+ if not wait_for_cell_data_connection(log, ad, True, timeout_value=wait_time):
+ ad.log.error("Failed to enable data connection.")
+ return False
+
+ log.info("Step2 Verify internet")
+ if not verify_internet_connection(log, ad, retries=3):
+ ad.log.error("Data not available on cell.")
+ return False
+
+ log.info("Step3 Turn off data and verify not connected.")
+ ad.droid.telephonyToggleDataConnection(False)
+ if not wait_for_cell_data_connection(log, ad, False):
+ ad.log.error("Step3 Failed to disable data connection.")
+ return False
+
+ if not verify_internet_connection(log, ad, expected_state=False):
+ ad.log.error("Step3 Data still available when disabled.")
+ return False
+
+ log.info("Step4 Re-enable data.")
+ ad.droid.telephonyToggleDataConnection(True)
+ if not wait_for_cell_data_connection(log, ad, True, timeout_value=wait_time):
+ ad.log.error("Step4 failed to re-enable data.")
+ return False
+ if not verify_internet_connection(log, ad, retries=3):
+ ad.log.error("Data not available on cell.")
+ return False
+
+ if not is_droid_in_network_generation_for_subscription(
+ log, ad, get_default_data_sub_id(ad), nw_gen,
+ NETWORK_SERVICE_DATA):
+ ad.log.error("Failed: droid is no longer on correct network")
+ ad.log.info("Expected:%s, Current:%s", nw_gen,
+ rat_generation_from_rat(
+ get_network_rat_for_subscription(
+ log, ad, get_default_data_sub_id(ad),
+ NETWORK_SERVICE_DATA)))
+ return False
+ return True
+ finally:
+ ad.droid.telephonyToggleDataConnection(True)
+
+
+def change_data_sim_and_verify_data(log, ad, sim_slot):
+ """Change Data SIM and verify Data attach and Internet access
+
+ Args:
+ log: log object.
+ ad: android device object.
+ sim_slot: SIM slot index.
+
+ Returns:
+ Data SIM changed successfully, data attached and Internet access is OK.
+ """
+ sub_id = get_subid_from_slot_index(log, ad, sim_slot)
+ ad.log.info("Change Data to subId: %s, SIM slot: %s", sub_id, sim_slot)
+ set_subid_for_data(ad, sub_id)
+ if not wait_for_data_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ ad.log.error("Failed to attach data on subId:%s", sub_id)
+ return False
+ if not verify_internet_connection(log, ad):
+ ad.log.error("No Internet access after changing Data SIM.")
+ return False
+ return True
+
+def browsing_test(log, ad, wifi_ssid=None, pass_threshold_in_mb = 1.0):
+ """ Ramdomly browse 6 among 23 selected web sites. The idle time is used to
+ simulate visit duration and normally distributed with the mean 35 seconds
+ and std dev 15 seconds, which means 95% of visit duration will be between
+ 5 and 65 seconds. DUT will enter suspend mode when idle time is greater than
+ 35 seconds.
+
+ Args:
+ log: log object.
+ ad: android object.
+ pass_threshold_in_mb: minimum traffic of browsing 6 web sites in MB for
+ test pass
+
+ Returns:
+ True if the total traffic of Chrome for browsing 6 web sites is greater
+ than pass_threshold_in_mb. Otherwise False.
+ """
+ web_sites = [
+ "http://tw.yahoo.com",
+ "http://24h.pchome.com.tw",
+ "http://www.mobile01.com",
+ "https://www.android.com/phones/",
+ "http://www.books.com.tw",
+ "http://www.udn.com.tw",
+ "http://news.baidu.com",
+ "http://www.google.com",
+ "http://www.cnn.com",
+ "http://www.nytimes.com",
+ "http://www.amazon.com",
+ "http://www.wikipedia.com",
+ "http://www.ebay.com",
+ "http://www.youtube.com",
+ "http://espn.go.com",
+ "http://www.sueddeutsche.de",
+ "http://www.bild.de",
+ "http://www.welt.de",
+ "http://www.lefigaro.fr",
+ "http://www.accuweather.com",
+ "https://www.flickr.com",
+ "http://world.taobao.com",
+ "http://www.theguardian.com",
+ "http://www.abc.net.au",
+ "http://www.gumtree.com.au",
+ "http://www.commbank.com.au",
+ "http://www.news.com.au",
+ "http://rakuten.co.jp",
+ "http://livedoor.jp",
+ "http://yahoo.co.jp"]
+
+ wifi_connected = False
+ if wifi_ssid and check_is_wifi_connected(ad.log, ad, wifi_ssid):
+ wifi_connected = True
+ usage_level_at_start = get_wifi_usage(ad, apk="com.android.chrome")
+ else:
+ usage_level_at_start = get_mobile_data_usage(ad, apk="com.android.chrome")
+
+ for web_site in random.sample(web_sites, 6):
+ ad.log.info("Browsing %s..." % web_site)
+ ad.adb.shell(
+ "am start -a android.intent.action.VIEW -d %s --es "
+ "com.android.browser.application_id com.android.browser" % web_site)
+
+ idle_time = round(random.normalvariate(35, 15))
+ if idle_time < 2:
+ idle_time = 2
+ elif idle_time > 90:
+ idle_time = 90
+
+ ad.log.info(
+ "Idle time before browsing next web site: %s sec." % idle_time)
+
+ if idle_time > 35:
+ time.sleep(35)
+ rest_idle_time = idle_time-35
+ if rest_idle_time < 3:
+ rest_idle_time = 3
+ ad.log.info("Let device go to sleep for %s sec." % rest_idle_time)
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ time.sleep(rest_idle_time)
+ ad.log.info("Wake up device.")
+ ad.wakeup_screen()
+ ad.adb.shell("input keyevent 82")
+ time.sleep(3)
+ else:
+ time.sleep(idle_time)
+
+ if wifi_connected:
+ usage_level = get_wifi_usage(ad, apk="com.android.chrome")
+ else:
+ usage_level = get_mobile_data_usage(ad, apk="com.android.chrome")
+
+ try:
+ usage = round((usage_level - usage_level_at_start)/1024/1024, 2)
+ if usage < pass_threshold_in_mb:
+ ad.log.error(
+ "Usage of browsing '%s MB' is smaller than %s " % (
+ usage, pass_threshold_in_mb))
+ return False
+ else:
+ ad.log.info("Usage of browsing: %s MB" % usage)
+ return True
+ except Exception as e:
+ ad.log.error(e)
+ usage = "unknown"
+ ad.log.info("Usage of browsing: %s MB" % usage)
+ return False
+
+def reboot_test(log, ad, wifi_ssid=None):
+ """ Reboot test to verify the service availability after reboot.
+
+ Test procedure:
+ 1. Reboot
+ 2. Wait WAIT_TIME_AFTER_REBOOT for reboot complete.
+ 3. Check service state. False will be returned if service state is not "IN_SERVICE".
+ 4. Check if network is connected. False will be returned if not.
+ 5. Check if cellular data or Wi-Fi connection is available. False will be returned if not.
+ 6. Check if internet connection is available. False will be returned if not.
+ 7. Check if DSDS mode, data sub ID, voice sub ID and message sub ID still keep the same.
+ 8. Check if voice and data RAT keep the same.
+
+ Args:
+ log: log object.
+ ad: android object.
+ wifi_ssid: SSID of Wi-Fi AP for Wi-Fi connection.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ try:
+ wifi_connected = False
+ if wifi_ssid and check_is_wifi_connected(ad.log, ad, wifi_ssid):
+ wifi_connected = True
+
+ data_subid = get_default_data_sub_id(ad)
+ voice_subid = get_outgoing_voice_sub_id(ad)
+ sms_subid = get_outgoing_message_sub_id(ad)
+
+ data_rat_before_reboot = get_network_rat_for_subscription(
+ log, ad, data_subid, NETWORK_SERVICE_DATA)
+ voice_rat_before_reboot = get_network_rat_for_subscription(
+ log, ad, voice_subid, NETWORK_SERVICE_VOICE)
+
+ ad.reboot()
+ time.sleep(WAIT_TIME_AFTER_REBOOT)
+
+ if not wait_for_state(
+ get_service_state_by_adb,
+ "IN_SERVICE",
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK,
+ log,
+ ad):
+ ad.log.error("Current service state: %s" % service_state)
+ return False
+
+ if not ad.droid.connectivityNetworkIsConnected():
+ ad.log.error("Network is NOT connected!")
+ return False
+
+ if wifi_connected:
+ if not check_is_wifi_connected(ad.log, ad, wifi_ssid):
+ return False
+ else:
+ if not wait_for_cell_data_connection(log, ad, True):
+ ad.log.error("Failed to enable data connection.")
+ return False
+
+ if not verify_internet_connection(log, ad):
+ ad.log.error("Internet connection is not available")
+ return False
+
+ sim_mode = ad.droid.telephonyGetPhoneCount()
+ if hasattr(ad, "dsds"):
+ if sim_mode == 1:
+ ad.log.error("Phone is in single SIM mode after reboot.")
+ return False
+ elif sim_mode == 2:
+ ad.log.info("Phone keeps being in dual SIM mode after reboot.")
+ else:
+ if sim_mode == 1:
+ ad.log.info("Phone keeps being in single SIM mode after reboot.")
+ elif sim_mode == 2:
+ ad.log.error("Phone is in dual SIM mode after reboot.")
+ return False
+
+ data_subid_after_reboot = get_default_data_sub_id(ad)
+ if data_subid_after_reboot != data_subid:
+ ad.log.error(
+ "Data sub ID changed! (Before reboot: %s; after reboot: %s)",
+ data_subid, data_subid_after_reboot)
+ return False
+ else:
+ ad.log.info("Data sub ID does not change after reboot.")
+
+ voice_subid_after_reboot = get_outgoing_voice_sub_id(ad)
+ if voice_subid_after_reboot != voice_subid:
+ ad.log.error(
+ "Voice sub ID changed! (Before reboot: %s; after reboot: %s)",
+ voice_subid, voice_subid_after_reboot)
+ return False
+ else:
+ ad.log.info("Voice sub ID does not change after reboot.")
+
+ sms_subid_after_reboot = get_outgoing_message_sub_id(ad)
+ if sms_subid_after_reboot != sms_subid:
+ ad.log.error(
+ "Message sub ID changed! (Before reboot: %s; after reboot: %s)",
+ sms_subid, sms_subid_after_reboot)
+ return False
+ else:
+ ad.log.info("Message sub ID does not change after reboot.")
+
+ data_rat_after_reboot = get_network_rat_for_subscription(
+ log, ad, data_subid_after_reboot, NETWORK_SERVICE_DATA)
+ voice_rat_after_reboot = get_network_rat_for_subscription(
+ log, ad, voice_subid_after_reboot, NETWORK_SERVICE_VOICE)
+
+ if data_rat_after_reboot == data_rat_before_reboot:
+ ad.log.info(
+ "Data RAT (%s) does not change after reboot.",
+ data_rat_after_reboot)
+ else:
+ ad.log.error(
+ "Data RAT changed! (Before reboot: %s; after reboot: %s)",
+ data_rat_before_reboot,
+ data_rat_after_reboot)
+ return False
+
+ if voice_rat_after_reboot == voice_rat_before_reboot:
+ ad.log.info(
+ "Voice RAT (%s) does not change after reboot.",
+ voice_rat_after_reboot)
+ else:
+ ad.log.error(
+ "Voice RAT changed! (Before reboot: %s; after reboot: %s)",
+ voice_rat_before_reboot,
+ voice_rat_after_reboot)
+ return False
+
+ except Exception as e:
+ ad.log.error(e)
+ return False
+
+ return True
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_defines.py b/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
new file mode 100644
index 0000000..e5d86f4
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
@@ -0,0 +1,863 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+###############################################
+# TIMERS
+###############################################
+# Max time to wait for phone data/network connection state update
+MAX_WAIT_TIME_CONNECTION_STATE_UPDATE = 60
+
+# Max time to wait for network reselection
+MAX_WAIT_TIME_NW_SELECTION = 180
+
+# Max time to wait for call drop
+MAX_WAIT_TIME_CALL_DROP = 60
+
+# Wait time between state check retry
+WAIT_TIME_BETWEEN_STATE_CHECK = 5
+
+# Max wait time for state change
+MAX_WAIT_TIME_FOR_STATE_CHANGE = 60
+
+# Max time to wait after caller make a call and before
+# callee start ringing
+MAX_WAIT_TIME_CALLEE_RINGING = 90
+
+# country code list
+COUNTRY_CODE_LIST = [
+ "+1", "+44", "+39", "+61", "+49", "+34", "+33", "+47", "+246", "+86",
+ "+850", "+81"
+]
+
+# default pin/password
+DEFAULT_DEVICE_PASSWORD = "1111"
+
+# Wait time after enterring puk code
+WAIT_TIME_SUPPLY_PUK_CODE = 30
+
+# Max time to wait after caller make a call and before
+# callee start ringing
+MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT = 30
+
+# Max time to wait for "onCallStatehangedIdle" event after reject or ignore
+# incoming call
+MAX_WAIT_TIME_CALL_IDLE_EVENT = 60
+
+# Max time to wait after initiating a call for telecom to report in-call
+MAX_WAIT_TIME_CALL_INITIATION = 90
+
+# Time to wait after change Mode Pref for Stress Test
+WAIT_TIME_AFTER_MODE_CHANGE = 60
+
+# Max time to wait for Carrier Config Version to Update in mins
+WAIT_TIME_FOR_CARRIERCONFIG_CHANGE = 20
+
+# Max time to wait for Emergency DB Version to Update in mins
+WAIT_TIME_FOR_ER_DB_CHANGE = 10
+
+# Max time to wait after toggle airplane mode and before
+# get expected event
+MAX_WAIT_TIME_AIRPLANEMODE_EVENT = 90
+
+# Max time to wait after device sent an SMS and before
+# get "onSmsSentSuccess" event
+MAX_WAIT_TIME_SMS_SENT_SUCCESS = 60
+
+# Max time to wait after device sent an SMS and before
+# get "onSmsSentSuccess" event in case of collision.
+MAX_WAIT_TIME_SMS_SENT_SUCCESS_IN_COLLISION = 60
+
+# Max time to wait after MT SMS was sent and before device
+# actually receive this MT SMS.
+MAX_WAIT_TIME_SMS_RECEIVE = 120
+
+# Max time to wait after MT SMS was sent and before device
+# actually receive this MT SMS in case of collision.
+MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION = 1200
+
+# Max time to wait for IMS registration
+MAX_WAIT_TIME_IMS_REGISTRATION = 120
+
+# TODO: b/26338156 MAX_WAIT_TIME_VOLTE_ENABLED and MAX_WAIT_TIME_WFC_ENABLED should only
+# be used for wait after IMS registration.
+
+# Max time to wait for VoLTE enabled flag to be True
+MAX_WAIT_TIME_VOLTE_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 60
+
+# Max time to wait for WFC enabled flag to be True
+MAX_WAIT_TIME_WFC_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 120
+
+# Max time to wait for WFC enabled flag to be False
+MAX_WAIT_TIME_WFC_DISABLED = 60
+
+# Max time to wait for WiFi Manager to Connect to an AP
+MAX_WAIT_TIME_WIFI_CONNECTION = 30
+
+# Max time to wait for Video Session Modify Messaging
+MAX_WAIT_TIME_VIDEO_SESSION_EVENT = 10
+
+# Max time to wait after a network connection for ConnectivityManager to
+# report a working user plane data connection
+MAX_WAIT_TIME_USER_PLANE_DATA = 20
+
+# Max time to wait for tethering entitlement check
+MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK = 60
+
+# Max time to wait for voice mail count report correct result.
+MAX_WAIT_TIME_VOICE_MAIL_COUNT = 90
+
+# Max time to wait for data SIM change
+MAX_WAIT_TIME_DATA_SUB_CHANGE = 150
+
+# Max time to wait for telecom Ringing status after receive ringing event
+MAX_WAIT_TIME_TELECOM_RINGING = 5
+
+# Max time to wait for phone get provisioned.
+MAX_WAIT_TIME_PROVISIONING = 300
+
+# Time to wait after call setup before declaring
+# that the call is actually successful
+WAIT_TIME_IN_CALL = 30
+
+# Time to wait after call setup before declaring
+# that the call is actually successful
+WAIT_TIME_IN_CALL_LONG = 60
+
+# (For IMS, e.g. VoLTE-VoLTE, WFC-WFC, VoLTE-WFC test only)
+# Time to wait after call setup before declaring
+# that the call is actually successful
+WAIT_TIME_IN_CALL_FOR_IMS = 30
+
+# Time to wait after phone receive incoming call before phone reject this call.
+WAIT_TIME_REJECT_CALL = 2
+
+# Time to leave a voice message after callee reject the incoming call
+WAIT_TIME_LEAVE_VOICE_MAIL = 30
+
+# Time to wait after accept video call and before checking state
+WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE = 2
+
+# Time delay to ensure user actions are performed in
+# 'human' time rather than at the speed of the script
+WAIT_TIME_ANDROID_STATE_SETTLING = 1
+
+# Time to wait after registration to ensure the phone
+# has sufficient time to reconfigure based on new network
+WAIT_TIME_BETWEEN_REG_AND_CALL = 5
+
+# Wait time for data pdn to be up on CBRS
+WAIT_TIME_FOR_CBRS_DATA_SWITCH = 60
+
+# Time to wait for 1xrtt voice attach check
+# After DUT voice network type report 1xrtt (from unknown), it need to wait for
+# several seconds before the DUT can receive incoming call.
+WAIT_TIME_1XRTT_VOICE_ATTACH = 30
+
+# Time to wait for data status change during wifi tethering,.
+WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING = 30
+
+# Time to wait for rssi calibration.
+# This is the delay between <WiFi Connected> and <Turn on Screen to get RSSI>.
+WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED = 10
+# This is the delay between <Turn on Screen> and <Call API to get WiFi RSSI>.
+WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON = 2
+
+# Time to wait for each operation on voice mail box.
+WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE = 10
+
+# Time to wait for radio to up and running after reboot
+WAIT_TIME_AFTER_REBOOT = 10
+
+# Time to wait for radio to up and running after force crash
+WAIT_TIME_AFTER_CRASH = 60
+
+# Time to wait for factory data reset
+WAIT_TIME_AFTER_FDR = 60
+
+# Time to wait for boot complete after reboot
+WAIT_TIME_FOR_BOOT_COMPLETE = 75
+
+# Time to wait for tethering test after reboot
+WAIT_TIME_TETHERING_AFTER_REBOOT = 10
+
+# Time to wait after changing data sub id
+WAIT_TIME_CHANGE_DATA_SUB_ID = 30
+
+# Time to wait after changing voice sub id
+WAIT_TIME_CHANGE_VOICE_SUB_ID = 5
+
+# Time to wait after changing message sub id
+WAIT_TIME_CHANGE_MESSAGE_SUB_ID = 5
+
+# Wait time for Data Stall to detect
+WAIT_TIME_FOR_DATA_STALL = 300
+
+# Wait time for Network Validation Failed detection
+WAIT_TIME_FOR_NW_VALID_FAIL = 300
+
+# Wait time for Data Stall to recover
+WAIT_TIME_FOR_DATA_STALL_RECOVERY = 360
+
+# Callbox Power level which will cause no service on device
+POWER_LEVEL_OUT_OF_SERVICE = -100
+
+# Callbox Power level which will ensure full service on device
+POWER_LEVEL_FULL_SERVICE = -20
+
+# set a fake time to test time recovering from network
+FAKE_DATE_TIME = "010203042019.05"
+FAKE_YEAR = "2019"
+WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK = 2
+
+# These are used in phone_number_formatter
+PHONE_NUMBER_STRING_FORMAT_7_DIGIT = 7
+PHONE_NUMBER_STRING_FORMAT_10_DIGIT = 10
+PHONE_NUMBER_STRING_FORMAT_11_DIGIT = 11
+PHONE_NUMBER_STRING_FORMAT_12_DIGIT = 12
+
+# MAX screen-on time during test (in unit of second)
+MAX_SCREEN_ON_TIME = 1800
+
+# In Voice Mail box, press this digit to delete one message.
+VOICEMAIL_DELETE_DIGIT = '7'
+
+# MAX number of saved voice mail in voice mail box.
+MAX_SAVED_VOICE_MAIL = 25
+
+# SIM1 slot index
+SIM1_SLOT_INDEX = 0
+
+# SIM2 slot index
+SIM2_SLOT_INDEX = 1
+
+# invalid Subscription ID
+INVALID_SUB_ID = -1
+
+# invalid SIM slot index
+INVALID_SIM_SLOT_INDEX = -1
+
+# WiFI RSSI is -127 if WiFi is not connected
+INVALID_WIFI_RSSI = -127
+
+# MAX and MIN value for attenuator settings
+ATTEN_MAX_VALUE = 95
+ATTEN_MIN_VALUE = 0
+
+MAX_RSSI_RESERVED_VALUE = 100
+MIN_RSSI_RESERVED_VALUE = -200
+
+# cellular weak RSSI value
+CELL_WEAK_RSSI_VALUE = -105
+# cellular strong RSSI value
+CELL_STRONG_RSSI_VALUE = -70
+# WiFi weak RSSI value
+WIFI_WEAK_RSSI_VALUE = -63
+
+# Emergency call number
+DEFAULT_EMERGENCY_CALL_NUMBER = "911"
+
+EMERGENCY_CALL_NUMBERS = [
+ "08", "000", "110", "112", "118", "119", "911", "999", "*911", "#911"
+]
+
+AOSP_PREFIX = "aosp_"
+
+INCALL_UI_DISPLAY_FOREGROUND = "foreground"
+INCALL_UI_DISPLAY_BACKGROUND = "background"
+INCALL_UI_DISPLAY_DEFAULT = "default"
+
+NETWORK_CONNECTION_TYPE_WIFI = 'wifi'
+NETWORK_CONNECTION_TYPE_CELL = 'cell'
+NETWORK_CONNECTION_TYPE_MMS = 'mms'
+NETWORK_CONNECTION_TYPE_HIPRI = 'hipri'
+NETWORK_CONNECTION_TYPE_UNKNOWN = 'unknown'
+
+TETHERING_MODE_WIFI = 'wifi'
+
+# Tether interface types defined in ConnectivityManager
+TETHERING_INVALID = -1
+TETHERING_WIFI = 0
+TETHERING_USB = 1
+TETHERING_BLUETOOTH = 2
+
+NETWORK_SERVICE_VOICE = 'voice'
+NETWORK_SERVICE_DATA = 'data'
+
+CARRIER_VZW = 'vzw'
+CARRIER_ATT = 'att'
+CARRIER_TMO = 'tmo'
+CARRIER_SPT = 'spt'
+CARRIER_EEUK = 'eeuk'
+CARRIER_VFUK = 'vfuk'
+CARRIER_UNKNOWN = 'unknown'
+CARRIER_GMBH = 'gmbh'
+CARRIER_ITA = 'ita'
+CARRIER_ESP = 'esp'
+CARRIER_ORG = 'org'
+CARRIER_TEL = 'tel'
+CARRIER_TSA = 'tsa'
+CARRIER_SING = 'singtel'
+CARRIER_USCC = 'uscc'
+CARRIER_ROGERS = 'ROGERS'
+CARRIER_TELUS = 'tls'
+CARRIER_KOODO = 'kdo'
+CARRIER_VIDEOTRON = 'vtrn'
+CARRIER_BELL = 'bell'
+CARRIER_FRE = 'fre'
+CARRIER_FI = 'fi'
+CARRIER_NTT_DOCOMO = 'ntt_docomo'
+CARRIER_KDDI = 'kddi'
+CARRIER_RAKUTEN = 'rakuten'
+CARRIER_SBM = 'sbm'
+
+RAT_FAMILY_CDMA = 'cdma'
+RAT_FAMILY_CDMA2000 = 'cdma2000'
+RAT_FAMILY_IDEN = 'iden'
+RAT_FAMILY_GSM = 'gsm'
+RAT_FAMILY_WCDMA = 'wcdma'
+RAT_FAMILY_UMTS = RAT_FAMILY_WCDMA
+RAT_FAMILY_WLAN = 'wlan'
+RAT_FAMILY_LTE = 'lte'
+RAT_FAMILY_NR = 'nr'
+RAT_FAMILY_TDSCDMA = 'tdscdma'
+RAT_FAMILY_UNKNOWN = 'unknown'
+
+CAPABILITY_PHONE = 'phone'
+CAPABILITY_VOLTE = 'volte'
+CAPABILITY_VT = 'vt'
+CAPABILITY_WFC = 'wfc'
+CAPABILITY_MSIM = 'msim'
+CAPABILITY_OMADM = 'omadm'
+CAPABILITY_WFC_MODE_CHANGE = 'wfc_mode_change'
+CAPABILITY_CONFERENCE = 'conference'
+CAPABILITY_VOLTE_PROVISIONING = 'volte_provisioning'
+CAPABILITY_VOLTE_OVERRIDE_WFC_PROVISIONING = 'volte_override_wfc_provisioning'
+CAPABILITY_HIDE_ENHANCED_4G_LTE_BOOL = 'hide_enhanced_4g_lte'
+
+# Carrier Config Versions
+VZW_CARRIER_CONFIG_VERSION = "29999999999.1"
+ATT_CARRIER_CONFIG_VERSION = "28888888888.1"
+
+# Constant for operation direction
+DIRECTION_MOBILE_ORIGINATED = "MO"
+DIRECTION_MOBILE_TERMINATED = "MT"
+
+# Constant for call teardown side
+CALL_TEARDOWN_PHONE = "PHONE"
+CALL_TEARDOWN_REMOTE = "REMOTE"
+
+WIFI_VERBOSE_LOGGING_ENABLED = 1
+WIFI_VERBOSE_LOGGING_DISABLED = 0
+"""
+Begin shared constant define for both Python and Java
+"""
+
+# Constant for WiFi Calling WFC mode
+WFC_MODE_WIFI_ONLY = "WIFI_ONLY"
+WFC_MODE_CELLULAR_PREFERRED = "CELLULAR_PREFERRED"
+WFC_MODE_WIFI_PREFERRED = "WIFI_PREFERRED"
+WFC_MODE_DISABLED = "DISABLED"
+WFC_MODE_UNKNOWN = "UNKNOWN"
+
+# Constant for Video Telephony VT state
+VT_STATE_AUDIO_ONLY = "AUDIO_ONLY"
+VT_STATE_TX_ENABLED = "TX_ENABLED"
+VT_STATE_RX_ENABLED = "RX_ENABLED"
+VT_STATE_BIDIRECTIONAL = "BIDIRECTIONAL"
+VT_STATE_TX_PAUSED = "TX_PAUSED"
+VT_STATE_RX_PAUSED = "RX_PAUSED"
+VT_STATE_BIDIRECTIONAL_PAUSED = "BIDIRECTIONAL_PAUSED"
+VT_STATE_STATE_INVALID = "INVALID"
+
+# Constant for Video Telephony Video quality
+VT_VIDEO_QUALITY_DEFAULT = "DEFAULT"
+VT_VIDEO_QUALITY_UNKNOWN = "UNKNOWN"
+VT_VIDEO_QUALITY_HIGH = "HIGH"
+VT_VIDEO_QUALITY_MEDIUM = "MEDIUM"
+VT_VIDEO_QUALITY_LOW = "LOW"
+VT_VIDEO_QUALITY_INVALID = "INVALID"
+
+# Constant for Call State (for call object)
+CALL_STATE_ACTIVE = "ACTIVE"
+CALL_STATE_NEW = "NEW"
+CALL_STATE_DIALING = "DIALING"
+CALL_STATE_RINGING = "RINGING"
+CALL_STATE_HOLDING = "HOLDING"
+CALL_STATE_DISCONNECTED = "DISCONNECTED"
+CALL_STATE_PRE_DIAL_WAIT = "PRE_DIAL_WAIT"
+CALL_STATE_CONNECTING = "CONNECTING"
+CALL_STATE_DISCONNECTING = "DISCONNECTING"
+CALL_STATE_UNKNOWN = "UNKNOWN"
+CALL_STATE_INVALID = "INVALID"
+
+# Constant for PRECISE Call State (for call object)
+PRECISE_CALL_STATE_ACTIVE = "ACTIVE"
+PRECISE_CALL_STATE_ALERTING = "ALERTING"
+PRECISE_CALL_STATE_DIALING = "DIALING"
+PRECISE_CALL_STATE_INCOMING = "INCOMING"
+PRECISE_CALL_STATE_HOLDING = "HOLDING"
+PRECISE_CALL_STATE_DISCONNECTED = "DISCONNECTED"
+PRECISE_CALL_STATE_WAITING = "WAITING"
+PRECISE_CALL_STATE_DISCONNECTING = "DISCONNECTING"
+PRECISE_CALL_STATE_IDLE = "IDLE"
+PRECISE_CALL_STATE_UNKNOWN = "UNKNOWN"
+PRECISE_CALL_STATE_INVALID = "INVALID"
+
+# Constant for DC POWER STATE
+DC_POWER_STATE_LOW = "LOW"
+DC_POWER_STATE_HIGH = "HIGH"
+DC_POWER_STATE_MEDIUM = "MEDIUM"
+DC_POWER_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for Audio Route
+AUDIO_ROUTE_EARPIECE = "EARPIECE"
+AUDIO_ROUTE_BLUETOOTH = "BLUETOOTH"
+AUDIO_ROUTE_SPEAKER = "SPEAKER"
+AUDIO_ROUTE_WIRED_HEADSET = "WIRED_HEADSET"
+AUDIO_ROUTE_WIRED_OR_EARPIECE = "WIRED_OR_EARPIECE"
+
+# Constant for Call Capability
+CALL_CAPABILITY_HOLD = "HOLD"
+CALL_CAPABILITY_SUPPORT_HOLD = "SUPPORT_HOLD"
+CALL_CAPABILITY_MERGE_CONFERENCE = "MERGE_CONFERENCE"
+CALL_CAPABILITY_SWAP_CONFERENCE = "SWAP_CONFERENCE"
+CALL_CAPABILITY_UNUSED_1 = "UNUSED_1"
+CALL_CAPABILITY_RESPOND_VIA_TEXT = "RESPOND_VIA_TEXT"
+CALL_CAPABILITY_MUTE = "MUTE"
+CALL_CAPABILITY_MANAGE_CONFERENCE = "MANAGE_CONFERENCE"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_RX = "SUPPORTS_VT_LOCAL_RX"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_TX = "SUPPORTS_VT_LOCAL_TX"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = "SUPPORTS_VT_LOCAL_BIDIRECTIONAL"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_RX = "SUPPORTS_VT_REMOTE_RX"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_TX = "SUPPORTS_VT_REMOTE_TX"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = "SUPPORTS_VT_REMOTE_BIDIRECTIONAL"
+CALL_CAPABILITY_SEPARATE_FROM_CONFERENCE = "SEPARATE_FROM_CONFERENCE"
+CALL_CAPABILITY_DISCONNECT_FROM_CONFERENCE = "DISCONNECT_FROM_CONFERENCE"
+CALL_CAPABILITY_SPEED_UP_MT_AUDIO = "SPEED_UP_MT_AUDIO"
+CALL_CAPABILITY_CAN_UPGRADE_TO_VIDEO = "CAN_UPGRADE_TO_VIDEO"
+CALL_CAPABILITY_CAN_PAUSE_VIDEO = "CAN_PAUSE_VIDEO"
+CALL_CAPABILITY_UNKOWN = "UNKOWN"
+
+# Constant for Call Property
+CALL_PROPERTY_HIGH_DEF_AUDIO = "HIGH_DEF_AUDIO"
+CALL_PROPERTY_CONFERENCE = "CONFERENCE"
+CALL_PROPERTY_GENERIC_CONFERENCE = "GENERIC_CONFERENCE"
+CALL_PROPERTY_WIFI = "WIFI"
+CALL_PROPERTY_EMERGENCY_CALLBACK_MODE = "EMERGENCY_CALLBACK_MODE"
+CALL_PROPERTY_UNKNOWN = "UNKNOWN"
+
+# Constant for Call Presentation
+CALL_PRESENTATION_ALLOWED = "ALLOWED"
+CALL_PRESENTATION_RESTRICTED = "RESTRICTED"
+CALL_PRESENTATION_PAYPHONE = "PAYPHONE"
+CALL_PRESENTATION_UNKNOWN = "UNKNOWN"
+
+# Constant for Network Generation
+GEN_2G = "2G"
+GEN_3G = "3G"
+GEN_4G = "4G"
+GEN_5G = "5G"
+GEN_UNKNOWN = "UNKNOWN"
+
+# Constant for Network RAT
+RAT_IWLAN = "IWLAN"
+RAT_NR = "NR"
+RAT_LTE = "LTE"
+RAT_5G = "5G"
+RAT_4G = "4G"
+RAT_3G = "3G"
+RAT_2G = "2G"
+RAT_WCDMA = "WCDMA"
+RAT_UMTS = "UMTS"
+RAT_1XRTT = "1XRTT"
+RAT_EDGE = "EDGE"
+RAT_GPRS = "GPRS"
+RAT_HSDPA = "HSDPA"
+RAT_HSUPA = "HSUPA"
+RAT_CDMA = "CDMA"
+RAT_EVDO = "EVDO"
+RAT_EVDO_0 = "EVDO_0"
+RAT_EVDO_A = "EVDO_A"
+RAT_EVDO_B = "EVDO_B"
+RAT_IDEN = "IDEN"
+RAT_EHRPD = "EHRPD"
+RAT_HSPA = "HSPA"
+RAT_HSPAP = "HSPAP"
+RAT_GSM = "GSM"
+RAT_TD_SCDMA = "TD_SCDMA"
+RAT_GLOBAL = "GLOBAL"
+RAT_LTE_CA = "LTE_CA" # LTE Carrier Aggregation
+RAT_UNKNOWN = "UNKNOWN"
+
+# Constant for Phone Type
+PHONE_TYPE_GSM = "GSM"
+PHONE_TYPE_NONE = "NONE"
+PHONE_TYPE_CDMA = "CDMA"
+PHONE_TYPE_SIP = "SIP"
+
+# Constant for SIM Power State
+CARD_POWER_DOWN = 0
+CARD_POWER_UP = 1
+CARD_POWER_UP_PASS_THROUGH = 2
+
+# Constant for SIM State
+SIM_STATE_READY = "READY"
+SIM_STATE_UNKNOWN = "UNKNOWN"
+SIM_STATE_ABSENT = "ABSENT"
+SIM_STATE_PUK_REQUIRED = "PUK_REQUIRED"
+SIM_STATE_PIN_REQUIRED = "PIN_REQUIRED"
+SIM_STATE_NETWORK_LOCKED = "NETWORK_LOCKED"
+SIM_STATE_NOT_READY = "NOT_READY"
+SIM_STATE_PERM_DISABLED = "PERM_DISABLED"
+SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR"
+SIM_STATE_LOADED = "LOADED"
+
+SINGLE_SIM_CONFIG = "ssss"
+MULTI_SIM_CONFIG = "dsds"
+
+# Constant for Data Connection State
+DATA_STATE_CONNECTED = "CONNECTED"
+DATA_STATE_DISCONNECTED = "DISCONNECTED"
+DATA_STATE_CONNECTING = "CONNECTING"
+DATA_STATE_SUSPENDED = "SUSPENDED"
+DATA_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for Data Roaming State
+DATA_ROAMING_ENABLE = 1
+DATA_ROAMING_DISABLE = 0
+
+# Constant for ConnectivityManager Data Connection
+TYPE_MOBILE = 0
+TYPE_WIFI = 1
+
+# Constant for Telephony Manager Call State
+TELEPHONY_STATE_RINGING = "RINGING"
+TELEPHONY_STATE_IDLE = "IDLE"
+TELEPHONY_STATE_OFFHOOK = "OFFHOOK"
+TELEPHONY_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for TTY Mode
+TTY_MODE_FULL = "FULL"
+TTY_MODE_HCO = "HCO"
+TTY_MODE_OFF = "OFF"
+TTY_MODE_VCO = "VCO"
+
+# Constant for Service State
+SERVICE_STATE_EMERGENCY_ONLY = "EMERGENCY_ONLY"
+SERVICE_STATE_IN_SERVICE = "IN_SERVICE"
+SERVICE_STATE_OUT_OF_SERVICE = "OUT_OF_SERVICE"
+SERVICE_STATE_POWER_OFF = "POWER_OFF"
+SERVICE_STATE_UNKNOWN = "UNKNOWN"
+
+# Service State Mapping
+SERVICE_STATE_MAPPING = {
+ "-1": SERVICE_STATE_UNKNOWN,
+ "0": SERVICE_STATE_IN_SERVICE,
+ "1": SERVICE_STATE_OUT_OF_SERVICE,
+ "2": SERVICE_STATE_EMERGENCY_ONLY,
+ "3": SERVICE_STATE_POWER_OFF
+}
+
+# Constant for VoLTE Hand-over Service State
+VOLTE_SERVICE_STATE_HANDOVER_STARTED = "STARTED"
+VOLTE_SERVICE_STATE_HANDOVER_COMPLETED = "COMPLETED"
+VOLTE_SERVICE_STATE_HANDOVER_FAILED = "FAILED"
+VOLTE_SERVICE_STATE_HANDOVER_CANCELED = "CANCELED"
+VOLTE_SERVICE_STATE_HANDOVER_UNKNOWN = "UNKNOWN"
+
+# Constant for precise call state state listen level
+PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND = "FOREGROUND"
+PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING = "RINGING"
+PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND = "BACKGROUND"
+
+# Constants used to register or de-register for call callback events
+EVENT_CALL_STATE_CHANGED = "EVENT_STATE_CHANGED"
+EVENT_CALL_CHILDREN_CHANGED = "EVENT_CHILDREN_CHANGED"
+
+# Constants used to register or de-register for video call callback events
+EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED = "EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED"
+EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED = "EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED"
+EVENT_VIDEO_SESSION_EVENT = "EVENT_VIDEO_SESSION_EVENT"
+EVENT_VIDEO_PEER_DIMENSIONS_CHANGED = "EVENT_VIDEO_PEER_DIMENSIONS_CHANGED"
+EVENT_VIDEO_QUALITY_CHANGED = "EVENT_VIDEO_QUALITY_CHANGED"
+EVENT_VIDEO_DATA_USAGE_CHANGED = "EVENT_VIDEO_DATA_USAGE_CHANGED"
+EVENT_VIDEO_CAMERA_CAPABILITIES_CHANGED = "EVENT_VIDEO_CAMERA_CAPABILITIES_CHANGED"
+EVENT_VIDEO_INVALID = "EVENT_VIDEO_INVALID"
+
+# Constant for Video Call Session Event Name
+SESSION_EVENT_RX_PAUSE = "SESSION_EVENT_RX_PAUSE"
+SESSION_EVENT_RX_RESUME = "SESSION_EVENT_RX_RESUME"
+SESSION_EVENT_TX_START = "SESSION_EVENT_TX_START"
+SESSION_EVENT_TX_STOP = "SESSION_EVENT_TX_STOP"
+SESSION_EVENT_CAMERA_FAILURE = "SESSION_EVENT_CAMERA_FAILURE"
+SESSION_EVENT_CAMERA_READY = "SESSION_EVENT_CAMERA_READY"
+SESSION_EVENT_UNKNOWN = "SESSION_EVENT_UNKNOWN"
+
+NETWORK_MODE_WCDMA_PREF = "NETWORK_MODE_WCDMA_PREF"
+NETWORK_MODE_GSM_ONLY = "NETWORK_MODE_GSM_ONLY"
+NETWORK_MODE_WCDMA_ONLY = "NETWORK_MODE_WCDMA_ONLY"
+NETWORK_MODE_GSM_UMTS = "NETWORK_MODE_GSM_UMTS"
+NETWORK_MODE_CDMA = "NETWORK_MODE_CDMA"
+NETWORK_MODE_CDMA_NO_EVDO = "NETWORK_MODE_CDMA_NO_EVDO"
+NETWORK_MODE_EVDO_NO_CDMA = "NETWORK_MODE_EVDO_NO_CDMA"
+NETWORK_MODE_GLOBAL = "NETWORK_MODE_GLOBAL"
+NETWORK_MODE_LTE_CDMA_EVDO = "NETWORK_MODE_LTE_CDMA_EVDO"
+NETWORK_MODE_LTE_GSM_WCDMA = "NETWORK_MODE_LTE_GSM_WCDMA"
+NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA"
+NETWORK_MODE_LTE_ONLY = "NETWORK_MODE_LTE_ONLY"
+NETWORK_MODE_LTE_WCDMA = "NETWORK_MODE_LTE_WCDMA"
+NETWORK_MODE_TDSCDMA_ONLY = "NETWORK_MODE_TDSCDMA_ONLY"
+NETWORK_MODE_TDSCDMA_WCDMA = "NETWORK_MODE_TDSCDMA_WCDMA"
+NETWORK_MODE_LTE_TDSCDMA = "NETWORK_MODE_LTE_TDSCDMA"
+NETWORK_MODE_TDSCDMA_GSM = "NETWORK_MODE_TDSCDMA_GSM"
+NETWORK_MODE_LTE_TDSCDMA_GSM = "NETWORK_MODE_LTE_TDSCDMA_GSM"
+NETWORK_MODE_TDSCDMA_GSM_WCDMA = "NETWORK_MODE_TDSCDMA_GSM_WCDMA"
+NETWORK_MODE_LTE_TDSCDMA_WCDMA = "NETWORK_MODE_LTE_TDSCDMA_WCDMA"
+NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = "NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA"
+NETWORK_MODE_TDSCDMA_CDMA_EVDO_WCDMA = "NETWORK_MODE_TDSCDMA_CDMA_EVDO_WCDMA"
+NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA"
+NETWORK_MODE_NR_LTE_GSM_WCDMA = "NETWORK_MODE_NR_LTE_GSM_WCDMA"
+NETWORK_MODE_NR_ONLY = "NETWORK_MODE_NR_ONLY"
+NETWORK_MODE_NR_LTE = "NETWORK_MODE_NR_LTE"
+NETWORK_MODE_NR_LTE_CDMA_EVDO = "NETWORK_MODE_NR_LTE_CDMA_EVDO"
+NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA"
+NETWORK_MODE_NR_LTE_WCDMA = "NETWORK_MODE_NR_LTE_WCDMA"
+NETWORK_MODE_NR_LTE_TDSCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA"
+NETWORK_MODE_NR_LTE_TDSCDMA_GSM = "NETWORK_MODE_NR_LTE_TDSCDMA_GSM"
+NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA"
+NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA"
+NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA"
+
+# Carrier Config Update
+CARRIER_ID_VERSION = "3"
+ER_DB_ID_VERSION = "99999"
+
+CARRIER_ID_VERSION_P = "5"
+WAIT_TIME_FOR_CARRIERID_CHANGE = 6
+CARRIER_ID_METADATA_URL = "am broadcast -a com.google.android.gms." \
+ "phenotype.FLAG_OVERRIDE --es package 'com.google.android.configupdater'" \
+ " --es user '\*' --esa flags 'CarrierIdentification__metadata_url' " \
+ "--esa values 'https://www.gstatic.com/android/config_update/110618-" \
+ "carrier-id-metadata.txt' --esa types 'string' com.google.android.gms"
+
+CARRIER_ID_METADATA_URL_P = "am broadcast -a com.google.android.gms." \
+ "phenotype.FLAG_OVERRIDE --es package 'com.google.android.configupdater'" \
+ " --es user '\*' --esa flags 'CarrierIdentification__metadata_url' " \
+ "--esa values 'https://www.gstatic.com/android/telephony/carrierid/" \
+ "030419-p-carrier-id-metadata.txt' --esa types 'string' com.google.android.gms"
+
+CARRIER_ID_CONTENT_URL = "am broadcast -a com.google.android.gms." \
+ "phenotype.FLAG_OVERRIDE --es package 'com.google.android.configupdater'" \
+ " --es user '\*' --esa flags 'CarrierIdentification__content_url' " \
+ "--esa values 'https://www.gstatic.com/android/config_update/110618-" \
+ "carrier-id.pb' --esa types 'string' com.google.android.gms"
+
+CARRIER_ID_CONTENT_URL_P = "am broadcast -a com.google.android.gms." \
+ "phenotype.FLAG_OVERRIDE --es package 'com.google.android.configupdater'" \
+ " --es user '\*' --esa flags 'CarrierIdentification__content_url' " \
+ "--esa values 'https://www.gstatic.com/android/telephony/carrierid/" \
+ "030419-p-carrier-id.pb' --esa types 'string' com.google.android.gms"
+
+# Constant for Messaging Event Name
+EventSmsDeliverSuccess = "SmsDeliverSuccess"
+EventSmsDeliverFailure = "SmsDeliverFailure"
+EventSmsSentSuccess = "SmsSentSuccess"
+EventSmsSentFailure = "SmsSentFailure"
+EventSmsReceived = "SmsReceived"
+EventMmsSentSuccess = "MmsSentSuccess"
+EventMmsSentFailure = "MmsSentFailure"
+EventMmsDownloaded = "MmsDownloaded"
+EventWapPushReceived = "WapPushReceived"
+EventDataSmsReceived = "DataSmsReceived"
+EventCmasReceived = "CmasReceived"
+EventEtwsReceived = "EtwsReceived"
+
+# Constants for Telecom Call Management Event Name (see InCallService.java).
+EventTelecomCallAdded = "TelecomCallAdded"
+EventTelecomCallRemoved = "TelecomCallRemoved"
+
+# Constant for Telecom Call Event Name (see Call.java)
+EventTelecomCallStateChanged = "TelecomCallStateChanged"
+EventTelecomCallParentChanged = "TelecomCallParentChanged"
+EventTelecomCallChildrenChanged = "TelecomCallChildrenChanged"
+EventTelecomCallDetailsChanged = "TelecomCallDetailsChanged"
+EventTelecomCallCannedTextResponsesLoaded = "TelecomCallCannedTextResponsesLoaded"
+EventTelecomCallPostDialWait = "TelecomCallPostDialWait"
+EventTelecomCallVideoCallChanged = "TelecomCallVideoCallChanged"
+EventTelecomCallDestroyed = "TelecomCallDestroyed"
+EventTelecomCallConferenceableCallsChanged = "TelecomCallConferenceableCallsChanged"
+
+# Constant for Video Call Event Name
+EventTelecomVideoCallSessionModifyRequestReceived = "TelecomVideoCallSessionModifyRequestReceived"
+EventTelecomVideoCallSessionModifyResponseReceived = "TelecomVideoCallSessionModifyResponseReceived"
+EventTelecomVideoCallSessionEvent = "TelecomVideoCallSessionEvent"
+EventTelecomVideoCallPeerDimensionsChanged = "TelecomVideoCallPeerDimensionsChanged"
+EventTelecomVideoCallVideoQualityChanged = "TelecomVideoCallVideoQualityChanged"
+EventTelecomVideoCallDataUsageChanged = "TelecomVideoCallDataUsageChanged"
+EventTelecomVideoCallCameraCapabilities = "TelecomVideoCallCameraCapabilities"
+
+# Constant for Other Event Name
+EventCallStateChanged = "CallStateChanged"
+EventPreciseStateChanged = "PreciseStateChanged"
+EventDataConnectionRealTimeInfoChanged = "DataConnectionRealTimeInfoChanged"
+EventDataConnectionStateChanged = "DataConnectionStateChanged"
+EventServiceStateChanged = "ServiceStateChanged"
+EventSignalStrengthChanged = "SignalStrengthChanged"
+EventVolteServiceStateChanged = "VolteServiceStateChanged"
+EventMessageWaitingIndicatorChanged = "MessageWaitingIndicatorChanged"
+EventConnectivityChanged = "ConnectivityChanged"
+EventActiveDataSubIdChanged = "ActiveDataSubIdChanged"
+EventDisplayInfoChanged = "DisplayInfoChanged"
+
+# Constant for Packet Keep Alive Call Back
+EventPacketKeepaliveCallback = "PacketKeepaliveCallback"
+PacketKeepaliveCallbackStarted = "Started"
+PacketKeepaliveCallbackStopped = "Stopped"
+PacketKeepaliveCallbackError = "Error"
+PacketKeepaliveCallbackInvalid = "Invalid"
+
+# Constant for Network Call Back
+EventNetworkCallback = "NetworkCallback"
+NetworkCallbackPreCheck = "PreCheck"
+NetworkCallbackAvailable = "Available"
+NetworkCallbackLosing = "Losing"
+NetworkCallbackLost = "Lost"
+NetworkCallbackUnavailable = "Unavailable"
+NetworkCallbackCapabilitiesChanged = "CapabilitiesChanged"
+NetworkCallbackSuspended = "Suspended"
+NetworkCallbackResumed = "Resumed"
+NetworkCallbackLinkPropertiesChanged = "LinkPropertiesChanged"
+NetworkCallbackInvalid = "Invalid"
+
+class SignalStrengthContainer:
+ SIGNAL_STRENGTH_GSM = "gsmSignalStrength"
+ SIGNAL_STRENGTH_GSM_DBM = "gsmDbm"
+ SIGNAL_STRENGTH_GSM_LEVEL = "gsmLevel"
+ SIGNAL_STRENGTH_GSM_ASU_LEVEL = "gsmAsuLevel"
+ SIGNAL_STRENGTH_GSM_BIT_ERROR_RATE = "gsmBitErrorRate"
+ SIGNAL_STRENGTH_CDMA_DBM = "cdmaDbm"
+ SIGNAL_STRENGTH_CDMA_LEVEL = "cdmaLevel"
+ SIGNAL_STRENGTH_CDMA_ASU_LEVEL = "cdmaAsuLevel"
+ SIGNAL_STRENGTH_CDMA_ECIO = "cdmaEcio"
+ SIGNAL_STRENGTH_EVDO_DBM = "evdoDbm"
+ SIGNAL_STRENGTH_EVDO_ECIO = "evdoEcio"
+ SIGNAL_STRENGTH_LTE = "lteSignalStrength"
+ SIGNAL_STRENGTH_LTE_DBM = "lteDbm"
+ SIGNAL_STRENGTH_LTE_LEVEL = "lteLevel"
+ SIGNAL_STRENGTH_LTE_ASU_LEVEL = "lteAsuLevel"
+ SIGNAL_STRENGTH_DBM = "dbm"
+ SIGNAL_STRENGTH_LEVEL = "level"
+ SIGNAL_STRENGTH_ASU_LEVEL = "asuLevel"
+
+
+class MessageWaitingIndicatorContainer:
+ IS_MESSAGE_WAITING = "isMessageWaiting"
+
+
+class CallStateContainer:
+ INCOMING_NUMBER = "incomingNumber"
+ SUBSCRIPTION_ID = "subscriptionId"
+ CALL_STATE = "callState"
+
+class DisplayInfoContainer:
+ TIME = "time"
+ NETWORK = "network"
+ OVERRIDE = "override"
+ SUBSCRIPTION_ID = "subscriptionId"
+
+class OverrideNetworkContainer:
+ OVERRIDE_NETWORK_TYPE_NONE = "NONE"
+ OVERRIDE_NETWORK_TYPE_LTE_CA = "LTE_CA"
+ OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = "LTE_ADVANCED_PRO"
+ OVERRIDE_NETWORK_TYPE_NR_NSA = "NR_NSA"
+ OVERRIDE_NETWORK_TYPE_NR_MMWAVE = "NR_MMWAVE"
+
+class PreciseCallStateContainer:
+ TYPE = "type"
+ CAUSE = "cause"
+ SUBSCRIPTION_ID = "subscriptionId"
+ PRECISE_CALL_STATE = "preciseCallState"
+
+
+class DataConnectionRealTimeInfoContainer:
+ TYPE = "type"
+ TIME = "time"
+ SUBSCRIPTION_ID = "subscriptionId"
+ DATA_CONNECTION_POWER_STATE = "dataConnectionPowerState"
+
+
+class DataConnectionStateContainer:
+ TYPE = "type"
+ DATA_NETWORK_TYPE = "dataNetworkType"
+ STATE_CODE = "stateCode"
+ SUBSCRIPTION_ID = "subscriptionId"
+ DATA_CONNECTION_STATE = "dataConnectionState"
+
+
+class ServiceStateContainer:
+ VOICE_REG_STATE = "voiceRegState"
+ VOICE_NETWORK_TYPE = "voiceNetworkType"
+ DATA_REG_STATE = "dataRegState"
+ DATA_NETWORK_TYPE = "dataNetworkType"
+ OPERATOR_NAME = "operatorName"
+ OPERATOR_ID = "operatorId"
+ IS_MANUAL_NW_SELECTION = "isManualNwSelection"
+ ROAMING = "roaming"
+ IS_EMERGENCY_ONLY = "isEmergencyOnly"
+ NETWORK_ID = "networkId"
+ SYSTEM_ID = "systemId"
+ SUBSCRIPTION_ID = "subscriptionId"
+ SERVICE_STATE = "serviceState"
+
+
+class PacketKeepaliveContainer:
+ ID = "id"
+ PACKET_KEEPALIVE_EVENT = "packetKeepaliveEvent"
+
+
+class NetworkCallbackContainer:
+ ID = "id"
+ NETWORK_CALLBACK_EVENT = "networkCallbackEvent"
+ MAX_MS_TO_LIVE = "maxMsToLive"
+ RSSI = "rssi"
+
+
+class CarrierConfigs:
+ NAME_STRING = "carrier_name_string"
+ SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"
+ VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool"
+ VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool"
+ VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool"
+ VOLTE_OVERRIDE_WFC_BOOL = "carrier_volte_override_wfc_provisioning_bool"
+ VT_AVAILABLE_BOOL = "carrier_vt_available_bool"
+ ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool"
+ HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool"
+ WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool"
+ WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool"
+ EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"
+ EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool"
+ EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool"
+ DEFAULT_DATA_ROAMING_ENABLED_BOOL = "carrier_default_data_roaming_enabled_bool"
+ DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = "carrier_default_wfc_ims_roaming_enabled_bool"
+ DEFAULT_WFC_IMS_ENABLED_BOOL = "carrier_default_wfc_ims_enabled_bool"
+ DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int"
+ DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = "carrier_default_wfc_ims_roaming_enabled_bool"
+ DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int"
+
+
+"""
+End shared constant define for both Python and Java
+"""
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py b/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py
new file mode 100644
index 0000000..c794fa6
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py
@@ -0,0 +1,737 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from acts.utils import NexusModelNames
+from acts_contrib.test_utils.tel import tel_defines
+
+
+def rat_family_from_rat(rat_type):
+ return _TelTables.technology_tbl[rat_type]['rat_family']
+
+
+def rat_generation_from_rat(rat_type):
+ return _TelTables.technology_tbl[rat_type]['generation']
+
+
+def network_preference_for_generation(generation, operator, phone_type=None):
+ if not phone_type:
+ return _TelTables.operator_network_tbl[operator][generation][
+ 'network_preference']
+ else:
+ return _TelTables.operator_network_tbl_by_phone_type[phone_type][
+ generation]['network_preference']
+
+
+def rat_families_for_network_preference(network_preference):
+ return _TelTables.network_preference_tbl[network_preference][
+ 'rat_family_list']
+
+
+def rat_family_for_generation(generation, operator, phone_type=None):
+ if not phone_type:
+ return _TelTables.operator_network_tbl[operator][generation][
+ 'rat_family']
+ else:
+ return _TelTables.operator_network_tbl_by_phone_type[phone_type][
+ generation]['rat_family']
+
+
+def operator_name_from_plmn_id(plmn_id):
+ return _TelTables.operator_id_to_name[plmn_id]
+
+
+def operator_name_from_network_name(name):
+ return _TelTables.operator_name_tbl.get("name", name)
+
+
+def is_valid_rat(rat_type):
+ return True if rat_type in _TelTables.technology_tbl else False
+
+
+def is_valid_generation(gen):
+ return True if gen in _TelTables.technology_gen_tbl else False
+
+
+def is_rat_svd_capable(rat):
+ return _TelTables.technology_tbl[rat]["simultaneous_voice_data"]
+
+
+def connection_type_from_type_string(input_string):
+ if input_string in _ConnectionTables.connection_type_tbl:
+ return _ConnectionTables.connection_type_tbl[input_string]
+ return tel_defines.NETWORK_CONNECTION_TYPE_UNKNOWN
+
+
+def is_user_plane_data_type(connection_type):
+ if connection_type in _ConnectionTables.user_plane_data_type:
+ return _ConnectionTables.user_plane_data_type[connection_type]
+ return False
+
+
+# For TMO, to check if voice mail count is correct after leaving a new voice message.
+def check_tmo_voice_mail_count(voice_mail_count_before,
+ voice_mail_count_after):
+ return (voice_mail_count_after == -1)
+
+
+# For ATT, to check if voice mail count is correct after leaving a new voice message.
+def check_att_voice_mail_count(voice_mail_count_before,
+ voice_mail_count_after):
+ return (voice_mail_count_after == (voice_mail_count_before + 1))
+
+
+# For SPT, to check if voice mail count is correct after leaving a new voice message.
+def check_spt_voice_mail_count(voice_mail_count_before,
+ voice_mail_count_after):
+ return (voice_mail_count_after == (voice_mail_count_before + 1))
+
+
+def get_voice_mail_check_number(operator):
+ return _TelTables.voice_mail_number_tbl.get(operator)
+
+
+def get_voice_mail_count_check_function(operator):
+ return _TelTables.voice_mail_count_check_function_tbl.get(
+ operator, check_tmo_voice_mail_count)
+
+
+def get_voice_mail_delete_digit(operator):
+ return _TelTables.voice_mail_delete_digit_tbl.get(operator, "7")
+
+
+def get_allowable_network_preference(operator, phone_type=None):
+ if not phone_type:
+ return _TelTables.allowable_network_preference_tbl[operator]
+ else:
+ return _TelTables.allowable_network_preference_tbl_by_phone_type[
+ phone_type]
+
+
+class _ConnectionTables():
+ connection_type_tbl = {
+ 'WIFI': tel_defines.NETWORK_CONNECTION_TYPE_WIFI,
+ 'WIFI_P2P': tel_defines.NETWORK_CONNECTION_TYPE_WIFI,
+ 'MOBILE': tel_defines.NETWORK_CONNECTION_TYPE_CELL,
+ 'MOBILE_DUN': tel_defines.NETWORK_CONNECTION_TYPE_CELL,
+ 'MOBILE_HIPRI': tel_defines.NETWORK_CONNECTION_TYPE_HIPRI,
+ # TODO: b/26296489 add support for 'MOBILE_SUPL', 'MOBILE_HIPRI',
+ # 'MOBILE_FOTA', 'MOBILE_IMS', 'MOBILE_CBS', 'MOBILE_IA',
+ # 'MOBILE_EMERGENCY'
+ 'MOBILE_MMS': tel_defines.NETWORK_CONNECTION_TYPE_MMS
+ }
+
+ user_plane_data_type = {
+ tel_defines.NETWORK_CONNECTION_TYPE_WIFI: True,
+ tel_defines.NETWORK_CONNECTION_TYPE_CELL: False,
+ tel_defines.NETWORK_CONNECTION_TYPE_MMS: False,
+ tel_defines.NETWORK_CONNECTION_TYPE_UNKNOWN: False
+ }
+
+
+class _TelTables():
+ # Operator id mapping to operator name
+ # Reference: Pages 43-50 in
+ # https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2013-PDF-E.pdf [2013]
+
+ operator_name_tbl = {
+ "T-Mobile": tel_defines.CARRIER_TMO,
+ "AT&T": tel_defines.CARRIER_ATT,
+ "Verizon": tel_defines.CARRIER_VZW,
+ "Verizon Wireless": tel_defines.CARRIER_VZW,
+ "Sprint": tel_defines.CARRIER_SPT,
+ "ROGERS": tel_defines.CARRIER_ROGERS,
+ "Videotron PRTNR1": tel_defines.CARRIER_VIDEOTRON,
+ "Bell": tel_defines.CARRIER_BELL,
+ "Koodo": tel_defines.CARRIER_KOODO,
+ "Ntt Docomo" : tel_defines.CARRIER_NTT_DOCOMO,
+ "KDDI" : tel_defines.CARRIER_KDDI,
+ "Rakuten": tel_defines.CARRIER_RAKUTEN,
+ "SBM": tel_defines.CARRIER_SBM
+ }
+ operator_id_to_name = {
+
+ #VZW (Verizon Wireless)
+ '310010': tel_defines.CARRIER_VZW,
+ '310012': tel_defines.CARRIER_VZW,
+ '310013': tel_defines.CARRIER_VZW,
+ '310590': tel_defines.CARRIER_VZW,
+ '310890': tel_defines.CARRIER_VZW,
+ '310910': tel_defines.CARRIER_VZW,
+ '310110': tel_defines.CARRIER_VZW,
+ '311270': tel_defines.CARRIER_VZW,
+ '311271': tel_defines.CARRIER_VZW,
+ '311272': tel_defines.CARRIER_VZW,
+ '311273': tel_defines.CARRIER_VZW,
+ '311274': tel_defines.CARRIER_VZW,
+ '311275': tel_defines.CARRIER_VZW,
+ '311276': tel_defines.CARRIER_VZW,
+ '311277': tel_defines.CARRIER_VZW,
+ '311278': tel_defines.CARRIER_VZW,
+ '311279': tel_defines.CARRIER_VZW,
+ '311280': tel_defines.CARRIER_VZW,
+ '311281': tel_defines.CARRIER_VZW,
+ '311282': tel_defines.CARRIER_VZW,
+ '311283': tel_defines.CARRIER_VZW,
+ '311284': tel_defines.CARRIER_VZW,
+ '311285': tel_defines.CARRIER_VZW,
+ '311286': tel_defines.CARRIER_VZW,
+ '311287': tel_defines.CARRIER_VZW,
+ '311288': tel_defines.CARRIER_VZW,
+ '311289': tel_defines.CARRIER_VZW,
+ '311390': tel_defines.CARRIER_VZW,
+ '311480': tel_defines.CARRIER_VZW,
+ '311481': tel_defines.CARRIER_VZW,
+ '311482': tel_defines.CARRIER_VZW,
+ '311483': tel_defines.CARRIER_VZW,
+ '311484': tel_defines.CARRIER_VZW,
+ '311485': tel_defines.CARRIER_VZW,
+ '311486': tel_defines.CARRIER_VZW,
+ '311487': tel_defines.CARRIER_VZW,
+ '311488': tel_defines.CARRIER_VZW,
+ '311489': tel_defines.CARRIER_VZW,
+
+ #TMO (T-Mobile USA)
+ '310160': tel_defines.CARRIER_TMO,
+ '310200': tel_defines.CARRIER_TMO,
+ '310210': tel_defines.CARRIER_TMO,
+ '310220': tel_defines.CARRIER_TMO,
+ '310230': tel_defines.CARRIER_TMO,
+ '310240': tel_defines.CARRIER_TMO,
+ '310250': tel_defines.CARRIER_TMO,
+ '310260': tel_defines.CARRIER_TMO,
+ '310270': tel_defines.CARRIER_TMO,
+ '310310': tel_defines.CARRIER_TMO,
+ '310490': tel_defines.CARRIER_TMO,
+ '310660': tel_defines.CARRIER_TMO,
+ '310800': tel_defines.CARRIER_TMO,
+
+ #ATT (AT&T and Cingular)
+ '310070': tel_defines.CARRIER_ATT,
+ '310560': tel_defines.CARRIER_ATT,
+ '310670': tel_defines.CARRIER_ATT,
+ '310680': tel_defines.CARRIER_ATT,
+ '310150': tel_defines.CARRIER_ATT, #Cingular
+ '310170': tel_defines.CARRIER_ATT, #Cingular
+ '310410': tel_defines.CARRIER_ATT, #Cingular
+ '311180': tel_defines.CARRIER_ATT,
+ #Cingular Licensee Pacific Telesis Mobile Services, LLC
+
+ #Sprint (and Sprint-Nextel)
+ '310120': tel_defines.CARRIER_SPT,
+ '311490': tel_defines.CARRIER_SPT,
+ '311870': tel_defines.CARRIER_SPT,
+ '311880': tel_defines.CARRIER_SPT,
+ '312190': tel_defines.CARRIER_SPT, #Sprint-Nextel Communications Inc
+ '316010': tel_defines.CARRIER_SPT, #Sprint-Nextel Communications Inc
+ '23433': tel_defines.CARRIER_EEUK, #Orange
+ '23434': tel_defines.CARRIER_EEUK, #Orange
+ '23430': tel_defines.CARRIER_EEUK, #T-Mobile UK
+ '23431': tel_defines.CARRIER_EEUK, #Virgin Mobile (MVNO)
+ '23432': tel_defines.CARRIER_EEUK, #Virgin Mobile (MVNO)
+ '23415': tel_defines.CARRIER_VFUK,
+
+ # Google Fi
+ '312580': tel_defines.CARRIER_FI,
+
+ #USCC
+ '311580': tel_defines.CARRIER_USCC,
+
+ #Vodafone (Germany)
+ '26202': tel_defines.CARRIER_GMBH,
+ '26204': tel_defines.CARRIER_GMBH,
+ '26209': tel_defines.CARRIER_GMBH,
+ '26242': tel_defines.CARRIER_GMBH,
+ '26243': tel_defines.CARRIER_GMBH,
+
+ #Vodafone (Italy)
+ '22206': tel_defines.CARRIER_ITA,
+ '22210': tel_defines.CARRIER_ITA,
+
+ #Vodafone (Spain)
+ '21401': tel_defines.CARRIER_ESP,
+ '20406': tel_defines.CARRIER_ESP,
+
+ #Orange (France)
+ '20801': tel_defines.CARRIER_ORG,
+ '20802': tel_defines.CARRIER_ORG,
+ '20891': tel_defines.CARRIER_ORG,
+
+ #Telenor (Norway)
+ '24201': tel_defines.CARRIER_TEL,
+ '24212': tel_defines.CARRIER_TEL,
+
+ #Canada Freedom
+ '302490': tel_defines.CARRIER_FRE,
+
+ #Telstra (Australia)
+ '52501': tel_defines.CARRIER_SING,
+ '50501': tel_defines.CARRIER_TSA
+ }
+
+ technology_gen_tbl = [
+ tel_defines.GEN_2G, tel_defines.GEN_3G, tel_defines.GEN_4G
+ ]
+
+ technology_tbl = {
+ tel_defines.RAT_1XRTT: {
+ 'is_voice_rat': True,
+ 'is_data_rat': False,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_EDGE: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_2G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_GSM
+ },
+ tel_defines.RAT_GPRS: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_2G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_GSM
+ },
+ tel_defines.RAT_GSM: {
+ 'is_voice_rat': True,
+ 'is_data_rat': False,
+ 'generation': tel_defines.GEN_2G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_GSM
+ },
+ tel_defines.RAT_UMTS: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_WCDMA: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_HSDPA: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_HSUPA: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_CDMA: {
+ 'is_voice_rat': True,
+ 'is_data_rat': False,
+ 'generation': tel_defines.GEN_2G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA
+ },
+ tel_defines.RAT_EVDO: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_EVDO_0: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_EVDO_A: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_EVDO_B: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_IDEN: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_2G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_IDEN
+ },
+ tel_defines.RAT_LTE_CA: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_4G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_LTE
+ },
+ tel_defines.RAT_LTE: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_4G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_LTE
+ },
+ tel_defines.RAT_NR: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_5G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_NR
+ },
+ tel_defines.RAT_EHRPD: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+ },
+ tel_defines.RAT_HSPA: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_HSPAP: {
+ 'is_voice_rat': False,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA
+ },
+ tel_defines.RAT_IWLAN: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_4G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_WLAN
+ },
+ tel_defines.RAT_TD_SCDMA: {
+ 'is_voice_rat': True,
+ 'is_data_rat': True,
+ 'generation': tel_defines.GEN_3G,
+ 'simultaneous_voice_data': True,
+ 'rat_family': tel_defines.RAT_FAMILY_TDSCDMA
+ },
+ tel_defines.RAT_UNKNOWN: {
+ 'is_voice_rat': False,
+ 'is_data_rat': False,
+ 'generation': tel_defines.GEN_UNKNOWN,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_UNKNOWN
+ },
+ tel_defines.RAT_GLOBAL: {
+ 'is_voice_rat': False,
+ 'is_data_rat': False,
+ 'generation': tel_defines.GEN_UNKNOWN,
+ 'simultaneous_voice_data': False,
+ 'rat_family': tel_defines.RAT_FAMILY_UNKNOWN
+ }
+ }
+
+ network_preference_tbl = {
+ tel_defines.NETWORK_MODE_LTE_GSM_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA,
+ tel_defines.RAT_FAMILY_GSM
+ ]
+ },
+ tel_defines.NETWORK_MODE_GSM_UMTS: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM]
+ },
+ tel_defines.NETWORK_MODE_GSM_ONLY: {
+ 'rat_family_list': [tel_defines.RAT_FAMILY_GSM]
+ },
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_CDMA2000,
+ tel_defines.RAT_FAMILY_CDMA
+ ]
+ },
+ tel_defines.NETWORK_MODE_CDMA: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
+ },
+ tel_defines.NETWORK_MODE_CDMA_NO_EVDO: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
+ },
+ tel_defines.NETWORK_MODE_WCDMA_PREF: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM]
+ },
+ tel_defines.NETWORK_MODE_WCDMA_ONLY: {
+ 'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA]
+ },
+ tel_defines.NETWORK_MODE_EVDO_NO_CDMA: {
+ 'rat_family_list': [tel_defines.RAT_FAMILY_CDMA2000]
+ },
+ tel_defines.NETWORK_MODE_GLOBAL: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM,
+ tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+ ]
+ },
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA,
+ tel_defines.RAT_FAMILY_GSM, tel_defines.RAT_FAMILY_CDMA2000,
+ tel_defines.RAT_FAMILY_CDMA
+ ]
+ },
+ tel_defines.NETWORK_MODE_LTE_ONLY: {
+ 'rat_family_list': [tel_defines.RAT_FAMILY_LTE]
+ },
+ tel_defines.NETWORK_MODE_LTE_WCDMA: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA]
+ },
+ tel_defines.NETWORK_MODE_TDSCDMA_ONLY: {
+ 'rat_family_list': [tel_defines.RAT_FAMILY_TDSCDMA]
+ },
+ tel_defines.NETWORK_MODE_TDSCDMA_WCDMA: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_TDSCDMA, tel_defines.RAT_FAMILY_WCDMA]
+ },
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA]
+ },
+ tel_defines.NETWORK_MODE_TDSCDMA_GSM: {
+ 'rat_family_list':
+ [tel_defines.RAT_FAMILY_TDSCDMA, tel_defines.RAT_FAMILY_GSM]
+ },
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_GSM: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_GSM
+ ]
+ },
+ tel_defines.NETWORK_MODE_TDSCDMA_GSM_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_GSM
+ ]
+ },
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_LTE
+ ]
+ },
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM
+ ]
+ },
+ tel_defines.NETWORK_MODE_TDSCDMA_CDMA_EVDO_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+ ]
+ },
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: {
+ 'rat_family_list': [
+ tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+ tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM,
+ tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+ ]
+ }
+ }
+ default_umts_operator_network_tbl = {
+ tel_defines.GEN_5G: {
+ 'rat_family': tel_defines.RAT_FAMILY_NR,
+ 'network_preference': tel_defines.NETWORK_MODE_NR_LTE_GSM_WCDMA
+ },
+ tel_defines.GEN_4G: {
+ 'rat_family': tel_defines.RAT_FAMILY_LTE,
+ 'network_preference':
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+ },
+ tel_defines.GEN_3G: {
+ 'rat_family': tel_defines.RAT_FAMILY_WCDMA,
+ 'network_preference': tel_defines.NETWORK_MODE_WCDMA_ONLY
+ },
+ tel_defines.GEN_2G: {
+ 'rat_family': tel_defines.RAT_FAMILY_GSM,
+ 'network_preference': tel_defines.NETWORK_MODE_GSM_ONLY
+ }
+ }
+ default_cdma_operator_network_tbl = {
+ tel_defines.GEN_5G: {
+ 'rat_family': tel_defines.RAT_FAMILY_NR,
+ 'network_preference': tel_defines.NETWORK_MODE_NR_LTE_GSM_WCDMA
+ },
+ tel_defines.GEN_4G: {
+ 'rat_family': tel_defines.RAT_FAMILY_LTE,
+ 'network_preference':
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+ },
+ tel_defines.GEN_3G: {
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000,
+ 'network_preference': tel_defines.NETWORK_MODE_CDMA
+ },
+ tel_defines.GEN_2G: {
+ 'rat_family': tel_defines.RAT_FAMILY_CDMA2000,
+ 'network_preference': tel_defines.NETWORK_MODE_CDMA_NO_EVDO
+ }
+ }
+ operator_network_tbl = {
+ tel_defines.CARRIER_TMO: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_ATT: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_VZW: default_cdma_operator_network_tbl,
+ tel_defines.CARRIER_SPT: default_cdma_operator_network_tbl,
+ tel_defines.CARRIER_EEUK: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_VFUK: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_GMBH: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_ITA: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_ESP: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_ORG: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_TEL: default_umts_operator_network_tbl,
+ tel_defines.CARRIER_TSA: default_umts_operator_network_tbl
+ }
+ operator_network_tbl_by_phone_type = {
+ tel_defines.PHONE_TYPE_GSM: default_umts_operator_network_tbl,
+ tel_defines.PHONE_TYPE_CDMA: default_cdma_operator_network_tbl
+ }
+
+ umts_allowable_network_preference_tbl = \
+ [tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+ tel_defines.NETWORK_MODE_LTE_GSM_WCDMA,
+ tel_defines.NETWORK_MODE_WCDMA_PREF,
+ tel_defines.NETWORK_MODE_WCDMA_ONLY,
+ tel_defines.NETWORK_MODE_GSM_ONLY]
+
+ cdma_allowable_network_preference_tbl = \
+ [tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+ tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO,
+ tel_defines.NETWORK_MODE_CDMA,
+ tel_defines.NETWORK_MODE_CDMA_NO_EVDO,
+ tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA]
+
+ allowable_network_preference_tbl = {
+ tel_defines.CARRIER_TMO: umts_allowable_network_preference_tbl,
+ tel_defines.CARRIER_ATT: umts_allowable_network_preference_tbl,
+ tel_defines.CARRIER_VZW: cdma_allowable_network_preference_tbl,
+ tel_defines.CARRIER_SPT: cdma_allowable_network_preference_tbl,
+ tel_defines.CARRIER_EEUK: umts_allowable_network_preference_tbl,
+ tel_defines.CARRIER_VFUK: umts_allowable_network_preference_tbl
+ }
+ allowable_network_preference_tbl_by_phone_type = {
+ tel_defines.PHONE_TYPE_GSM: umts_allowable_network_preference_tbl,
+ tel_defines.PHONE_TYPE_CDMA: cdma_allowable_network_preference_tbl
+ }
+
+ voice_mail_number_tbl = {
+ tel_defines.CARRIER_TMO: "123",
+ tel_defines.CARRIER_VZW: "*86",
+ tel_defines.CARRIER_ATT: None,
+ tel_defines.CARRIER_SPT: None,
+ tel_defines.CARRIER_EEUK: "+447953222222",
+ tel_defines.CARRIER_NTT_DOCOMO: "1417",
+ tel_defines.CARRIER_KDDI: "1417",
+ tel_defines.CARRIER_RAKUTEN: "1417",
+ tel_defines.CARRIER_SBM: "1416"
+ }
+
+ voice_mail_count_check_function_tbl = {
+ tel_defines.CARRIER_TMO: check_tmo_voice_mail_count,
+ tel_defines.CARRIER_ATT: check_att_voice_mail_count,
+ tel_defines.CARRIER_SPT: check_spt_voice_mail_count
+ }
+
+ voice_mail_delete_digit_tbl = {
+ tel_defines.CARRIER_EEUK: "3",
+ tel_defines.CARRIER_NTT_DOCOMO: "3",
+ tel_defines.CARRIER_KDDI: "9"
+ }
+
+
+device_capabilities = {
+ NexusModelNames.ONE:
+ [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_MSIM],
+ NexusModelNames.N5: [tel_defines.CAPABILITY_PHONE],
+ NexusModelNames.N5v2: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+ ],
+ NexusModelNames.N6: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+ ],
+ NexusModelNames.N6v2: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+ ],
+ NexusModelNames.N5v3: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+ tel_defines.CAPABILITY_VT
+ ],
+ NexusModelNames.N6v3: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+ tel_defines.CAPABILITY_VT
+ ],
+ "default": [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+ tel_defines.CAPABILITY_VT
+ ]
+}
+
+operator_capabilities = {
+ tel_defines.CARRIER_VZW: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+ tel_defines.CAPABILITY_VT
+ ],
+ tel_defines.CARRIER_ATT:
+ [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE],
+ tel_defines.CARRIER_TMO: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+ tel_defines.CAPABILITY_WFC, tel_defines.CAPABILITY_VT
+ ],
+ tel_defines.CARRIER_SPT: [tel_defines.CAPABILITY_PHONE],
+ tel_defines.CARRIER_ROGERS: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+ tel_defines.CAPABILITY_WFC
+ ],
+ tel_defines.CARRIER_EEUK: [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+ tel_defines.CAPABILITY_WFC
+ ],
+ tel_defines.CARRIER_VFUK: [tel_defines.CAPABILITY_PHONE],
+ "default": [tel_defines.CAPABILITY_PHONE]
+}
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py
new file mode 100644
index 0000000..69d9e11
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py
@@ -0,0 +1,517 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is test util for subscription setup.
+# It will be deleted once we have better solution for subscription ids.
+from future import standard_library
+standard_library.install_aliases()
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+
+import time
+
+
+def initial_set_up_for_subid_infomation(log, ad):
+ """Initial subid setup for voice, message and data according to ad's
+ attribute.
+
+ Setup sub id properties for android device. Including the followings:
+ incoming_voice_sub_id
+ incoming_message_sub_id
+ outgoing_voice_sub_id
+ outgoing_message_sub_id
+ default_data_sub_id
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ None
+ """
+ # outgoing_voice_sub_id
+ # If default_voice_sim_slot_index is set in config file, then use sub_id
+ # of this SIM as default_outgoing_sub_id. If default_voice_sim_slot_index
+ # is not set, then use default voice sub_id as default_outgoing_sub_id.
+ # Outgoing voice call will be made on default_outgoing_sub_id by default.
+ if hasattr(ad, "default_voice_sim_slot_index"):
+ outgoing_voice_sub_id = get_subid_from_slot_index(
+ log, ad, ad.default_voice_sim_slot_index)
+ set_subid_for_outgoing_call(ad, outgoing_voice_sub_id)
+ else:
+ outgoing_voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ setattr(ad, "outgoing_voice_sub_id", outgoing_voice_sub_id)
+
+ # outgoing_message_sub_id
+ # If default_message_sim_slot_index is set in config file, then use sub_id
+ # of this SIM as outgoing_message_sub_id. If default_message_sim_slot_index
+ # is not set, then use default Sms sub_id as outgoing_message_sub_id.
+ # Outgoing SMS will be sent on outgoing_message_sub_id by default.
+ if hasattr(ad, "default_message_sim_slot_index"):
+ outgoing_message_sub_id = get_subid_from_slot_index(
+ log, ad, ad.default_message_sim_slot_index)
+ set_subid_for_message(ad, outgoing_message_sub_id)
+ else:
+ outgoing_message_sub_id = ad.droid.subscriptionGetDefaultSmsSubId()
+ setattr(ad, "outgoing_message_sub_id", outgoing_message_sub_id)
+
+ # default_data_sub_id
+ # If default_data_sim_slot_index is set in config file, then use sub_id
+ # of this SIM as default_data_sub_id. If default_data_sim_slot_index
+ # is not set, then use default Data sub_id as default_data_sub_id.
+ # Data connection will be established on default_data_sub_id by default.
+ if hasattr(ad, "default_data_sim_slot_index"):
+ default_data_sub_id = get_subid_from_slot_index(
+ log, ad, ad.default_data_sim_slot_index)
+ set_subid_for_data(ad, default_data_sub_id, 0)
+ else:
+ default_data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
+ setattr(ad, "default_data_sub_id", default_data_sub_id)
+
+ # This is for Incoming Voice Sub ID
+ # If "incoming_voice_sim_slot_index" is set in config file, then
+ # incoming voice call will call to the phone number of the SIM in
+ # "incoming_voice_sim_slot_index".
+ # If "incoming_voice_sim_slot_index" is NOT set in config file,
+ # then incoming voice call will call to the phone number of default
+ # subId.
+ if hasattr(ad, "incoming_voice_sim_slot_index"):
+ incoming_voice_sub_id = get_subid_from_slot_index(
+ log, ad, ad.incoming_voice_sim_slot_index)
+ else:
+ incoming_voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ setattr(ad, "incoming_voice_sub_id", incoming_voice_sub_id)
+
+ # This is for Incoming SMS Sub ID
+ # If "incoming_message_sim_slot_index" is set in config file, then
+ # incoming SMS be sent to the phone number of the SIM in
+ # "incoming_message_sim_slot_index".
+ # If "incoming_message_sim_slot_index" is NOT set in config file,
+ # then incoming SMS be sent to the phone number of default
+ # subId.
+ if hasattr(ad, "incoming_message_sim_slot_index"):
+ incoming_message_sub_id = get_subid_from_slot_index(
+ log, ad, ad.incoming_message_sim_slot_index)
+ else:
+ incoming_message_sub_id = ad.droid.subscriptionGetDefaultSmsSubId()
+ setattr(ad, "incoming_message_sub_id", incoming_message_sub_id)
+
+
+def get_default_data_sub_id(ad):
+ """ Get default data subscription id
+ """
+ if hasattr(ad, "default_data_sub_id"):
+ return ad.default_data_sub_id
+ else:
+ return ad.droid.subscriptionGetDefaultDataSubId()
+
+
+def get_outgoing_message_sub_id(ad):
+ """ Get outgoing message subscription id
+ """
+ if hasattr(ad, "outgoing_message_sub_id"):
+ return ad.outgoing_message_sub_id
+ else:
+ return ad.droid.subscriptionGetDefaultSmsSubId()
+
+
+def get_outgoing_voice_sub_id(ad):
+ """ Get outgoing voice subscription id
+ """
+ if hasattr(ad, "outgoing_voice_sub_id"):
+ return ad.outgoing_voice_sub_id
+ else:
+ return ad.droid.subscriptionGetDefaultVoiceSubId()
+
+
+def get_incoming_voice_sub_id(ad):
+ """ Get incoming voice subscription id
+ """
+ if hasattr(ad, "incoming_voice_sub_id"):
+ return ad.incoming_voice_sub_id
+ else:
+ return ad.droid.subscriptionGetDefaultVoiceSubId()
+
+
+def get_incoming_message_sub_id(ad):
+ """ Get incoming message subscription id
+ """
+ if hasattr(ad, "incoming_message_sub_id"):
+ return ad.incoming_message_sub_id
+ else:
+ return ad.droid.subscriptionGetDefaultSmsSubId()
+
+
+def get_subid_from_slot_index(log, ad, sim_slot_index):
+ """ Get the subscription ID for a SIM at a particular slot
+
+ Args:
+ ad: android_device object.
+
+ Returns:
+ result: Subscription ID
+ """
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ for info in subInfo:
+ if info['simSlotIndex'] == sim_slot_index:
+ return info['subscriptionId']
+ return INVALID_SUB_ID
+
+
+def get_operatorname_from_slot_index(ad, sim_slot_index):
+ """ Get the operator name for a SIM at a particular slot
+
+ Args:
+ ad: android_device object.
+
+ Returns:
+ result: Operator Name
+ """
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ for info in subInfo:
+ if info['simSlotIndex'] == sim_slot_index:
+ return info['displayName']
+ return None
+
+
+def get_carrierid_from_slot_index(ad, sim_slot_index):
+ """ Get the carrierId for a SIM at a particular slot
+
+ Args:
+ ad: android_device object.
+ sim_slot_index: slot 0 or slot 1
+
+ Returns:
+ result: CarrierId
+ """
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ for info in subInfo:
+ if info['simSlotIndex'] == sim_slot_index:
+ return info['carrierId']
+ return None
+
+def get_isopportunistic_from_slot_index(ad, sim_slot_index):
+ """ Get the isOppotunistic field for a particular slot
+
+ Args:
+ ad: android_device object.
+ sim_slot_index: slot 0 or slot 1
+
+ Returns:
+ result: True or False based on Value set
+ """
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ for info in subInfo:
+ if info['simSlotIndex'] == sim_slot_index:
+ return info['isOpportunistic']
+ return None
+
+def set_subid_for_data(ad, sub_id, time_to_sleep=WAIT_TIME_CHANGE_DATA_SUB_ID):
+ """Set subId for data
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ # TODO: Need to check onSubscriptionChanged event. b/27843365
+ if ad.droid.subscriptionGetDefaultDataSubId() != sub_id:
+ ad.droid.subscriptionSetDefaultDataSubId(sub_id)
+ time.sleep(time_to_sleep)
+ setattr(ad, "default_data_sub_id", sub_id)
+
+
+def set_subid_for_message(ad, sub_id):
+ """Set subId for outgoing message
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
+ if hasattr(ad, "outgoing_message_sub_id"):
+ ad.outgoing_message_sub_id = sub_id
+
+def set_message_subid(ad, sub_id):
+ """Set subId for both outgoing and incoming messages
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
+ if hasattr(ad, "outgoing_message_sub_id"):
+ ad.outgoing_message_sub_id = sub_id
+ if hasattr(ad, "incoming_message_sub_id"):
+ ad.incoming_message_sub_id = sub_id
+
+
+def set_subid_for_outgoing_call(ad, sub_id):
+ """Set subId for outgoing voice call
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.telecomSetUserSelectedOutgoingPhoneAccountBySubId(sub_id)
+ if hasattr(ad, "outgoing_voice_sub_id"):
+ ad.outgoing_voice_sub_id = sub_id
+
+
+def set_incoming_voice_sub_id(ad, sub_id):
+ """Set default subId for voice calls
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
+ if hasattr(ad, "incoming_voice_sub_id"):
+ ad.incoming_voice_sub_id = sub_id
+
+def set_voice_sub_id(ad, sub_id):
+ """Set default subId for both incoming and outgoing voice calls
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
+ if hasattr(ad, "incoming_voice_sub_id"):
+ ad.incoming_voice_sub_id = sub_id
+ if hasattr(ad, "outgoing_voice_sub_id"):
+ ad.outgoing_voice_sub_id = sub_id
+
+def set_voice_sub_id(ad, sub_id):
+ """Set default subId for both incoming and outgoing voice calls
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+
+ Returns:
+ None
+ """
+ ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
+ if hasattr(ad, "incoming_voice_sub_id"):
+ ad.incoming_voice_sub_id = sub_id
+ if hasattr(ad, "outgoing_voice_sub_id"):
+ ad.outgoing_voice_sub_id = sub_id
+
+
+def set_default_sub_for_all_services(ad, slot_id=0):
+ """Set subId for all services
+
+ Args:
+ ad: android device object.
+ slot_id: 0 or 1 (integer)
+
+ Returns:
+ None
+ """
+ sub_id = get_subid_from_slot_index(ad.log, ad, slot_id)
+ ad.log.info("Default Subid for all service is %s", sub_id)
+ set_subid_for_outgoing_call(ad, sub_id)
+ set_incoming_voice_sub_id(ad, sub_id)
+ set_subid_for_data(ad, sub_id)
+ set_subid_for_message(ad, sub_id)
+ ad.droid.telephonyToggleDataConnection(True)
+
+
+def perform_dds_switch(ad):
+ slot_dict = {0: {}, 1: {}}
+ for slot in (0,1):
+ slot_dict[slot]['sub_id'] = get_subid_from_slot_index(ad.log, ad, slot)
+ slot_dict[slot]['operator'] = get_operatorname_from_slot_index(ad, slot)
+ ad.log.debug("%s", slot_dict)
+
+ current_data = get_default_data_sub_id(ad)
+ if slot_dict[0]['sub_id'] == current_data:
+ ad.log.info("DDS Switch from %s to %s", slot_dict[0]['operator'],
+ slot_dict[1]['operator'])
+ new_data = slot_dict[1]['sub_id']
+ new_oper = slot_dict[1]['operator']
+ else:
+ ad.log.info("DDS Switch from %s to %s", slot_dict[1]['operator'],
+ slot_dict[0]['operator'])
+ new_data = slot_dict[0]['sub_id']
+ new_oper = slot_dict[0]['operator']
+ set_subid_for_data(ad, new_data)
+ ad.droid.telephonyToggleDataConnection(True)
+ if get_default_data_sub_id(ad) == new_data:
+ return new_oper
+ else:
+ ad.log.error("DDS Switch Failed")
+ return False
+
+
+def set_dds_on_slot_0(ad):
+ sub_id = get_subid_from_slot_index(ad.log, ad, 0)
+ operator = get_operatorname_from_slot_index(ad, 0)
+ if get_default_data_sub_id(ad) == sub_id:
+ ad.log.info("Current DDS is already on %s", operator)
+ return True
+ ad.log.info("Setting DDS on %s", operator)
+ set_subid_for_data(ad, sub_id)
+ ad.droid.telephonyToggleDataConnection(True)
+ time.sleep(WAIT_TIME_CHANGE_DATA_SUB_ID)
+ if get_default_data_sub_id(ad) == sub_id:
+ return True
+ else:
+ return False
+
+
+def set_dds_on_slot_1(ad):
+ sub_id = get_subid_from_slot_index(ad.log, ad, 1)
+ operator = get_operatorname_from_slot_index(ad, 1)
+ if get_default_data_sub_id(ad) == sub_id:
+ ad.log.info("Current DDS is already on %s", operator)
+ return True
+ ad.log.info("Setting DDS on %s", operator)
+ set_subid_for_data(ad, sub_id)
+ ad.droid.telephonyToggleDataConnection(True)
+ time.sleep(WAIT_TIME_CHANGE_DATA_SUB_ID)
+ if get_default_data_sub_id(ad) == sub_id:
+ return True
+ else:
+ return False
+
+
+def set_slways_allow_mms_data(ad, sub_id, state=True):
+ """Set always allow mms data on sub_id
+
+ Args:
+ ad: android device object.
+ sub_id: subscription id (integer)
+ state: True or False
+
+ Returns:
+ None
+ """
+ list_of_models = ["sdm", "msm", "kon", "lit"]
+ if any(model in ad.model for model in list_of_models):
+ ad.log.debug("SKIP telephonySetAlwaysAllowMmsData")
+ else:
+ ad.log.debug("telephonySetAlwaysAllowMmsData %s sub_id %s", state, sub_id)
+ ad.droid.telephonySetAlwaysAllowMmsData(sub_id, state)
+ return True
+
+
+def get_cbrs_and_default_sub_id(ad):
+ """Gets CBRS and Default SubId
+
+ Args:
+ ad: android device object.
+
+ Returns:
+ cbrs_subId
+ default_subId
+ """
+ cbrs_subid, default_subid = None, None
+ slot_dict = {0: {}, 1: {}}
+ for slot in (0, 1):
+ slot_dict[slot]['sub_id'] = get_subid_from_slot_index(
+ ad.log, ad, slot)
+ slot_dict[slot]['carrier_id'] = get_carrierid_from_slot_index(
+ ad, slot)
+ slot_dict[slot]['operator'] = get_operatorname_from_slot_index(
+ ad, slot)
+ if slot_dict[slot]['carrier_id'] == 2340:
+ cbrs_subid = slot_dict[slot]['sub_id']
+ else:
+ default_subid = slot_dict[slot]['sub_id']
+ ad.log.info("Slot %d - Sub %s - Carrier %d - %s", slot,
+ slot_dict[slot]['sub_id'],
+ slot_dict[slot]['carrier_id'],
+ slot_dict[slot]['operator'])
+ if not cbrs_subid:
+ ad.log.error("CBRS sub_id is not ACTIVE")
+ return cbrs_subid, default_subid
+
+def get_subid_on_same_network_of_host_ad(ads, host_sub_id=None, type="voice"):
+ ad_host = ads[0]
+ ad_p1 = ads[1]
+
+ try:
+ ad_p2 = ads[2]
+ except:
+ ad_p2 = None
+
+ if not host_sub_id:
+ if type == "sms":
+ host_sub_id = get_outgoing_message_sub_id(ad_host)
+ else:
+ host_sub_id = get_incoming_voice_sub_id(ad_host)
+ host_mcc = ad_host.telephony["subscription"][host_sub_id]["mcc"]
+ host_mnc = ad_host.telephony["subscription"][host_sub_id]["mnc"]
+ p1_sub_id = INVALID_SUB_ID
+ p2_sub_id = INVALID_SUB_ID
+ p1_mcc = None
+ p1_mnc = None
+ p2_mcc = None
+ p2_mnc = None
+
+ for ad in [ad_p1, ad_p2]:
+ if ad:
+ for sub_id in ad.telephony["subscription"]:
+ mcc = ad.telephony["subscription"][sub_id]["mcc"]
+ mnc = ad.telephony["subscription"][sub_id]["mnc"]
+
+ if ad == ad_p1:
+ if p1_sub_id == INVALID_SUB_ID:
+ p1_sub_id = sub_id
+ if not p1_mcc:
+ p1_mcc = mcc
+ if not p1_mnc:
+ p1_mnc = mnc
+ elif ad == ad_p2:
+ if p2_sub_id == INVALID_SUB_ID:
+ p2_sub_id = sub_id
+ if not p2_mcc:
+ p2_mcc = mcc
+ if not p2_mnc:
+ p2_mnc = mnc
+
+ if mcc == host_mcc and mnc == host_mnc:
+ if ad == ad_p1:
+ p1_sub_id = sub_id
+ p1_mcc = mcc
+ p1_mnc = mnc
+
+ elif ad == ad_p2:
+ p2_sub_id = sub_id
+ p2_mcc = mcc
+ p2_mnc = mnc
+
+ return host_sub_id, p1_sub_id, p2_sub_id
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py
new file mode 100644
index 0000000..846a2f9
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py
@@ -0,0 +1,11011 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from future import standard_library
+standard_library.install_aliases()
+
+import concurrent.futures
+import json
+import logging
+import re
+import os
+import urllib.parse
+import time
+import acts.controllers.iperf_server as ipf
+import shutil
+import struct
+
+from acts import signals
+from acts import utils
+from queue import Empty
+from acts.asserts import abort_all
+from acts.asserts import fail
+from acts.controllers.adb_lib.error import AdbError
+from acts.controllers.android_device import list_adb_devices
+from acts.controllers.android_device import list_fastboot_devices
+from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
+from acts.controllers.android_device import DEFAULT_SDM_LOG_PATH
+from acts.controllers.android_device import SL4A_APK_NAME
+from acts.libs.proc import job
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
+from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs, CARRIER_NTT_DOCOMO, CARRIER_KDDI, CARRIER_RAKUTEN, \
+ CARRIER_SBM
+from acts_contrib.test_utils.tel.tel_defines import AOSP_PREFIX
+from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_DOWN
+from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_UP
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE_PROVISIONING
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE_OVERRIDE_WFC_PROVISIONING
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_HIDE_ENHANCED_4G_LTE_BOOL
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC_MODE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_FRE
+from acts_contrib.test_utils.tel.tel_defines import COUNTRY_CODE_LIST
+from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_DISCONNECTED
+from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_ENABLE
+from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_DISABLE
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import GEN_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SIM_SLOT_INDEX
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import MAX_SAVED_VOICE_MAIL
+from acts_contrib.test_utils.tel.tel_defines import MAX_SCREEN_ON_TIME
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_AIRPLANEMODE_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_DATA_SUB_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_IDLE_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_SENT_SUCCESS
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_SENT_SUCCESS_IN_COLLISION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOICE_MAIL_COUNT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_NW_VALID_FAIL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL_RECOVERY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_CELL
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_7_DIGIT
+from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_10_DIGIT
+from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_11_DIGIT
+from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_12_DIGIT
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_EMERGENCY_ONLY
+from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_IN_SERVICE
+from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_MAPPING
+from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_OUT_OF_SERVICE
+from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_POWER_OFF
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_LOADED
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_NOT_READY
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_PIN_REQUIRED
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_READY
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import VOICEMAIL_DELETE_DIGIT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_1XRTT_VOICE_ATTACH
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import TYPE_MOBILE
+from acts_contrib.test_utils.tel.tel_defines import TYPE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import EventCallStateChanged
+from acts_contrib.test_utils.tel.tel_defines import EventActiveDataSubIdChanged
+from acts_contrib.test_utils.tel.tel_defines import EventDisplayInfoChanged
+from acts_contrib.test_utils.tel.tel_defines import EventConnectivityChanged
+from acts_contrib.test_utils.tel.tel_defines import EventDataConnectionStateChanged
+from acts_contrib.test_utils.tel.tel_defines import EventDataSmsReceived
+from acts_contrib.test_utils.tel.tel_defines import EventMessageWaitingIndicatorChanged
+from acts_contrib.test_utils.tel.tel_defines import EventServiceStateChanged
+from acts_contrib.test_utils.tel.tel_defines import EventMmsSentFailure
+from acts_contrib.test_utils.tel.tel_defines import EventMmsSentSuccess
+from acts_contrib.test_utils.tel.tel_defines import EventMmsDownloaded
+from acts_contrib.test_utils.tel.tel_defines import EventSmsReceived
+from acts_contrib.test_utils.tel.tel_defines import EventSmsDeliverFailure
+from acts_contrib.test_utils.tel.tel_defines import EventSmsDeliverSuccess
+from acts_contrib.test_utils.tel.tel_defines import EventSmsSentFailure
+from acts_contrib.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts_contrib.test_utils.tel.tel_defines import CallStateContainer
+from acts_contrib.test_utils.tel.tel_defines import DataConnectionStateContainer
+from acts_contrib.test_utils.tel.tel_defines import MessageWaitingIndicatorContainer
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackContainer
+from acts_contrib.test_utils.tel.tel_defines import ServiceStateContainer
+from acts_contrib.test_utils.tel.tel_defines import DisplayInfoContainer
+from acts_contrib.test_utils.tel.tel_defines import OverrideNetworkContainer
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_NR_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_VZW, CARRIER_ATT, \
+ CARRIER_BELL, CARRIER_ROGERS, CARRIER_KOODO, CARRIER_VIDEOTRON, CARRIER_TELUS
+from acts_contrib.test_utils.tel.tel_lookup_tables import connection_type_from_type_string
+from acts_contrib.test_utils.tel.tel_lookup_tables import is_valid_rat
+from acts_contrib.test_utils.tel.tel_lookup_tables import get_allowable_network_preference
+from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_count_check_function
+from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_check_number
+from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
+from acts_contrib.test_utils.tel.tel_lookup_tables import network_preference_for_generation
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_network_name
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_families_for_network_preference
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_for_generation
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_from_rat
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_generation_from_rat
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id, get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_message_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_message
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_on_same_network_of_host_ad
+from acts_contrib.test_utils.wifi import wifi_test_utils
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts.utils import adb_shell_ping
+from acts.utils import load_config
+from acts.utils import start_standing_subprocess
+from acts.utils import stop_standing_subprocess
+from acts.logger import epoch_to_log_line_timestamp
+from acts.logger import normalize_log_line_timestamp
+from acts.utils import get_current_epoch_time
+from acts.utils import exe_cmd
+from acts.utils import rand_ascii_str
+
+
+WIFI_SSID_KEY = wifi_test_utils.WifiEnums.SSID_KEY
+WIFI_PWD_KEY = wifi_test_utils.WifiEnums.PWD_KEY
+WIFI_CONFIG_APBAND_2G = 1
+WIFI_CONFIG_APBAND_5G = 2
+WIFI_CONFIG_APBAND_AUTO = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_AUTO
+log = logging
+STORY_LINE = "+19523521350"
+CallResult = TelephonyVoiceTestResult.CallResult.Value
+
+
+class TelTestUtilsError(Exception):
+ pass
+
+
+class TelResultWrapper(object):
+ """Test results wrapper for Telephony test utils.
+
+ In order to enable metrics reporting without refactoring
+ all of the test utils this class is used to keep the
+ current return boolean scheme in tact.
+ """
+
+ def __init__(self, result_value):
+ self._result_value = result_value
+
+ @property
+ def result_value(self):
+ return self._result_value
+
+ @result_value.setter
+ def result_value(self, result_value):
+ self._result_value = result_value
+
+ def __bool__(self):
+ return self._result_value == CallResult('SUCCESS')
+
+
+def abort_all_tests(log, msg):
+ log.error("Aborting all ongoing tests due to: %s.", msg)
+ abort_all(msg)
+
+
+def get_phone_number_by_adb(ad):
+ return phone_number_formatter(
+ ad.adb.shell("service call iphonesubinfo 13"))
+
+
+def get_iccid_by_adb(ad):
+ return ad.adb.shell("service call iphonesubinfo 11")
+
+
+def get_operator_by_adb(ad):
+ operator = ad.adb.getprop("gsm.sim.operator.alpha")
+ if "," in operator:
+ operator = operator.strip()[0]
+ return operator
+
+
+def get_plmn_by_adb(ad):
+ plmn_id = ad.adb.getprop("gsm.sim.operator.numeric")
+ if "," in plmn_id:
+ plmn_id = plmn_id.strip()[0]
+ return plmn_id
+
+
+def get_sub_id_by_adb(ad):
+ return ad.adb.shell("service call iphonesubinfo 5")
+
+
+def setup_droid_properties_by_adb(log, ad, sim_filename=None):
+
+ sim_data = None
+ if sim_filename:
+ try:
+ sim_data = load_config(sim_filename)
+ except Exception:
+ log.warning("Failed to load %s!", sim_filename)
+
+ sub_id = get_sub_id_by_adb(ad)
+ iccid = get_iccid_by_adb(ad)
+ ad.log.info("iccid = %s", iccid)
+ if sim_data.get(iccid) and sim_data[iccid].get("phone_num"):
+ phone_number = phone_number_formatter(sim_data[iccid]["phone_num"])
+ else:
+ phone_number = get_phone_number_by_adb(ad)
+ if not phone_number and hasattr(ad, phone_number):
+ phone_number = ad.phone_number
+ if not phone_number:
+ ad.log.error("Failed to find valid phone number for %s", iccid)
+ abort_all_tests(ad.log, "Failed to find valid phone number for %s")
+ sub_record = {
+ 'phone_num': phone_number,
+ 'iccid': get_iccid_by_adb(ad),
+ 'sim_operator_name': get_operator_by_adb(ad),
+ 'operator': operator_name_from_plmn_id(get_plmn_by_adb(ad))
+ }
+ device_props = {'subscription': {sub_id: sub_record}}
+ ad.log.info("subId %s SIM record: %s", sub_id, sub_record)
+ setattr(ad, 'telephony', device_props)
+
+
+def setup_droid_properties(log, ad, sim_filename=None):
+
+ if ad.skip_sl4a:
+ return setup_droid_properties_by_adb(
+ log, ad, sim_filename=sim_filename)
+ refresh_droid_config(log, ad)
+ device_props = {}
+ device_props['subscription'] = {}
+
+ sim_data = {}
+ if sim_filename:
+ try:
+ sim_data = load_config(sim_filename)
+ except Exception:
+ log.warning("Failed to load %s!", sim_filename)
+ if not ad.telephony["subscription"]:
+ abort_all_tests(ad.log, "No valid subscription")
+ ad.log.debug("Subscription DB %s", ad.telephony["subscription"])
+ result = True
+ active_sub_id = get_outgoing_voice_sub_id(ad)
+ for sub_id, sub_info in ad.telephony["subscription"].items():
+ ad.log.debug("Loop for Subid %s", sub_id)
+ sub_info["operator"] = get_operator_name(log, ad, sub_id)
+ iccid = sub_info["iccid"]
+ if not iccid:
+ ad.log.warning("Unable to find ICC-ID for subscriber %s", sub_id)
+ continue
+ if sub_info.get("phone_num"):
+ if iccid in sim_data and sim_data[iccid].get("phone_num"):
+ if not check_phone_number_match(sim_data[iccid]["phone_num"],
+ sub_info["phone_num"]):
+ ad.log.warning(
+ "phone_num %s in sim card data file for iccid %s"
+ " do not match phone_num %s from subscription",
+ sim_data[iccid]["phone_num"], iccid,
+ sub_info["phone_num"])
+ sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+ else:
+ if iccid in sim_data and sim_data[iccid].get("phone_num"):
+ sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+ elif sub_id == active_sub_id:
+ phone_number = get_phone_number_by_secret_code(
+ ad, sub_info["sim_operator_name"])
+ if phone_number:
+ sub_info["phone_num"] = phone_number
+ elif getattr(ad, "phone_num", None):
+ sub_info["phone_num"] = ad.phone_number
+ if (not sub_info.get("phone_num")) and sub_id == active_sub_id:
+ ad.log.info("sub_id %s sub_info = %s", sub_id, sub_info)
+ ad.log.error(
+ "Unable to retrieve phone number for sub %s with iccid"
+ " %s from device or testbed config or sim card file %s",
+ sub_id, iccid, sim_filename)
+ result = False
+ if not hasattr(
+ ad, 'roaming'
+ ) and sub_info["sim_plmn"] != sub_info["network_plmn"] and sub_info["sim_operator_name"].strip(
+ ) not in sub_info["network_operator_name"].strip():
+ ad.log.info("roaming is not enabled, enable it")
+ setattr(ad, 'roaming', True)
+ ad.log.info("SubId %s info: %s", sub_id, sorted(sub_info.items()))
+ get_phone_capability(ad)
+ data_roaming = getattr(ad, 'roaming', False)
+ if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
+ set_cell_data_roaming_state_by_adb(ad, data_roaming)
+ # Setup VoWiFi MDN for Verizon. b/33187374
+ if not result:
+ abort_all_tests(ad.log, "Failed to find valid phone number")
+
+ ad.log.debug("telephony = %s", ad.telephony)
+
+
+def refresh_droid_config(log, ad):
+ """ Update Android Device telephony records for each sub_id.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ None
+ """
+ if not getattr(ad, 'telephony', {}):
+ setattr(ad, 'telephony', {"subscription": {}})
+ droid = ad.droid
+ sub_info_list = droid.subscriptionGetAllSubInfoList()
+ ad.log.info("SubInfoList is %s", sub_info_list)
+ active_sub_id = get_outgoing_voice_sub_id(ad)
+ for sub_info in sub_info_list:
+ sub_id = sub_info["subscriptionId"]
+ sim_slot = sub_info["simSlotIndex"]
+ if sub_info.get("carrierId"):
+ carrier_id = sub_info["carrierId"]
+ else:
+ carrier_id = -1
+ if sub_info.get("isOpportunistic"):
+ isopportunistic = sub_info["isOpportunistic"]
+ else:
+ isopportunistic = -1
+
+ if sim_slot != INVALID_SIM_SLOT_INDEX:
+ if sub_id not in ad.telephony["subscription"]:
+ ad.telephony["subscription"][sub_id] = {}
+ sub_record = ad.telephony["subscription"][sub_id]
+ if sub_info.get("iccId"):
+ sub_record["iccid"] = sub_info["iccId"]
+ else:
+ sub_record[
+ "iccid"] = droid.telephonyGetSimSerialNumberForSubscription(
+ sub_id)
+ sub_record["sim_slot"] = sim_slot
+ if sub_info.get("mcc"):
+ sub_record["mcc"] = sub_info["mcc"]
+ if sub_info.get("mnc"):
+ sub_record["mnc"] = sub_info["mnc"]
+ if sub_info.get("displayName"):
+ sub_record["display_name"] = sub_info["displayName"]
+ try:
+ sub_record[
+ "phone_type"] = droid.telephonyGetPhoneTypeForSubscription(
+ sub_id)
+ except:
+ if not sub_record.get("phone_type"):
+ sub_record["phone_type"] = droid.telephonyGetPhoneType()
+ sub_record[
+ "sim_plmn"] = droid.telephonyGetSimOperatorForSubscription(
+ sub_id)
+ sub_record[
+ "sim_operator_name"] = droid.telephonyGetSimOperatorNameForSubscription(
+ sub_id)
+ sub_record[
+ "network_plmn"] = droid.telephonyGetNetworkOperatorForSubscription(
+ sub_id)
+ sub_record[
+ "network_operator_name"] = droid.telephonyGetNetworkOperatorNameForSubscription(
+ sub_id)
+ sub_record[
+ "sim_country"] = droid.telephonyGetSimCountryIsoForSubscription(
+ sub_id)
+ if active_sub_id == sub_id:
+ try:
+ sub_record[
+ "carrier_id"] = ad.droid.telephonyGetSimCarrierId()
+ sub_record[
+ "carrier_id_name"] = ad.droid.telephonyGetSimCarrierIdName(
+ )
+ except:
+ ad.log.info("Carrier ID is not supported")
+ if carrier_id == 2340:
+ ad.log.info("SubId %s info: %s", sub_id, sorted(
+ sub_record.items()))
+ continue
+ if carrier_id == 1989 and isopportunistic == "true":
+ ad.log.info("SubId %s info: %s", sub_id, sorted(
+ sub_record.items()))
+ continue
+ if not sub_info.get("number"):
+ sub_info[
+ "number"] = droid.telephonyGetLine1NumberForSubscription(
+ sub_id)
+ if sub_info.get("number"):
+ if sub_record.get("phone_num"):
+ # Use the phone number provided in sim info file by default
+ # as the sub_info["number"] may not be formatted in a
+ # dialable number
+ if not check_phone_number_match(sub_info["number"],
+ sub_record["phone_num"]):
+ ad.log.info(
+ "Subscriber phone number changed from %s to %s",
+ sub_record["phone_num"], sub_info["number"])
+ sub_record["phone_num"] = sub_info["number"]
+ else:
+ sub_record["phone_num"] = phone_number_formatter(
+ sub_info["number"])
+ #ad.telephony['subscription'][sub_id] = sub_record
+ ad.log.info("SubId %s info: %s", sub_id, sorted(
+ sub_record.items()))
+
+
+def get_phone_number_by_secret_code(ad, operator):
+ if "T-Mobile" in operator:
+ ad.droid.telecomDialNumber("#686#")
+ ad.send_keycode("ENTER")
+ for _ in range(12):
+ output = ad.search_logcat("mobile number")
+ if output:
+ result = re.findall(r"mobile number is (\S+)",
+ output[-1]["log_message"])
+ ad.send_keycode("BACK")
+ return result[0]
+ else:
+ time.sleep(5)
+ return ""
+
+
+def get_user_config_profile(ad):
+ return {
+ "Airplane Mode":
+ ad.droid.connectivityCheckAirplaneMode(),
+ "IMS Registered":
+ ad.droid.telephonyIsImsRegistered(),
+ "Preferred Network Type":
+ ad.droid.telephonyGetPreferredNetworkTypes(),
+ "VoLTE Platform Enabled":
+ ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform(),
+ "VoLTE Enabled":
+ ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser(),
+ "VoLTE Available":
+ ad.droid.telephonyIsVolteAvailable(),
+ "VT Available":
+ ad.droid.telephonyIsVideoCallingAvailable(),
+ "VT Enabled":
+ ad.droid.imsIsVtEnabledByUser(),
+ "VT Platform Enabled":
+ ad.droid.imsIsVtEnabledByPlatform(),
+ "WiFi State":
+ ad.droid.wifiCheckState(),
+ "WFC Available":
+ ad.droid.telephonyIsWifiCallingAvailable(),
+ "WFC Enabled":
+ ad.droid.imsIsWfcEnabledByUser(),
+ "WFC Platform Enabled":
+ ad.droid.imsIsWfcEnabledByPlatform(),
+ "WFC Mode":
+ ad.droid.imsGetWfcMode()
+ }
+
+
+def get_slot_index_from_subid(log, ad, sub_id):
+ try:
+ info = ad.droid.subscriptionGetSubInfoForSubscriber(sub_id)
+ return info['simSlotIndex']
+ except KeyError:
+ return INVALID_SIM_SLOT_INDEX
+
+
+def get_num_active_sims(log, ad):
+ """ Get the number of active SIM cards by counting slots
+
+ Args:
+ ad: android_device object.
+
+ Returns:
+ result: The number of loaded (physical) SIM cards
+ """
+ # using a dictionary as a cheap way to prevent double counting
+ # in the situation where multiple subscriptions are on the same SIM.
+ # yes, this is a corner corner case.
+ valid_sims = {}
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ for info in subInfo:
+ ssidx = info['simSlotIndex']
+ if ssidx == INVALID_SIM_SLOT_INDEX:
+ continue
+ valid_sims[ssidx] = True
+ return len(valid_sims.keys())
+
+
+def toggle_airplane_mode_by_adb(log, ad, new_state=None):
+ """ Toggle the state of airplane mode.
+
+ Args:
+ log: log handler.
+ ad: android_device object.
+ new_state: Airplane mode state to set to.
+ If None, opposite of the current state.
+ strict_checking: Whether to turn on strict checking that checks all features.
+
+ Returns:
+ result: True if operation succeed. False if error happens.
+ """
+ cur_state = bool(int(ad.adb.shell("settings get global airplane_mode_on")))
+ if new_state == cur_state:
+ ad.log.info("Airplane mode already in %s", new_state)
+ return True
+ elif new_state is None:
+ new_state = not cur_state
+ ad.log.info("Change airplane mode from %s to %s", cur_state, new_state)
+ try:
+ ad.adb.shell("settings put global airplane_mode_on %s" % int(new_state))
+ ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ changed_state = bool(int(ad.adb.shell("settings get global airplane_mode_on")))
+ return changed_state == new_state
+
+
+def toggle_airplane_mode(log, ad, new_state=None, strict_checking=True):
+ """ Toggle the state of airplane mode.
+
+ Args:
+ log: log handler.
+ ad: android_device object.
+ new_state: Airplane mode state to set to.
+ If None, opposite of the current state.
+ strict_checking: Whether to turn on strict checking that checks all features.
+
+ Returns:
+ result: True if operation succeed. False if error happens.
+ """
+ if ad.skip_sl4a:
+ return toggle_airplane_mode_by_adb(log, ad, new_state)
+ else:
+ return toggle_airplane_mode_msim(
+ log, ad, new_state, strict_checking=strict_checking)
+
+
+def get_telephony_signal_strength(ad):
+ #{'evdoEcio': -1, 'asuLevel': 28, 'lteSignalStrength': 14, 'gsmLevel': 0,
+ # 'cdmaAsuLevel': 99, 'evdoDbm': -120, 'gsmDbm': -1, 'cdmaEcio': -160,
+ # 'level': 2, 'lteLevel': 2, 'cdmaDbm': -120, 'dbm': -112, 'cdmaLevel': 0,
+ # 'lteAsuLevel': 28, 'gsmAsuLevel': 99, 'gsmBitErrorRate': 0,
+ # 'lteDbm': -112, 'gsmSignalStrength': 99}
+ try:
+ signal_strength = ad.droid.telephonyGetSignalStrength()
+ if not signal_strength:
+ signal_strength = {}
+ except Exception as e:
+ ad.log.error(e)
+ signal_strength = {}
+ return signal_strength
+
+
+def get_wifi_signal_strength(ad):
+ signal_strength = ad.droid.wifiGetConnectionInfo()['rssi']
+ ad.log.info("WiFi Signal Strength is %s" % signal_strength)
+ return signal_strength
+
+
+def get_lte_rsrp(ad):
+ try:
+ if ad.adb.getprop("ro.build.version.release")[0] in ("9", "P"):
+ out = ad.adb.shell(
+ "dumpsys telephony.registry | grep -i signalstrength")
+ if out:
+ lte_rsrp = out.split()[9]
+ if lte_rsrp:
+ ad.log.info("lte_rsrp: %s ", lte_rsrp)
+ return lte_rsrp
+ else:
+ out = ad.adb.shell(
+ "dumpsys telephony.registry |grep -i primary=CellSignalStrengthLte")
+ if out:
+ lte_cell_info = out.split('mLte=')[1]
+ lte_rsrp = re.match(r'.*rsrp=(\S+).*', lte_cell_info).group(1)
+ if lte_rsrp:
+ ad.log.info("lte_rsrp: %s ", lte_rsrp)
+ return lte_rsrp
+ except Exception as e:
+ ad.log.error(e)
+ return None
+
+
+def check_data_stall_detection(ad, wait_time=WAIT_TIME_FOR_DATA_STALL):
+ data_stall_detected = False
+ time_var = 1
+ try:
+ while (time_var < wait_time):
+ out = ad.adb.shell("dumpsys network_stack " \
+ "| grep \"Suspecting data stall\"",
+ ignore_status=True)
+ ad.log.debug("Output is %s", out)
+ if out:
+ ad.log.info("NetworkMonitor detected - %s", out)
+ data_stall_detected = True
+ break
+ time.sleep(30)
+ time_var += 30
+ except Exception as e:
+ ad.log.error(e)
+ return data_stall_detected
+
+
+def check_network_validation_fail(ad, begin_time=None,
+ wait_time=WAIT_TIME_FOR_NW_VALID_FAIL):
+ network_validation_fail = False
+ time_var = 1
+ try:
+ while (time_var < wait_time):
+ time_var += 30
+ nw_valid = ad.search_logcat("validation failed",
+ begin_time)
+ if nw_valid:
+ ad.log.info("Validation Failed received here - %s",
+ nw_valid[0]["log_message"])
+ network_validation_fail = True
+ break
+ time.sleep(30)
+ except Exception as e:
+ ad.log.error(e)
+ return network_validation_fail
+
+
+def check_data_stall_recovery(ad, begin_time=None,
+ wait_time=WAIT_TIME_FOR_DATA_STALL_RECOVERY):
+ data_stall_recovery = False
+ time_var = 1
+ try:
+ while (time_var < wait_time):
+ time_var += 30
+ recovery = ad.search_logcat("doRecovery() cleanup all connections",
+ begin_time)
+ if recovery:
+ ad.log.info("Recovery Performed here - %s",
+ recovery[-1]["log_message"])
+ data_stall_recovery = True
+ break
+ time.sleep(30)
+ except Exception as e:
+ ad.log.error(e)
+ return data_stall_recovery
+
+
+def break_internet_except_sl4a_port(ad, sl4a_port):
+ ad.log.info("Breaking internet using iptables rules")
+ ad.adb.shell("iptables -I INPUT 1 -p tcp --dport %s -j ACCEPT" % sl4a_port,
+ ignore_status=True)
+ ad.adb.shell("iptables -I INPUT 2 -p tcp --sport %s -j ACCEPT" % sl4a_port,
+ ignore_status=True)
+ ad.adb.shell("iptables -I INPUT 3 -j DROP", ignore_status=True)
+ ad.adb.shell("ip6tables -I INPUT -j DROP", ignore_status=True)
+ return True
+
+
+def resume_internet_with_sl4a_port(ad, sl4a_port):
+ ad.log.info("Bring internet back using iptables rules")
+ ad.adb.shell("iptables -D INPUT -p tcp --dport %s -j ACCEPT" % sl4a_port,
+ ignore_status=True)
+ ad.adb.shell("iptables -D INPUT -p tcp --sport %s -j ACCEPT" % sl4a_port,
+ ignore_status=True)
+ ad.adb.shell("iptables -D INPUT -j DROP", ignore_status=True)
+ ad.adb.shell("ip6tables -D INPUT -j DROP", ignore_status=True)
+ return True
+
+
+def test_data_browsing_success_using_sl4a(log, ad):
+ result = True
+ web_page_list = ['https://www.google.com', 'https://www.yahoo.com',
+ 'https://www.amazon.com', 'https://www.nike.com',
+ 'https://www.facebook.com']
+ for website in web_page_list:
+ if not verify_http_connection(log, ad, website, retry=0):
+ ad.log.error("Failed to browse %s successfully!", website)
+ result = False
+ return result
+
+
+def test_data_browsing_failure_using_sl4a(log, ad):
+ result = True
+ web_page_list = ['https://www.youtube.com', 'https://www.cnn.com',
+ 'https://www.att.com', 'https://www.nbc.com',
+ 'https://www.verizonwireless.com']
+ for website in web_page_list:
+ if not verify_http_connection(log, ad, website, retry=0,
+ expected_state=False):
+ ad.log.error("Browsing to %s worked!", website)
+ result = False
+ return result
+
+
+def is_expected_event(event_to_check, events_list):
+ """ check whether event is present in the event list
+
+ Args:
+ event_to_check: event to be checked.
+ events_list: list of events
+ Returns:
+ result: True if event present in the list. False if not.
+ """
+ for event in events_list:
+ if event in event_to_check['name']:
+ return True
+ return False
+
+
+def is_sim_ready(log, ad, sim_slot_id=None):
+ """ check whether SIM is ready.
+
+ Args:
+ ad: android_device object.
+ sim_slot_id: check the SIM status for sim_slot_id
+ This is optional. If this is None, check default SIM.
+
+ Returns:
+ result: True if all SIMs are ready. False if not.
+ """
+ if sim_slot_id is None:
+ status = ad.droid.telephonyGetSimState()
+ else:
+ status = ad.droid.telephonyGetSimStateForSlotId(sim_slot_id)
+ if status != SIM_STATE_READY:
+ ad.log.info("Sim state is %s, not ready", status)
+ return False
+ return True
+
+
+def is_sim_ready_by_adb(log, ad):
+ state = ad.adb.getprop("gsm.sim.state")
+ ad.log.info("gsm.sim.state = %s", state)
+ return state == SIM_STATE_READY or state == SIM_STATE_LOADED
+
+
+def wait_for_sim_ready_by_adb(log, ad, wait_time=90):
+ return _wait_for_droid_in_state(log, ad, wait_time, is_sim_ready_by_adb)
+
+
+def is_sims_ready_by_adb(log, ad):
+ states = list(ad.adb.getprop("gsm.sim.state").split(","))
+ ad.log.info("gsm.sim.state = %s", states)
+ for state in states:
+ if state != SIM_STATE_READY and state != SIM_STATE_LOADED:
+ return False
+ return True
+
+
+def wait_for_sims_ready_by_adb(log, ad, wait_time=90):
+ return _wait_for_droid_in_state(log, ad, wait_time, is_sims_ready_by_adb)
+
+
+def get_service_state_by_adb(log, ad):
+ output = ad.adb.shell("dumpsys telephony.registry | grep mServiceState")
+ if "mVoiceRegState" in output:
+ result = re.search(r"mVoiceRegState=(\S+)\((\S+)\)", output)
+ if result:
+ ad.log.info("mVoiceRegState is %s %s", result.group(1),
+ result.group(2))
+ return result.group(2)
+ else:
+ if getattr(ad, "sdm_log", False):
+ #look for all occurrence in string
+ result2 = re.findall(r"mVoiceRegState=(\S+)\((\S+)\)", output)
+ for voice_state in result2:
+ if voice_state[0] == 0:
+ ad.log.info("mVoiceRegState is 0 %s", voice_state[1])
+ return voice_state[1]
+ return result2[1][1]
+ else:
+ result = re.search(r"mServiceState=(\S+)", output)
+ if result:
+ ad.log.info("mServiceState=%s %s", result.group(1),
+ SERVICE_STATE_MAPPING[result.group(1)])
+ return SERVICE_STATE_MAPPING[result.group(1)]
+
+
+def _is_expecting_event(event_recv_list):
+ """ check for more event is expected in event list
+
+ Args:
+ event_recv_list: list of events
+ Returns:
+ result: True if more events are expected. False if not.
+ """
+ for state in event_recv_list:
+ if state is False:
+ return True
+ return False
+
+
+def _set_event_list(event_recv_list, sub_id_list, sub_id, value):
+ """ set received event in expected event list
+
+ Args:
+ event_recv_list: list of received events
+ sub_id_list: subscription ID list
+ sub_id: subscription id of current event
+ value: True or False
+ Returns:
+ None.
+ """
+ for i in range(len(sub_id_list)):
+ if sub_id_list[i] == sub_id:
+ event_recv_list[i] = value
+
+
+def _wait_for_bluetooth_in_state(log, ad, state, max_wait):
+ # FIXME: These event names should be defined in a common location
+ _BLUETOOTH_STATE_ON_EVENT = 'BluetoothStateChangedOn'
+ _BLUETOOTH_STATE_OFF_EVENT = 'BluetoothStateChangedOff'
+ ad.ed.clear_events(_BLUETOOTH_STATE_ON_EVENT)
+ ad.ed.clear_events(_BLUETOOTH_STATE_OFF_EVENT)
+
+ ad.droid.bluetoothStartListeningForAdapterStateChange()
+ try:
+ bt_state = ad.droid.bluetoothCheckState()
+ if bt_state == state:
+ return True
+ if max_wait <= 0:
+ ad.log.error("Time out: bluetooth state still %s, expecting %s",
+ bt_state, state)
+ return False
+
+ event = {
+ False: _BLUETOOTH_STATE_OFF_EVENT,
+ True: _BLUETOOTH_STATE_ON_EVENT
+ }[state]
+ event = ad.ed.pop_event(event, max_wait)
+ ad.log.info("Got event %s", event['name'])
+ return True
+ except Empty:
+ ad.log.error("Time out: bluetooth state still in %s, expecting %s",
+ bt_state, state)
+ return False
+ finally:
+ ad.droid.bluetoothStopListeningForAdapterStateChange()
+
+
+# TODO: replace this with an event-based function
+def _wait_for_wifi_in_state(log, ad, state, max_wait):
+ return _wait_for_droid_in_state(log, ad, max_wait,
+ lambda log, ad, state: \
+ (True if ad.droid.wifiCheckState() == state else False),
+ state)
+
+
+def toggle_airplane_mode_msim(log, ad, new_state=None, strict_checking=True):
+ """ Toggle the state of airplane mode.
+
+ Args:
+ log: log handler.
+ ad: android_device object.
+ new_state: Airplane mode state to set to.
+ If None, opposite of the current state.
+ strict_checking: Whether to turn on strict checking that checks all features.
+
+ Returns:
+ result: True if operation succeed. False if error happens.
+ """
+
+ cur_state = ad.droid.connectivityCheckAirplaneMode()
+ if cur_state == new_state:
+ ad.log.info("Airplane mode already in %s", new_state)
+ return True
+ elif new_state is None:
+ new_state = not cur_state
+ ad.log.info("Toggle APM mode, from current tate %s to %s", cur_state,
+ new_state)
+ sub_id_list = []
+ active_sub_info = ad.droid.subscriptionGetAllSubInfoList()
+ if active_sub_info:
+ for info in active_sub_info:
+ sub_id_list.append(info['subscriptionId'])
+
+ ad.ed.clear_all_events()
+ time.sleep(0.1)
+ service_state_list = []
+ if new_state:
+ service_state_list.append(SERVICE_STATE_POWER_OFF)
+ ad.log.info("Turn on airplane mode")
+
+ else:
+ # If either one of these 3 events show up, it should be OK.
+ # Normal SIM, phone in service
+ service_state_list.append(SERVICE_STATE_IN_SERVICE)
+ # NO SIM, or Dead SIM, or no Roaming coverage.
+ service_state_list.append(SERVICE_STATE_OUT_OF_SERVICE)
+ service_state_list.append(SERVICE_STATE_EMERGENCY_ONLY)
+ ad.log.info("Turn off airplane mode")
+
+ for sub_id in sub_id_list:
+ ad.droid.telephonyStartTrackingServiceStateChangeForSubscription(
+ sub_id)
+
+ timeout_time = time.time() + MAX_WAIT_TIME_AIRPLANEMODE_EVENT
+ ad.droid.connectivityToggleAirplaneMode(new_state)
+
+ try:
+ try:
+ event = ad.ed.wait_for_event(
+ EventServiceStateChanged,
+ is_event_match_for_list,
+ timeout=MAX_WAIT_TIME_AIRPLANEMODE_EVENT,
+ field=ServiceStateContainer.SERVICE_STATE,
+ value_list=service_state_list)
+ ad.log.info("Got event %s", event)
+ except Empty:
+ ad.log.warning("Did not get expected service state change to %s",
+ service_state_list)
+ finally:
+ for sub_id in sub_id_list:
+ ad.droid.telephonyStopTrackingServiceStateChangeForSubscription(
+ sub_id)
+ except Exception as e:
+ ad.log.error(e)
+
+ # APM on (new_state=True) will turn off bluetooth but may not turn it on
+ try:
+ if new_state and not _wait_for_bluetooth_in_state(
+ log, ad, False, timeout_time - time.time()):
+ ad.log.error(
+ "Failed waiting for bluetooth during airplane mode toggle")
+ if strict_checking: return False
+ except Exception as e:
+ ad.log.error("Failed to check bluetooth state due to %s", e)
+ if strict_checking:
+ raise
+
+ # APM on (new_state=True) will turn off wifi but may not turn it on
+ if new_state and not _wait_for_wifi_in_state(log, ad, False,
+ timeout_time - time.time()):
+ ad.log.error("Failed waiting for wifi during airplane mode toggle on")
+ if strict_checking: return False
+
+ if ad.droid.connectivityCheckAirplaneMode() != new_state:
+ ad.log.error("Set airplane mode to %s failed", new_state)
+ return False
+ return True
+
+
+def wait_and_answer_call(log,
+ ad,
+ incoming_number=None,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ caller=None,
+ video_state=None):
+ """Wait for an incoming call on default voice subscription and
+ accepts the call.
+
+ Args:
+ ad: android device object.
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ return wait_and_answer_call_for_subscription(
+ log,
+ ad,
+ get_incoming_voice_sub_id(ad),
+ incoming_number,
+ incall_ui_display=incall_ui_display,
+ caller=caller,
+ video_state=video_state)
+
+
+def _wait_for_ringing_event(log, ad, wait_time):
+ """Wait for ringing event.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ wait_time: max time to wait for ringing event.
+
+ Returns:
+ event_ringing if received ringing event.
+ otherwise return None.
+ """
+ event_ringing = None
+
+ try:
+ event_ringing = ad.ed.wait_for_event(
+ EventCallStateChanged,
+ is_event_match,
+ timeout=wait_time,
+ field=CallStateContainer.CALL_STATE,
+ value=TELEPHONY_STATE_RINGING)
+ ad.log.info("Receive ringing event")
+ except Empty:
+ ad.log.info("No Ringing Event")
+ finally:
+ return event_ringing
+
+
+def wait_for_ringing_call(log, ad, incoming_number=None):
+ """Wait for an incoming call on default voice subscription and
+ accepts the call.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ return wait_for_ringing_call_for_subscription(
+ log, ad, get_incoming_voice_sub_id(ad), incoming_number)
+
+
+def wait_for_ringing_call_for_subscription(
+ log,
+ ad,
+ sub_id,
+ incoming_number=None,
+ caller=None,
+ event_tracking_started=False,
+ timeout=MAX_WAIT_TIME_CALLEE_RINGING,
+ interval=WAIT_TIME_BETWEEN_STATE_CHECK):
+ """Wait for an incoming call on specified subscription.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ sub_id: subscription ID
+ incoming_number: Expected incoming number. Default is None
+ event_tracking_started: True if event tracking already state outside
+ timeout: time to wait for ring
+ interval: checking interval
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ if not event_tracking_started:
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ ring_event_received = False
+ end_time = time.time() + timeout
+ try:
+ while time.time() < end_time:
+ if not ring_event_received:
+ event_ringing = _wait_for_ringing_event(log, ad, interval)
+ if event_ringing:
+ if incoming_number and not check_phone_number_match(
+ event_ringing['data']
+ [CallStateContainer.INCOMING_NUMBER], incoming_number):
+ ad.log.error(
+ "Incoming Number not match. Expected number:%s, actual number:%s",
+ incoming_number, event_ringing['data'][
+ CallStateContainer.INCOMING_NUMBER])
+ return False
+ ring_event_received = True
+ telephony_state = ad.droid.telephonyGetCallStateForSubscription(
+ sub_id)
+ telecom_state = ad.droid.telecomGetCallState()
+ if telephony_state == TELEPHONY_STATE_RINGING and (
+ telecom_state == TELEPHONY_STATE_RINGING):
+ ad.log.info("callee is in telephony and telecom RINGING state")
+ if caller:
+ if caller.droid.telecomIsInCall():
+ caller.log.info("Caller telecom is in call state")
+ return True
+ else:
+ caller.log.info("Caller telecom is NOT in call state")
+ else:
+ return True
+ else:
+ ad.log.info(
+ "telephony in %s, telecom in %s, expecting RINGING state",
+ telephony_state, telecom_state)
+ time.sleep(interval)
+ finally:
+ if not event_tracking_started:
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
+ sub_id)
+
+
+def wait_for_call_offhook_for_subscription(
+ log,
+ ad,
+ sub_id,
+ event_tracking_started=False,
+ timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
+ interval=WAIT_TIME_BETWEEN_STATE_CHECK):
+ """Wait for an incoming call on specified subscription.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ sub_id: subscription ID
+ timeout: time to wait for ring
+ interval: checking interval
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ if not event_tracking_started:
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ offhook_event_received = False
+ end_time = time.time() + timeout
+ try:
+ while time.time() < end_time:
+ if not offhook_event_received:
+ if wait_for_call_offhook_event(log, ad, sub_id, True,
+ interval):
+ offhook_event_received = True
+ telephony_state = ad.droid.telephonyGetCallStateForSubscription(
+ sub_id)
+ telecom_state = ad.droid.telecomGetCallState()
+ if telephony_state == TELEPHONY_STATE_OFFHOOK and (
+ telecom_state == TELEPHONY_STATE_OFFHOOK):
+ ad.log.info("telephony and telecom are in OFFHOOK state")
+ return True
+ else:
+ ad.log.info(
+ "telephony in %s, telecom in %s, expecting OFFHOOK state",
+ telephony_state, telecom_state)
+ if offhook_event_received:
+ time.sleep(interval)
+ finally:
+ if not event_tracking_started:
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
+ sub_id)
+
+
+def wait_for_call_offhook_event(
+ log,
+ ad,
+ sub_id,
+ event_tracking_started=False,
+ timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT):
+ """Wait for an incoming call on specified subscription.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ event_tracking_started: True if event tracking already state outside
+ timeout: time to wait for event
+
+ Returns:
+ True: if call offhook event is received.
+ False: if call offhook event is not received.
+ """
+ if not event_tracking_started:
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ try:
+ ad.ed.wait_for_event(
+ EventCallStateChanged,
+ is_event_match,
+ timeout=timeout,
+ field=CallStateContainer.CALL_STATE,
+ value=TELEPHONY_STATE_OFFHOOK)
+ ad.log.info("Got event %s", TELEPHONY_STATE_OFFHOOK)
+ except Empty:
+ ad.log.info("No event for call state change to OFFHOOK")
+ return False
+ finally:
+ if not event_tracking_started:
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
+ sub_id)
+ return True
+
+
+def wait_and_answer_call_for_subscription(
+ log,
+ ad,
+ sub_id,
+ incoming_number=None,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ timeout=MAX_WAIT_TIME_CALLEE_RINGING,
+ caller=None,
+ video_state=None):
+ """Wait for an incoming call on specified subscription and
+ accepts the call.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ sub_id: subscription ID
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ try:
+ if not wait_for_ringing_call_for_subscription(
+ log,
+ ad,
+ sub_id,
+ incoming_number=incoming_number,
+ caller=caller,
+ event_tracking_started=True,
+ timeout=timeout):
+ ad.log.info("Incoming call ringing check failed.")
+ return False
+ ad.log.info("Accept the ring call")
+ ad.droid.telecomAcceptRingingCall(video_state)
+
+ if wait_for_call_offhook_for_subscription(
+ log, ad, sub_id, event_tracking_started=True):
+ return True
+ else:
+ ad.log.error("Could not answer the call.")
+ return False
+ except Exception as e:
+ log.error(e)
+ return False
+ finally:
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
+ if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+ ad.droid.telecomShowInCallScreen()
+ elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+ ad.droid.showHomeScreen()
+
+
+def wait_and_reject_call(log,
+ ad,
+ incoming_number=None,
+ delay_reject=WAIT_TIME_REJECT_CALL,
+ reject=True):
+ """Wait for an incoming call on default voice subscription and
+ reject the call.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ delay_reject: time to wait before rejecting the call
+ Optional. Default is WAIT_TIME_REJECT_CALL
+
+ Returns:
+ True: if incoming call is received and reject successfully.
+ False: for errors
+ """
+ return wait_and_reject_call_for_subscription(log, ad,
+ get_incoming_voice_sub_id(ad),
+ incoming_number, delay_reject,
+ reject)
+
+
+def wait_and_reject_call_for_subscription(log,
+ ad,
+ sub_id,
+ incoming_number=None,
+ delay_reject=WAIT_TIME_REJECT_CALL,
+ reject=True):
+ """Wait for an incoming call on specific subscription and
+ reject the call.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ sub_id: subscription ID
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ delay_reject: time to wait before rejecting the call
+ Optional. Default is WAIT_TIME_REJECT_CALL
+
+ Returns:
+ True: if incoming call is received and reject successfully.
+ False: for errors
+ """
+
+ if not wait_for_ringing_call_for_subscription(log, ad, sub_id,
+ incoming_number):
+ ad.log.error(
+ "Could not reject a call: incoming call in ringing check failed.")
+ return False
+
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ if reject is True:
+ # Delay between ringing and reject.
+ time.sleep(delay_reject)
+ is_find = False
+ # Loop the call list and find the matched one to disconnect.
+ for call in ad.droid.telecomCallGetCallIds():
+ if check_phone_number_match(
+ get_number_from_tel_uri(get_call_uri(ad, call)),
+ incoming_number):
+ ad.droid.telecomCallDisconnect(call)
+ ad.log.info("Callee reject the call")
+ is_find = True
+ if is_find is False:
+ ad.log.error("Callee did not find matching call to reject.")
+ return False
+ else:
+ # don't reject on callee. Just ignore the incoming call.
+ ad.log.info("Callee received incoming call. Ignore it.")
+ try:
+ ad.ed.wait_for_event(
+ EventCallStateChanged,
+ is_event_match_for_list,
+ timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
+ field=CallStateContainer.CALL_STATE,
+ value_list=[TELEPHONY_STATE_IDLE, TELEPHONY_STATE_OFFHOOK])
+ except Empty:
+ ad.log.error("No onCallStateChangedIdle event received.")
+ return False
+ finally:
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
+ return True
+
+
+def hangup_call(log, ad, is_emergency=False):
+ """Hang up ongoing active call.
+
+ Args:
+ log: log object.
+ ad: android device object.
+
+ Returns:
+ True: if all calls are cleared
+ False: for errors
+ """
+ # short circuit in case no calls are active
+ if not ad.droid.telecomIsInCall():
+ return True
+ ad.ed.clear_events(EventCallStateChanged)
+ ad.droid.telephonyStartTrackingCallState()
+ ad.log.info("Hangup call.")
+ if is_emergency:
+ for call in ad.droid.telecomCallGetCallIds():
+ ad.droid.telecomCallDisconnect(call)
+ else:
+ ad.droid.telecomEndCall()
+
+ try:
+ ad.ed.wait_for_event(
+ EventCallStateChanged,
+ is_event_match,
+ timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
+ field=CallStateContainer.CALL_STATE,
+ value=TELEPHONY_STATE_IDLE)
+ except Empty:
+ ad.log.warning("Call state IDLE event is not received after hang up.")
+ finally:
+ ad.droid.telephonyStopTrackingCallStateChange()
+ if not wait_for_state(ad.droid.telecomIsInCall, False, 15, 1):
+ ad.log.error("Telecom is in call, hangup call failed.")
+ return False
+ return True
+
+
+def wait_for_cbrs_data_active_sub_change_event(
+ ad,
+ event_tracking_started=False,
+ timeout=120):
+ """Wait for an data change event on specified subscription.
+
+ Args:
+ ad: android device object.
+ event_tracking_started: True if event tracking already state outside
+ timeout: time to wait for event
+
+ Returns:
+ True: if data change event is received.
+ False: if data change event is not received.
+ """
+ if not event_tracking_started:
+ ad.ed.clear_events(EventActiveDataSubIdChanged)
+ ad.droid.telephonyStartTrackingActiveDataChange()
+ try:
+ ad.ed.wait_for_event(
+ EventActiveDataSubIdChanged,
+ is_event_match,
+ timeout=timeout)
+ ad.log.info("Got event activedatasubidchanged")
+ except Empty:
+ ad.log.info("No event for data subid change")
+ return False
+ finally:
+ if not event_tracking_started:
+ ad.droid.telephonyStopTrackingActiveDataChange()
+ return True
+
+
+def is_current_data_on_cbrs(ad, cbrs_subid):
+ """Verifies if current data sub is on CBRS
+
+ Args:
+ ad: android device object.
+ cbrs_subid: sub_id against which we need to check
+
+ Returns:
+ True: if data is on cbrs
+ False: if data is not on cbrs
+ """
+ if cbrs_subid is None:
+ return False
+ current_data = ad.droid.subscriptionGetActiveDataSubscriptionId()
+ ad.log.info("Current Data subid %s cbrs_subid %s", current_data, cbrs_subid)
+ if current_data == cbrs_subid:
+ return True
+ else:
+ return False
+
+
+def get_current_override_network_type(ad, timeout=30):
+ """Returns current override network type
+
+ Args:
+ ad: android device object.
+ timeout: max time to wait for event
+
+ Returns:
+ value: current override type
+ -1: if no event received
+ """
+ override_value_list = [OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NONE,
+ OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_MMWAVE,
+ OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_LTE_CA,
+ OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO]
+ ad.ed.clear_events(EventDisplayInfoChanged)
+ ad.droid.telephonyStartTrackingDisplayInfoChange()
+ try:
+ event = ad.ed.wait_for_event(
+ EventDisplayInfoChanged,
+ is_event_match_for_list,
+ timeout=timeout,
+ field=DisplayInfoContainer.OVERRIDE,
+ value_list=override_value_list)
+ override_type = event['data']['override']
+ ad.log.info("Current Override Type is %s", override_type)
+ return override_type
+ except Empty:
+ ad.log.info("No event for display info change")
+ return -1
+ finally:
+ ad.droid.telephonyStopTrackingDisplayInfoChange()
+ return -1
+
+
+def is_current_network_5g_nsa(ad, timeout=30):
+ """Verifies 5G NSA override network type
+
+ Args:
+ ad: android device object.
+ timeout: max time to wait for event
+
+ Returns:
+ True: if data is on 5g NSA
+ False: if data is not on 5g NSA
+ """
+ ad.ed.clear_events(EventDisplayInfoChanged)
+ ad.droid.telephonyStartTrackingDisplayInfoChange()
+ try:
+ event = ad.ed.wait_for_event(
+ EventDisplayInfoChanged,
+ is_event_match,
+ timeout=timeout,
+ field=DisplayInfoContainer.OVERRIDE,
+ value=OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA)
+ ad.log.info("Got expected event %s", event)
+ return True
+ except Empty:
+ ad.log.info("No event for display info change")
+ return False
+ finally:
+ ad.droid.telephonyStopTrackingDisplayInfoChange()
+ return None
+
+
+def disconnect_call_by_id(log, ad, call_id):
+ """Disconnect call by call id.
+ """
+ ad.droid.telecomCallDisconnect(call_id)
+ return True
+
+
+def _phone_number_remove_prefix(number):
+ """Remove the country code and other prefix from the input phone number.
+ Currently only handle phone number with the following formats:
+ (US phone number format)
+ +1abcxxxyyyy
+ 1abcxxxyyyy
+ abcxxxyyyy
+ abc xxx yyyy
+ abc.xxx.yyyy
+ abc-xxx-yyyy
+ (EEUK phone number format)
+ +44abcxxxyyyy
+ 0abcxxxyyyy
+
+ Args:
+ number: input phone number
+
+ Returns:
+ Phone number without country code or prefix
+ """
+ if number is None:
+ return None, None
+ for country_code in COUNTRY_CODE_LIST:
+ if number.startswith(country_code):
+ return number[len(country_code):], country_code
+ if number[0] == "1" or number[0] == "0":
+ return number[1:], None
+ return number, None
+
+
+def check_phone_number_match(number1, number2):
+ """Check whether two input phone numbers match or not.
+
+ Compare the two input phone numbers.
+ If they match, return True; otherwise, return False.
+ Currently only handle phone number with the following formats:
+ (US phone number format)
+ +1abcxxxyyyy
+ 1abcxxxyyyy
+ abcxxxyyyy
+ abc xxx yyyy
+ abc.xxx.yyyy
+ abc-xxx-yyyy
+ (EEUK phone number format)
+ +44abcxxxyyyy
+ 0abcxxxyyyy
+
+ There are some scenarios we can not verify, one example is:
+ number1 = +15555555555, number2 = 5555555555
+ (number2 have no country code)
+
+ Args:
+ number1: 1st phone number to be compared.
+ number2: 2nd phone number to be compared.
+
+ Returns:
+ True if two phone numbers match. Otherwise False.
+ """
+ number1 = phone_number_formatter(number1)
+ number2 = phone_number_formatter(number2)
+ # Handle extra country code attachment when matching phone number
+ if number1[-7:] in number2 or number2[-7:] in number1:
+ return True
+ else:
+ logging.info("phone number1 %s and number2 %s does not match" %
+ (number1, number2))
+ return False
+
+
+def initiate_call(log,
+ ad,
+ callee_number,
+ emergency=False,
+ timeout=MAX_WAIT_TIME_CALL_INITIATION,
+ checking_interval=5,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ video=False):
+ """Make phone call from caller to callee.
+
+ Args:
+ ad_caller: Caller android device object.
+ callee_number: Callee phone number.
+ emergency : specify the call is emergency.
+ Optional. Default value is False.
+ incall_ui_display: show the dialer UI foreground or backgroud
+ video: whether to initiate as video call
+
+ Returns:
+ result: if phone call is placed successfully.
+ """
+ ad.ed.clear_events(EventCallStateChanged)
+ sub_id = get_outgoing_voice_sub_id(ad)
+ begin_time = get_device_epoch_time(ad)
+ ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+ try:
+ # Make a Call
+ ad.log.info("Make a phone call to %s", callee_number)
+ if emergency:
+ ad.droid.telecomCallEmergencyNumber(callee_number)
+ else:
+ ad.droid.telecomCallNumber(callee_number, video)
+
+ # Verify OFFHOOK state
+ if not wait_for_call_offhook_for_subscription(
+ log, ad, sub_id, event_tracking_started=True):
+ ad.log.info("sub_id %s not in call offhook state", sub_id)
+ last_call_drop_reason(ad, begin_time=begin_time)
+ return False
+ else:
+ return True
+ finally:
+ if hasattr(ad, "sdm_log") and getattr(ad, "sdm_log"):
+ ad.adb.shell("i2cset -fy 3 64 6 1 b", ignore_status=True)
+ ad.adb.shell("i2cset -fy 3 65 6 1 b", ignore_status=True)
+ ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
+ if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+ ad.droid.telecomShowInCallScreen()
+ elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+ ad.droid.showHomeScreen()
+
+
+def dial_phone_number(ad, callee_number):
+ for number in str(callee_number):
+ if number == "#":
+ ad.send_keycode("POUND")
+ elif number == "*":
+ ad.send_keycode("STAR")
+ elif number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]:
+ ad.send_keycode("%s" % number)
+
+
+def get_call_state_by_adb(ad):
+ slot_index_of_default_voice_subid = get_slot_index_from_subid(ad.log, ad,
+ get_incoming_voice_sub_id(ad))
+ output = ad.adb.shell("dumpsys telephony.registry | grep mCallState")
+ if "mCallState" in output:
+ call_state_list = re.findall("mCallState=(\d)", output)
+ if call_state_list:
+ return call_state_list[slot_index_of_default_voice_subid]
+
+
+def check_call_state_connected_by_adb(ad):
+ return "2" in get_call_state_by_adb(ad)
+
+
+def check_call_state_idle_by_adb(ad):
+ return "0" in get_call_state_by_adb(ad)
+
+
+def check_call_state_ring_by_adb(ad):
+ return "1" in get_call_state_by_adb(ad)
+
+
+def get_incoming_call_number_by_adb(ad):
+ output = ad.adb.shell(
+ "dumpsys telephony.registry | grep mCallIncomingNumber")
+ return re.search(r"mCallIncomingNumber=(.*)", output).group(1)
+
+
+def emergency_dialer_call_by_keyevent(ad, callee_number):
+ for i in range(3):
+ if "EmergencyDialer" in ad.get_my_current_focus_window():
+ ad.log.info("EmergencyDialer is the current focus window")
+ break
+ elif i <= 2:
+ ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
+ time.sleep(1)
+ else:
+ ad.log.error("Unable to bring up EmergencyDialer")
+ return False
+ ad.log.info("Make a phone call to %s", callee_number)
+ dial_phone_number(ad, callee_number)
+ ad.send_keycode("CALL")
+
+
+def initiate_emergency_dialer_call_by_adb(
+ log,
+ ad,
+ callee_number,
+ timeout=MAX_WAIT_TIME_CALL_INITIATION,
+ checking_interval=5):
+ """Make emergency call by EmergencyDialer.
+
+ Args:
+ ad: Caller android device object.
+ callee_number: Callee phone number.
+ emergency : specify the call is emergency.
+ Optional. Default value is False.
+
+ Returns:
+ result: if phone call is placed successfully.
+ """
+ try:
+ # Make a Call
+ ad.wakeup_screen()
+ ad.send_keycode("MENU")
+ ad.log.info("Call %s", callee_number)
+ ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
+ ad.adb.shell(
+ "am start -a android.intent.action.CALL_EMERGENCY -d tel:%s" %
+ callee_number)
+ if not timeout: return True
+ ad.log.info("Check call state")
+ # Verify Call State
+ elapsed_time = 0
+ while elapsed_time < timeout:
+ time.sleep(checking_interval)
+ elapsed_time += checking_interval
+ if check_call_state_connected_by_adb(ad):
+ ad.log.info("Call to %s is connected", callee_number)
+ return True
+ if check_call_state_idle_by_adb(ad):
+ ad.log.info("Call to %s failed", callee_number)
+ return False
+ ad.log.info("Make call to %s failed", callee_number)
+ return False
+ except Exception as e:
+ ad.log.error("initiate emergency call failed with error %s", e)
+
+
+def hangup_call_by_adb(ad):
+ """Make emergency call by EmergencyDialer.
+
+ Args:
+ ad: Caller android device object.
+ callee_number: Callee phone number.
+ """
+ ad.log.info("End call by adb")
+ ad.send_keycode("ENDCALL")
+
+
+def dumpsys_all_call_info(ad):
+ """ Get call information by dumpsys telecom. """
+ output = ad.adb.shell("dumpsys telecom")
+ calls = re.findall("Call TC@\d+: {(.*?)}", output, re.DOTALL)
+ calls_info = []
+ for call in calls:
+ call_info = {}
+ for attr in ("startTime", "endTime", "direction", "isInterrupted",
+ "callTechnologies", "callTerminationsReason",
+ "connectionService", "isVideoCall", "callProperties"):
+ match = re.search(r"%s: (.*)" % attr, call)
+ if match:
+ if attr in ("startTime", "endTime"):
+ call_info[attr] = epoch_to_log_line_timestamp(
+ int(match.group(1)))
+ else:
+ call_info[attr] = match.group(1)
+ call_info["inCallServices"] = re.findall(r"name: (.*)", call)
+ calls_info.append(call_info)
+ ad.log.debug("calls_info = %s", calls_info)
+ return calls_info
+
+
+def dumpsys_last_call_info(ad):
+ """ Get call information by dumpsys telecom. """
+ num = dumpsys_last_call_number(ad)
+ output = ad.adb.shell("dumpsys telecom")
+ result = re.search(r"Call TC@%s: {(.*?)}" % num, output, re.DOTALL)
+ call_info = {"TC": num}
+ if result:
+ result = result.group(1)
+ for attr in ("startTime", "endTime", "direction", "isInterrupted",
+ "callTechnologies", "callTerminationsReason",
+ "isVideoCall", "callProperties"):
+ match = re.search(r"%s: (.*)" % attr, result)
+ if match:
+ if attr in ("startTime", "endTime"):
+ call_info[attr] = epoch_to_log_line_timestamp(
+ int(match.group(1)))
+ else:
+ call_info[attr] = match.group(1)
+ ad.log.debug("call_info = %s", call_info)
+ return call_info
+
+
+def dumpsys_last_call_number(ad):
+ output = ad.adb.shell("dumpsys telecom")
+ call_nums = re.findall("Call TC@(\d+):", output)
+ if not call_nums:
+ return 0
+ else:
+ return int(call_nums[-1])
+
+
+def dumpsys_new_call_info(ad, last_tc_number, retries=3, interval=5):
+ for i in range(retries):
+ if dumpsys_last_call_number(ad) > last_tc_number:
+ call_info = dumpsys_last_call_info(ad)
+ ad.log.info("New call info = %s", sorted(call_info.items()))
+ return call_info
+ else:
+ time.sleep(interval)
+ ad.log.error("New call is not in sysdump telecom")
+ return {}
+
+
+def dumpsys_carrier_config(ad):
+ output = ad.adb.shell("dumpsys carrier_config").split("\n")
+ output_phone_id_0 = []
+ output_phone_id_1 = []
+ current_output = []
+ for line in output:
+ if "Phone Id = 0" in line:
+ current_output = output_phone_id_0
+ elif "Phone Id = 1" in line:
+ current_output = output_phone_id_1
+ current_output.append(line.strip())
+
+ configs = {}
+ if ad.adb.getprop("ro.build.version.release")[0] in ("9", "P"):
+ phone_count = 1
+ if "," in ad.adb.getprop("gsm.network.type"):
+ phone_count = 2
+ else:
+ phone_count = ad.droid.telephonyGetPhoneCount()
+
+ slot_0_subid = get_subid_from_slot_index(ad.log, ad, 0)
+ if slot_0_subid != INVALID_SUB_ID:
+ configs[slot_0_subid] = {}
+
+ if phone_count == 2:
+ slot_1_subid = get_subid_from_slot_index(ad.log, ad, 1)
+ if slot_1_subid != INVALID_SUB_ID:
+ configs[slot_1_subid] = {}
+
+ attrs = [attr for attr in dir(CarrierConfigs) if not attr.startswith("__")]
+ for attr in attrs:
+ attr_string = getattr(CarrierConfigs, attr)
+ values = re.findall(
+ r"%s = (\S+)" % attr_string, "\n".join(output_phone_id_0))
+
+ if slot_0_subid != INVALID_SUB_ID:
+ if values:
+ value = values[-1]
+ if value == "true":
+ configs[slot_0_subid][attr_string] = True
+ elif value == "false":
+ configs[slot_0_subid][attr_string] = False
+ elif attr_string == CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT:
+ if value == "0":
+ configs[slot_0_subid][attr_string] = WFC_MODE_WIFI_ONLY
+ elif value == "1":
+ configs[slot_0_subid][attr_string] = \
+ WFC_MODE_CELLULAR_PREFERRED
+ elif value == "2":
+ configs[slot_0_subid][attr_string] = \
+ WFC_MODE_WIFI_PREFERRED
+ else:
+ try:
+ configs[slot_0_subid][attr_string] = int(value)
+ except Exception:
+ configs[slot_0_subid][attr_string] = value
+ else:
+ configs[slot_0_subid][attr_string] = None
+
+ if phone_count == 2:
+ if slot_1_subid != INVALID_SUB_ID:
+ values = re.findall(
+ r"%s = (\S+)" % attr_string, "\n".join(output_phone_id_1))
+ if values:
+ value = values[-1]
+ if value == "true":
+ configs[slot_1_subid][attr_string] = True
+ elif value == "false":
+ configs[slot_1_subid][attr_string] = False
+ elif attr_string == CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT:
+ if value == "0":
+ configs[slot_1_subid][attr_string] = \
+ WFC_MODE_WIFI_ONLY
+ elif value == "1":
+ configs[slot_1_subid][attr_string] = \
+ WFC_MODE_CELLULAR_PREFERRED
+ elif value == "2":
+ configs[slot_1_subid][attr_string] = \
+ WFC_MODE_WIFI_PREFERRED
+ else:
+ try:
+ configs[slot_1_subid][attr_string] = int(value)
+ except Exception:
+ configs[slot_1_subid][attr_string] = value
+ else:
+ configs[slot_1_subid][attr_string] = None
+ return configs
+
+
+def get_phone_capability(ad):
+ carrier_configs = dumpsys_carrier_config(ad)
+ for sub_id in carrier_configs:
+ capabilities = []
+ if carrier_configs[sub_id][CarrierConfigs.VOLTE_AVAILABLE_BOOL]:
+ capabilities.append(CAPABILITY_VOLTE)
+ if carrier_configs[sub_id][CarrierConfigs.WFC_IMS_AVAILABLE_BOOL]:
+ capabilities.append(CAPABILITY_WFC)
+ if carrier_configs[sub_id][CarrierConfigs.EDITABLE_WFC_MODE_BOOL]:
+ capabilities.append(CAPABILITY_WFC_MODE_CHANGE)
+ if carrier_configs[sub_id][CarrierConfigs.SUPPORT_CONFERENCE_CALL_BOOL]:
+ capabilities.append(CAPABILITY_CONFERENCE)
+ if carrier_configs[sub_id][CarrierConfigs.VT_AVAILABLE_BOOL]:
+ capabilities.append(CAPABILITY_VT)
+ if carrier_configs[sub_id][CarrierConfigs.VOLTE_PROVISIONED_BOOL]:
+ capabilities.append(CAPABILITY_VOLTE_PROVISIONING)
+ if carrier_configs[sub_id][CarrierConfigs.VOLTE_OVERRIDE_WFC_BOOL]:
+ capabilities.append(CAPABILITY_VOLTE_OVERRIDE_WFC_PROVISIONING)
+ if carrier_configs[sub_id][CarrierConfigs.HIDE_ENHANCED_4G_LTE_BOOL]:
+ capabilities.append(CAPABILITY_HIDE_ENHANCED_4G_LTE_BOOL)
+
+ ad.log.info("Capabilities of sub ID %s: %s", sub_id, capabilities)
+ if not getattr(ad, 'telephony', {}):
+ ad.telephony["subscription"] = {}
+ ad.telephony["subscription"][sub_id] = {}
+ setattr(
+ ad.telephony["subscription"][sub_id],
+ 'capabilities', capabilities)
+
+ else:
+ ad.telephony["subscription"][sub_id]["capabilities"] = capabilities
+ if CAPABILITY_WFC not in capabilities:
+ wfc_modes = []
+ else:
+ if carrier_configs[sub_id].get(
+ CarrierConfigs.EDITABLE_WFC_MODE_BOOL, False):
+ wfc_modes = [
+ WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED]
+ else:
+ wfc_modes = [
+ carrier_configs[sub_id].get(
+ CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT,
+ WFC_MODE_CELLULAR_PREFERRED)
+ ]
+ if carrier_configs[sub_id].get(
+ CarrierConfigs.WFC_SUPPORTS_WIFI_ONLY_BOOL,
+ False) and WFC_MODE_WIFI_ONLY not in wfc_modes:
+ wfc_modes.append(WFC_MODE_WIFI_ONLY)
+ ad.telephony["subscription"][sub_id]["wfc_modes"] = wfc_modes
+ if wfc_modes:
+ ad.log.info("Supported WFC modes for sub ID %s: %s", sub_id,
+ wfc_modes)
+
+
+def get_capability_for_subscription(ad, capability, subid):
+ if capability in ad.telephony["subscription"][subid].get(
+ "capabilities", []):
+ ad.log.info('Capability "%s" is available for sub ID %s.',
+ capability, subid)
+ return True
+ else:
+ ad.log.info('Capability "%s" is NOT available for sub ID %s.',
+ capability, subid)
+ return False
+
+
+def call_reject(log, ad_caller, ad_callee, reject=True):
+ """Caller call Callee, then reject on callee.
+
+
+ """
+ subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+ subid_callee = ad_callee.incoming_voice_sub_id
+ ad_caller.log.info("Sub-ID Caller %s, Sub-ID Callee %s", subid_caller,
+ subid_callee)
+ return call_reject_for_subscription(log, ad_caller, ad_callee,
+ subid_caller, subid_callee, reject)
+
+
+def call_reject_for_subscription(log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ reject=True):
+ """
+ """
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+
+ ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
+ if not initiate_call(log, ad_caller, callee_number):
+ ad_caller.log.error("Initiate call failed")
+ return False
+
+ if not wait_and_reject_call_for_subscription(
+ log, ad_callee, subid_callee, caller_number, WAIT_TIME_REJECT_CALL,
+ reject):
+ ad_callee.log.error("Reject call fail.")
+ return False
+ # Check if incoming call is cleared on callee or not.
+ if ad_callee.droid.telephonyGetCallStateForSubscription(
+ subid_callee) == TELEPHONY_STATE_RINGING:
+ ad_callee.log.error("Incoming call is not cleared")
+ return False
+ # Hangup on caller
+ hangup_call(log, ad_caller)
+ return True
+
+
+def call_reject_leave_message(log,
+ ad_caller,
+ ad_callee,
+ verify_caller_func=None,
+ wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
+ """On default voice subscription, Call from caller to callee,
+ reject on callee, caller leave a voice mail.
+
+ 1. Caller call Callee.
+ 2. Callee reject incoming call.
+ 3. Caller leave a voice mail.
+ 4. Verify callee received the voice mail notification.
+
+ Args:
+ ad_caller: caller android device object.
+ ad_callee: callee android device object.
+ verify_caller_func: function to verify caller is in correct state while in-call.
+ This is optional, default is None.
+ wait_time_in_call: time to wait when leaving a voice mail.
+ This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
+
+ Returns:
+ True: if voice message is received on callee successfully.
+ False: for errors
+ """
+ subid_caller = get_outgoing_voice_sub_id(ad_caller)
+ subid_callee = get_incoming_voice_sub_id(ad_callee)
+ return call_reject_leave_message_for_subscription(
+ log, ad_caller, ad_callee, subid_caller, subid_callee,
+ verify_caller_func, wait_time_in_call)
+
+
+def check_reject_needed_for_voice_mail(log, ad_callee):
+ """Check if the carrier requires reject call to receive voice mail or just keep ringing
+ Requested in b//155935290
+ Four Japan carriers do not need to reject
+ SBM, KDDI, Ntt Docomo, Rakuten
+ Args:
+ log: log object
+ ad_callee: android device object
+ Returns:
+ True if callee's carrier is not one of the four Japan carriers
+ False if callee's carrier is one of the four Japan carriers
+ """
+
+ operators_no_reject = [CARRIER_NTT_DOCOMO,
+ CARRIER_KDDI,
+ CARRIER_RAKUTEN,
+ CARRIER_SBM]
+ operator_name = get_operator_name(log, ad_callee)
+
+ return operator_name not in operators_no_reject
+
+
+def call_reject_leave_message_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ verify_caller_func=None,
+ wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
+ """On specific voice subscription, Call from caller to callee,
+ reject on callee, caller leave a voice mail.
+
+ 1. Caller call Callee.
+ 2. Callee reject incoming call.
+ 3. Caller leave a voice mail.
+ 4. Verify callee received the voice mail notification.
+
+ Args:
+ ad_caller: caller android device object.
+ ad_callee: callee android device object.
+ subid_caller: caller's subscription id.
+ subid_callee: callee's subscription id.
+ verify_caller_func: function to verify caller is in correct state while in-call.
+ This is optional, default is None.
+ wait_time_in_call: time to wait when leaving a voice mail.
+ This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
+
+ Returns:
+ True: if voice message is received on callee successfully.
+ False: for errors
+ """
+
+ # Currently this test utility only works for TMO and ATT and SPT.
+ # It does not work for VZW (see b/21559800)
+ # "with VVM TelephonyManager APIs won't work for vm"
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+
+ ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
+
+ try:
+ voice_mail_count_before = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
+ subid_callee)
+ ad_callee.log.info("voice mail count is %s", voice_mail_count_before)
+ # -1 means there are unread voice mail, but the count is unknown
+ # 0 means either this API not working (VZW) or no unread voice mail.
+ if voice_mail_count_before != 0:
+ log.warning("--Pending new Voice Mail, please clear on phone.--")
+
+ if not initiate_call(log, ad_caller, callee_number):
+ ad_caller.log.error("Initiate call failed.")
+ return False
+ if check_reject_needed_for_voice_mail(log, ad_callee):
+ carrier_specific_delay_reject = 30
+ else:
+ carrier_specific_delay_reject = 2
+ carrier_reject_call = not check_reject_needed_for_voice_mail(log, ad_callee)
+
+ if not wait_and_reject_call_for_subscription(
+ log, ad_callee, subid_callee, incoming_number=caller_number, delay_reject=carrier_specific_delay_reject,
+ reject=carrier_reject_call):
+ ad_callee.log.error("Reject call fail.")
+ return False
+
+ ad_callee.droid.telephonyStartTrackingVoiceMailStateChangeForSubscription(
+ subid_callee)
+
+ # ensure that all internal states are updated in telecom
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ ad_callee.ed.clear_events(EventCallStateChanged)
+
+ if verify_caller_func and not verify_caller_func(log, ad_caller):
+ ad_caller.log.error("Caller not in correct state!")
+ return False
+
+ # TODO: b/26293512 Need to play some sound to leave message.
+ # Otherwise carrier voice mail server may drop this voice mail.
+ time.sleep(wait_time_in_call)
+
+ if not verify_caller_func:
+ caller_state_result = ad_caller.droid.telecomIsInCall()
+ else:
+ caller_state_result = verify_caller_func(log, ad_caller)
+ if not caller_state_result:
+ ad_caller.log.error("Caller not in correct state after %s seconds",
+ wait_time_in_call)
+
+ if not hangup_call(log, ad_caller):
+ ad_caller.log.error("Error in Hanging-Up Call")
+ return False
+
+ ad_callee.log.info("Wait for voice mail indicator on callee.")
+ try:
+ event = ad_callee.ed.wait_for_event(
+ EventMessageWaitingIndicatorChanged,
+ _is_on_message_waiting_event_true)
+ ad_callee.log.info("Got event %s", event)
+ except Empty:
+ ad_callee.log.warning("No expected event %s",
+ EventMessageWaitingIndicatorChanged)
+ return False
+ voice_mail_count_after = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
+ subid_callee)
+ ad_callee.log.info(
+ "telephonyGetVoiceMailCount output - before: %s, after: %s",
+ voice_mail_count_before, voice_mail_count_after)
+
+ # voice_mail_count_after should:
+ # either equals to (voice_mail_count_before + 1) [For ATT and SPT]
+ # or equals to -1 [For TMO]
+ # -1 means there are unread voice mail, but the count is unknown
+ if not check_voice_mail_count(log, ad_callee, voice_mail_count_before,
+ voice_mail_count_after):
+ log.error("before and after voice mail count is not incorrect.")
+ return False
+ finally:
+ ad_callee.droid.telephonyStopTrackingVoiceMailStateChangeForSubscription(
+ subid_callee)
+ return True
+
+
+def call_voicemail_erase_all_pending_voicemail(log, ad):
+ """Script for phone to erase all pending voice mail.
+ This script only works for TMO and ATT and SPT currently.
+ This script only works if phone have already set up voice mail options,
+ and phone should disable password protection for voice mail.
+
+ 1. If phone don't have pending voice message, return True.
+ 2. Dial voice mail number.
+ For TMO, the number is '123'
+ For ATT, the number is phone's number
+ For SPT, the number is phone's number
+ 3. Wait for voice mail connection setup.
+ 4. Wait for voice mail play pending voice message.
+ 5. Send DTMF to delete one message.
+ The digit is '7'.
+ 6. Repeat steps 4 and 5 until voice mail server drop this call.
+ (No pending message)
+ 6. Check telephonyGetVoiceMailCount result. it should be 0.
+
+ Args:
+ log: log object
+ ad: android device object
+ Returns:
+ False if error happens. True is succeed.
+ """
+ log.info("Erase all pending voice mail.")
+ count = ad.droid.telephonyGetVoiceMailCount()
+ if count == 0:
+ ad.log.info("No Pending voice mail.")
+ return True
+ if count == -1:
+ ad.log.info("There is pending voice mail, but the count is unknown")
+ count = MAX_SAVED_VOICE_MAIL
+ else:
+ ad.log.info("There are %s voicemails", count)
+
+ voice_mail_number = get_voice_mail_number(log, ad)
+ delete_digit = get_voice_mail_delete_digit(get_operator_name(log, ad))
+ if not initiate_call(log, ad, voice_mail_number):
+ log.error("Initiate call to voice mail failed.")
+ return False
+ time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
+ callId = ad.droid.telecomCallGetCallIds()[0]
+ time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
+ while (is_phone_in_call(log, ad) and (count > 0)):
+ ad.log.info("Press %s to delete voice mail.", delete_digit)
+ ad.droid.telecomCallPlayDtmfTone(callId, delete_digit)
+ ad.droid.telecomCallStopDtmfTone(callId)
+ time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
+ count -= 1
+ if is_phone_in_call(log, ad):
+ hangup_call(log, ad)
+
+ # wait for telephonyGetVoiceMailCount to update correct result
+ remaining_time = MAX_WAIT_TIME_VOICE_MAIL_COUNT
+ while ((remaining_time > 0)
+ and (ad.droid.telephonyGetVoiceMailCount() != 0)):
+ time.sleep(1)
+ remaining_time -= 1
+ current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
+ ad.log.info("telephonyGetVoiceMailCount: %s", current_voice_mail_count)
+ return (current_voice_mail_count == 0)
+
+
+def _is_on_message_waiting_event_true(event):
+ """Private function to return if the received EventMessageWaitingIndicatorChanged
+ event MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING field is True.
+ """
+ return event['data'][MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING]
+
+
+def call_setup_teardown(log,
+ ad_caller,
+ ad_callee,
+ ad_hangup=None,
+ verify_caller_func=None,
+ verify_callee_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None,
+ slot_id_callee=None):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on default voice subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_call_mode_caller: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ verify_call_mode_caller: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+ dialing_number_length: the number of digits used for dialing
+ slot_id_callee : the slot if of the callee to call to
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ subid_caller = get_outgoing_voice_sub_id(ad_caller)
+ if slot_id_callee is None:
+ subid_callee = get_incoming_voice_sub_id(ad_callee)
+ else:
+ subid_callee = get_subid_from_slot_index(log, ad_callee, slot_id_callee)
+
+ return call_setup_teardown_for_subscription(
+ log, ad_caller, ad_callee, subid_caller, subid_callee, ad_hangup,
+ verify_caller_func, verify_callee_func, wait_time_in_call,
+ incall_ui_display, dialing_number_length, video_state)
+
+
+def call_setup_teardown_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ ad_hangup=None,
+ verify_caller_func=None,
+ verify_callee_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on specified subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ subid_caller: Caller subscription ID
+ subid_callee: Callee subscription ID
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_call_mode_caller: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ verify_call_mode_caller: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ TelResultWrapper which will evaluate as False if error.
+
+ """
+ CHECK_INTERVAL = 5
+ begin_time = get_current_epoch_time()
+ if not verify_caller_func:
+ verify_caller_func = is_phone_in_call
+ if not verify_callee_func:
+ verify_callee_func = is_phone_in_call
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+ if dialing_number_length:
+ skip_test = False
+ trunc_position = 0 - int(dialing_number_length)
+ try:
+ caller_area_code = caller_number[:trunc_position]
+ callee_area_code = callee_number[:trunc_position]
+ callee_dial_number = callee_number[trunc_position:]
+ except:
+ skip_test = True
+ if caller_area_code != callee_area_code:
+ skip_test = True
+ if skip_test:
+ msg = "Cannot make call from %s to %s by %s digits" % (
+ caller_number, callee_number, dialing_number_length)
+ ad_caller.log.info(msg)
+ raise signals.TestSkip(msg)
+ else:
+ callee_number = callee_dial_number
+
+ tel_result_wrapper = TelResultWrapper(CallResult('SUCCESS'))
+ msg = "Call from %s to %s" % (caller_number, callee_number)
+ if video_state:
+ msg = "Video %s" % msg
+ video = True
+ else:
+ video = False
+ if ad_hangup:
+ msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+ ad_caller.log.info(msg)
+
+ for ad in (ad_caller, ad_callee):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ if call_ids:
+ ad.log.info("Pre-exist CallId %s before making call", call_ids)
+ try:
+ if not initiate_call(
+ log,
+ ad_caller,
+ callee_number,
+ incall_ui_display=incall_ui_display,
+ video=video):
+ ad_caller.log.error("Initiate call failed.")
+ tel_result_wrapper.result_value = CallResult('INITIATE_FAILED')
+ return tel_result_wrapper
+ else:
+ ad_caller.log.info("Caller initate call successfully")
+ if not wait_and_answer_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller_number,
+ caller=ad_caller,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+ ad_callee.log.error("Answer call fail.")
+ tel_result_wrapper.result_value = CallResult(
+ 'NO_RING_EVENT_OR_ANSWER_FAILED')
+ return tel_result_wrapper
+ else:
+ ad_callee.log.info("Callee answered the call successfully")
+
+ for ad, call_func in zip([ad_caller, ad_callee],
+ [verify_caller_func, verify_callee_func]):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_ids = set(call_ids) - set(ad.call_ids)
+ if not new_call_ids:
+ ad.log.error(
+ "No new call ids are found after call establishment")
+ ad.log.error("telecomCallGetCallIds returns %s",
+ ad.droid.telecomCallGetCallIds())
+ tel_result_wrapper.result_value = CallResult('NO_CALL_ID_FOUND')
+ for new_call_id in new_call_ids:
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT')
+ else:
+ ad.log.info("callProperties = %s",
+ ad.droid.telecomCallGetProperties(new_call_id))
+
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state")
+ tel_result_wrapper.result_value = CallResult(
+ 'AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT')
+
+ if call_func(log, ad):
+ ad.log.info("Call is in %s state", call_func.__name__)
+ else:
+ ad.log.error("Call is not in %s state, voice in RAT %s",
+ call_func.__name__,
+ ad.droid.telephonyGetCurrentVoiceNetworkType())
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT')
+ if not tel_result_wrapper:
+ return tel_result_wrapper
+ elapsed_time = 0
+ while (elapsed_time < wait_time_in_call):
+ CHECK_INTERVAL = min(CHECK_INTERVAL,
+ wait_time_in_call - elapsed_time)
+ time.sleep(CHECK_INTERVAL)
+ elapsed_time += CHECK_INTERVAL
+ time_message = "at <%s>/<%s> second." % (elapsed_time,
+ wait_time_in_call)
+ for ad, call_func in [(ad_caller, verify_caller_func),
+ (ad_callee, verify_callee_func)]:
+ if not call_func(log, ad):
+ ad.log.error(
+ "NOT in correct %s state at %s, voice in RAT %s",
+ call_func.__name__, time_message,
+ ad.droid.telephonyGetCurrentVoiceNetworkType())
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED')
+ else:
+ ad.log.info("In correct %s state at %s",
+ call_func.__name__, time_message)
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state at %s",
+ time_message)
+ tel_result_wrapper.result_value = CallResult(
+ 'AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED')
+ if not tel_result_wrapper:
+ return tel_result_wrapper
+
+ if ad_hangup:
+ if not hangup_call(log, ad_hangup):
+ ad_hangup.log.info("Failed to hang up the call")
+ tel_result_wrapper.result_value = CallResult('CALL_HANGUP_FAIL')
+ return tel_result_wrapper
+ finally:
+ if not tel_result_wrapper:
+ for ad in (ad_caller, ad_callee):
+ last_call_drop_reason(ad, begin_time)
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.log.info("In call. End now.")
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+ if ad_hangup or not tel_result_wrapper:
+ for ad in (ad_caller, ad_callee):
+ if not wait_for_call_id_clearing(
+ ad, getattr(ad, "caller_ids", [])):
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_ID_CLEANUP_FAIL')
+ return tel_result_wrapper
+
+def call_setup_teardown_for_call_forwarding(
+ log,
+ ad_caller,
+ ad_callee,
+ forwarded_callee,
+ ad_hangup=None,
+ verify_callee_func=None,
+ verify_after_cf_disabled=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None,
+ call_forwarding_type="unconditional"):
+ """ Call process for call forwarding, including make a phone call from
+ caller, forward from callee, accept from the forwarded callee and hang up.
+ The call is on default voice subscription
+
+ In call process, call from <ad_caller> to <ad_callee>, forwarded to
+ <forwarded_callee>, accept the call, (optional) and then hang up from
+ <ad_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object which forwards the call.
+ forwarded_callee: Callee Android Device Object which answers the call.
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ verify_after_cf_disabled: If True the test of disabling call forwarding
+ will be appended.
+ wait_time_in_call: the call duration of a connected call
+ incall_ui_display: after answer the call, bring in-call UI to foreground
+ or background.
+ Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+ dialing_number_length: the number of digits used for dialing
+ video_state: video call or voice call. Default is voice call.
+ call_forwarding_type: type of call forwarding listed below:
+ - unconditional
+ - busy
+ - not_answered
+ - not_reachable
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ subid_caller = get_outgoing_voice_sub_id(ad_caller)
+ subid_callee = get_incoming_voice_sub_id(ad_callee)
+ subid_forwarded_callee = get_incoming_voice_sub_id(forwarded_callee)
+ return call_setup_teardown_for_call_forwarding_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ forwarded_callee,
+ subid_caller,
+ subid_callee,
+ subid_forwarded_callee,
+ ad_hangup,
+ verify_callee_func,
+ wait_time_in_call,
+ incall_ui_display,
+ dialing_number_length,
+ video_state,
+ call_forwarding_type,
+ verify_after_cf_disabled)
+
+def call_setup_teardown_for_call_forwarding_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ forwarded_callee,
+ subid_caller,
+ subid_callee,
+ subid_forwarded_callee,
+ ad_hangup=None,
+ verify_callee_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None,
+ call_forwarding_type="unconditional",
+ verify_after_cf_disabled=None):
+ """ Call process for call forwarding, including make a phone call from caller,
+ forward from callee, accept from the forwarded callee and hang up.
+ The call is on specified subscription
+
+ In call process, call from <ad_caller> to <ad_callee>, forwarded to
+ <forwarded_callee>, accept the call, (optional) and then hang up from
+ <ad_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object which forwards the call.
+ forwarded_callee: Callee Android Device Object which answers the call.
+ subid_caller: Caller subscription ID
+ subid_callee: Callee subscription ID
+ subid_forwarded_callee: Forwarded callee subscription ID
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ wait_time_in_call: the call duration of a connected call
+ incall_ui_display: after answer the call, bring in-call UI to foreground
+ or background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+ dialing_number_length: the number of digits used for dialing
+ video_state: video call or voice call. Default is voice call.
+ call_forwarding_type: type of call forwarding listed below:
+ - unconditional
+ - busy
+ - not_answered
+ - not_reachable
+ verify_after_cf_disabled: If True the call forwarding will not be
+ enabled. This argument is used to verify if the call can be received
+ successfully after call forwarding was disabled.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ CHECK_INTERVAL = 5
+ begin_time = get_current_epoch_time()
+ verify_caller_func = is_phone_in_call
+ if not verify_callee_func:
+ verify_callee_func = is_phone_in_call
+ verify_forwarded_callee_func = is_phone_in_call
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+ forwarded_callee_number = forwarded_callee.telephony['subscription'][
+ subid_forwarded_callee]['phone_num']
+
+ if dialing_number_length:
+ skip_test = False
+ trunc_position = 0 - int(dialing_number_length)
+ try:
+ caller_area_code = caller_number[:trunc_position]
+ callee_area_code = callee_number[:trunc_position]
+ callee_dial_number = callee_number[trunc_position:]
+ except:
+ skip_test = True
+ if caller_area_code != callee_area_code:
+ skip_test = True
+ if skip_test:
+ msg = "Cannot make call from %s to %s by %s digits" % (
+ caller_number, callee_number, dialing_number_length)
+ ad_caller.log.info(msg)
+ raise signals.TestSkip(msg)
+ else:
+ callee_number = callee_dial_number
+
+ result = True
+ msg = "Call from %s to %s (forwarded to %s)" % (
+ caller_number, callee_number, forwarded_callee_number)
+ if video_state:
+ msg = "Video %s" % msg
+ video = True
+ else:
+ video = False
+ if ad_hangup:
+ msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+ ad_caller.log.info(msg)
+
+ for ad in (ad_caller, forwarded_callee):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ if call_ids:
+ ad.log.info("Pre-exist CallId %s before making call", call_ids)
+
+ if not verify_after_cf_disabled:
+ if not set_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ forwarded_callee,
+ call_forwarding_type=call_forwarding_type):
+ raise signals.TestFailure(
+ "Failed to register or activate call forwarding.",
+ extras={"fail_reason": "Failed to register or activate call"
+ " forwarding."})
+
+ if call_forwarding_type == "not_reachable":
+ if not toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=True,
+ strict_checking=True):
+ return False
+
+ if call_forwarding_type == "busy":
+ ad_callee.log.info("Callee is making a phone call to 0000000000 to make"
+ " itself busy.")
+ ad_callee.droid.telecomCallNumber("0000000000", False)
+ time.sleep(2)
+
+ if check_call_state_idle_by_adb(ad_callee):
+ ad_callee.log.error("Call state of the callee is idle.")
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+
+ try:
+ if not initiate_call(
+ log,
+ ad_caller,
+ callee_number,
+ incall_ui_display=incall_ui_display,
+ video=video):
+
+ ad_caller.log.error("Caller failed to initiate the call.")
+ result = False
+
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+ else:
+ ad_caller.log.info("Caller initated the call successfully.")
+
+ if call_forwarding_type == "not_answered":
+ if not wait_for_ringing_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller_number,
+ caller=ad_caller,
+ event_tracking_started=True):
+ ad.log.info("Incoming call ringing check failed.")
+ return False
+
+ _timeout = 30
+ while check_call_state_ring_by_adb(ad_callee) == 1 and _timeout >= 0:
+ time.sleep(1)
+ _timeout = _timeout - 1
+
+ if not wait_and_answer_call_for_subscription(
+ log,
+ forwarded_callee,
+ subid_forwarded_callee,
+ incoming_number=caller_number,
+ caller=ad_caller,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+
+ if not verify_after_cf_disabled:
+ forwarded_callee.log.error("Forwarded callee failed to receive"
+ "or answer the call.")
+ result = False
+ else:
+ forwarded_callee.log.info("Forwarded callee did not receive or"
+ " answer the call.")
+
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+
+ else:
+ if not verify_after_cf_disabled:
+ forwarded_callee.log.info("Forwarded callee answered the call"
+ " successfully.")
+ else:
+ forwarded_callee.log.error("Forwarded callee should not be able"
+ " to answer the call.")
+ hangup_call(log, ad_caller)
+ result = False
+
+ for ad, subid, call_func in zip(
+ [ad_caller, forwarded_callee],
+ [subid_caller, subid_forwarded_callee],
+ [verify_caller_func, verify_forwarded_callee_func]):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_ids = set(call_ids) - set(ad.call_ids)
+ if not new_call_ids:
+ if not verify_after_cf_disabled:
+ ad.log.error(
+ "No new call ids are found after call establishment")
+ ad.log.error("telecomCallGetCallIds returns %s",
+ ad.droid.telecomCallGetCallIds())
+ result = False
+ for new_call_id in new_call_ids:
+ if not verify_after_cf_disabled:
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ result = False
+ else:
+ ad.log.info("callProperties = %s",
+ ad.droid.telecomCallGetProperties(new_call_id))
+ else:
+ ad.log.error("No new call id should be found.")
+
+ if not ad.droid.telecomCallGetAudioState():
+ if not verify_after_cf_disabled:
+ ad.log.error("Audio is not in call state")
+ result = False
+
+ if call_func(log, ad):
+ if not verify_after_cf_disabled:
+ ad.log.info("Call is in %s state", call_func.__name__)
+ else:
+ ad.log.error("Call is in %s state", call_func.__name__)
+ else:
+ if not verify_after_cf_disabled:
+ ad.log.error(
+ "Call is not in %s state, voice in RAT %s",
+ call_func.__name__,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+
+ if not result:
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+
+ elapsed_time = 0
+ while (elapsed_time < wait_time_in_call):
+ CHECK_INTERVAL = min(CHECK_INTERVAL,
+ wait_time_in_call - elapsed_time)
+ time.sleep(CHECK_INTERVAL)
+ elapsed_time += CHECK_INTERVAL
+ time_message = "at <%s>/<%s> second." % (elapsed_time,
+ wait_time_in_call)
+ for ad, subid, call_func in [
+ (ad_caller, subid_caller, verify_caller_func),
+ (forwarded_callee, subid_forwarded_callee,
+ verify_forwarded_callee_func)]:
+ if not call_func(log, ad):
+ if not verify_after_cf_disabled:
+ ad.log.error(
+ "NOT in correct %s state at %s, voice in RAT %s",
+ call_func.__name__, time_message,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+ else:
+ if not verify_after_cf_disabled:
+ ad.log.info("In correct %s state at %s",
+ call_func.__name__, time_message)
+ else:
+ ad.log.error("In correct %s state at %s",
+ call_func.__name__, time_message)
+
+ if not ad.droid.telecomCallGetAudioState():
+ if not verify_after_cf_disabled:
+ ad.log.error("Audio is not in call state at %s",
+ time_message)
+ result = False
+
+ if not result:
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+
+ if ad_hangup:
+ if not hangup_call(log, ad_hangup):
+ ad_hangup.log.info("Failed to hang up the call")
+ result = False
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+ return False
+ finally:
+ if not result:
+ if verify_after_cf_disabled:
+ result = True
+ else:
+ for ad in (ad_caller, forwarded_callee):
+ last_call_drop_reason(ad, begin_time)
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.log.info("In call. End now.")
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+ if ad_hangup or not result:
+ for ad in (ad_caller, forwarded_callee):
+ if not wait_for_call_id_clearing(
+ ad, getattr(ad, "caller_ids", [])):
+ result = False
+
+ if call_forwarding_type == "not_reachable":
+ if toggle_airplane_mode_msim(
+ log,
+ ad_callee,
+ new_state=False,
+ strict_checking=True):
+ time.sleep(10)
+ elif call_forwarding_type == "busy":
+ hangup_call(log, ad_callee)
+
+ if not verify_after_cf_disabled:
+ erase_call_forwarding_by_mmi(
+ log,
+ ad_callee,
+ call_forwarding_type=call_forwarding_type)
+
+ if not result:
+ return result
+
+ ad_caller.log.info(
+ "Make a normal call to callee to ensure the call can be connected after"
+ " call forwarding was disabled")
+ return call_setup_teardown_for_subscription(
+ log, ad_caller, ad_callee, subid_caller, subid_callee, ad_caller,
+ verify_caller_func, verify_callee_func, wait_time_in_call,
+ incall_ui_display, dialing_number_length, video_state)
+
+def call_setup_teardown_for_call_waiting(log,
+ ad_caller,
+ ad_callee,
+ ad_caller2,
+ ad_hangup=None,
+ ad_hangup2=None,
+ verify_callee_func=None,
+ end_first_call_before_answering_second_call=True,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None,
+ call_waiting=True):
+ """ Call process for call waiting, including make the 1st phone call from
+ caller, answer the call by the callee, and receive the 2nd call from the
+ caller2. The call is on default voice subscription
+
+ In call process, 1st call from <ad_caller> to <ad_callee>, 2nd call from
+ <ad_caller2> to <ad_callee>, hang up the existing call or reject the
+ incoming call according to the test scenario.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ ad_caller2: Caller2 Android Device Object.
+ ad_hangup: Android Device Object end the 1st phone call.
+ Optional. Default value is None, and phone call will continue.
+ ad_hangup2: Android Device Object end the 2nd phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ end_first_call_before_answering_second_call: If True the 2nd call will
+ be rejected on the ringing stage.
+ wait_time_in_call: the call duration of a connected call
+ incall_ui_display: after answer the call, bring in-call UI to foreground
+ or background.
+ Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+ dialing_number_length: the number of digits used for dialing
+ video_state: video call or voice call. Default is voice call.
+ call_waiting: True to enable call waiting and False to disable.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ subid_caller = get_outgoing_voice_sub_id(ad_caller)
+ subid_callee = get_incoming_voice_sub_id(ad_callee)
+ subid_caller2 = get_incoming_voice_sub_id(ad_caller2)
+ return call_setup_teardown_for_call_waiting_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ ad_caller2,
+ subid_caller,
+ subid_callee,
+ subid_caller2,
+ ad_hangup, ad_hangup2,
+ verify_callee_func,
+ end_first_call_before_answering_second_call,
+ wait_time_in_call,
+ incall_ui_display,
+ dialing_number_length,
+ video_state,
+ call_waiting)
+
+def call_setup_teardown_for_call_waiting_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ ad_caller2,
+ subid_caller,
+ subid_callee,
+ subid_caller2,
+ ad_hangup=None,
+ ad_hangup2=None,
+ verify_callee_func=None,
+ end_first_call_before_answering_second_call=True,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None,
+ call_waiting=True):
+ """ Call process for call waiting, including make the 1st phone call from
+ caller, answer the call by the callee, and receive the 2nd call from the
+ caller2. The call is on specified subscription.
+
+ In call process, 1st call from <ad_caller> to <ad_callee>, 2nd call from
+ <ad_caller2> to <ad_callee>, hang up the existing call or reject the
+ incoming call according to the test scenario.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ ad_caller2: Caller2 Android Device Object.
+ subid_caller: Caller subscription ID.
+ subid_callee: Callee subscription ID.
+ subid_caller2: Caller2 subscription ID.
+ ad_hangup: Android Device Object end the 1st phone call.
+ Optional. Default value is None, and phone call will continue.
+ ad_hangup2: Android Device Object end the 2nd phone call.
+ Optional. Default value is None, and phone call will continue.
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ end_first_call_before_answering_second_call: If True the 2nd call will
+ be rejected on the ringing stage.
+ wait_time_in_call: the call duration of a connected call
+ incall_ui_display: after answer the call, bring in-call UI to foreground
+ or background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+ dialing_number_length: the number of digits used for dialing
+ video_state: video call or voice call. Default is voice call.
+ call_waiting: True to enable call waiting and False to disable.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+
+ CHECK_INTERVAL = 5
+ begin_time = get_current_epoch_time()
+ verify_caller_func = is_phone_in_call
+ if not verify_callee_func:
+ verify_callee_func = is_phone_in_call
+ verify_caller2_func = is_phone_in_call
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+ caller2_number = ad_caller2.telephony['subscription'][subid_caller2][
+ 'phone_num']
+ if dialing_number_length:
+ skip_test = False
+ trunc_position = 0 - int(dialing_number_length)
+ try:
+ caller_area_code = caller_number[:trunc_position]
+ callee_area_code = callee_number[:trunc_position]
+ callee_dial_number = callee_number[trunc_position:]
+ except:
+ skip_test = True
+ if caller_area_code != callee_area_code:
+ skip_test = True
+ if skip_test:
+ msg = "Cannot make call from %s to %s by %s digits" % (
+ caller_number, callee_number, dialing_number_length)
+ ad_caller.log.info(msg)
+ raise signals.TestSkip(msg)
+ else:
+ callee_number = callee_dial_number
+
+ result = True
+ msg = "Call from %s to %s" % (caller_number, callee_number)
+ if video_state:
+ msg = "Video %s" % msg
+ video = True
+ else:
+ video = False
+ if ad_hangup:
+ msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+ ad_caller.log.info(msg)
+
+ for ad in (ad_caller, ad_callee, ad_caller2):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ if call_ids:
+ ad.log.info("Pre-exist CallId %s before making call", call_ids)
+
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=0)
+ else:
+ set_call_waiting(log, ad_callee, enable=1)
+
+ first_call_ids = []
+ try:
+ if not initiate_call(
+ log,
+ ad_caller,
+ callee_number,
+ incall_ui_display=incall_ui_display,
+ video=video):
+ ad_caller.log.error("Initiate call failed.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ else:
+ ad_caller.log.info("Caller initate call successfully")
+ if not wait_and_answer_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller_number,
+ caller=ad_caller,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+ ad_callee.log.error("Answer call fail.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ else:
+ ad_callee.log.info("Callee answered the call successfully")
+
+ for ad, subid, call_func in zip(
+ [ad_caller, ad_callee],
+ [subid_caller, subid_callee],
+ [verify_caller_func, verify_callee_func]):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_ids = set(call_ids) - set(ad.call_ids)
+ if not new_call_ids:
+ ad.log.error(
+ "No new call ids are found after call establishment")
+ ad.log.error("telecomCallGetCallIds returns %s",
+ ad.droid.telecomCallGetCallIds())
+ result = False
+ for new_call_id in new_call_ids:
+ first_call_ids.append(new_call_id)
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ result = False
+ else:
+ ad.log.info("callProperties = %s",
+ ad.droid.telecomCallGetProperties(new_call_id))
+
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state")
+ result = False
+
+ if call_func(log, ad):
+ ad.log.info("Call is in %s state", call_func.__name__)
+ else:
+ ad.log.error("Call is not in %s state, voice in RAT %s",
+ call_func.__name__,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+ if not result:
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ time.sleep(3)
+ if not call_waiting:
+ if not initiate_call(
+ log,
+ ad_caller2,
+ callee_number,
+ incall_ui_display=incall_ui_display,
+ video=video):
+ ad_caller2.log.info("Initiate call failed.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ else:
+ ad_caller2.log.info("Caller 2 initate 2nd call successfully")
+
+ if not wait_and_answer_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller2_number,
+ caller=ad_caller2,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+ ad_callee.log.info(
+ "Answering 2nd call fail due to call waiting deactivate.")
+ else:
+ ad_callee.log.error("Callee should not be able to answer the"
+ " 2nd call due to call waiting deactivated.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+
+ time.sleep(3)
+ if not hangup_call(log, ad_caller2):
+ ad_caller2.log.info("Failed to hang up the 2nd call")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+
+ else:
+
+ for ad in (ad_callee, ad_caller2):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ if call_ids:
+ ad.log.info("Current existing CallId %s before making the"
+ " second call.", call_ids)
+
+ if not initiate_call(
+ log,
+ ad_caller2,
+ callee_number,
+ incall_ui_display=incall_ui_display,
+ video=video):
+ ad_caller2.log.info("Initiate 2nd call failed.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ else:
+ ad_caller2.log.info("Caller 2 initate 2nd call successfully")
+
+ if end_first_call_before_answering_second_call:
+ try:
+ if not wait_for_ringing_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller2_number,
+ caller=ad_caller2,
+ event_tracking_started=True):
+ ad_callee.log.info(
+ "2nd incoming call ringing check failed.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ time.sleep(3)
+
+ ad_hangup.log.info("Disconnecting first call...")
+ for call_id in first_call_ids:
+ disconnect_call_by_id(log, ad_hangup, call_id)
+ time.sleep(3)
+
+ ad_callee.log.info("Answering the 2nd ring call...")
+ ad_callee.droid.telecomAcceptRingingCall(video_state)
+
+ if wait_for_call_offhook_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ event_tracking_started=True):
+ ad_callee.log.info(
+ "Callee answered the 2nd call successfully.")
+ else:
+ ad_callee.log.error("Could not answer the 2nd call.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+ except Exception as e:
+ log.error(e)
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ else:
+ if not wait_and_answer_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller2_number,
+ caller=ad_caller2,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+ ad_callee.log.error("Failed to answer 2nd call.")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ else:
+ ad_callee.log.info(
+ "Callee answered the 2nd call successfully.")
+
+ for ad, subid, call_func in zip(
+ [ad_callee, ad_caller2],
+ [subid_callee, subid_caller2],
+ [verify_callee_func, verify_caller2_func]):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_ids = set(call_ids) - set(ad.call_ids)
+ if not new_call_ids:
+ ad.log.error(
+ "No new call ids are found after 2nd call establishment")
+ ad.log.error("telecomCallGetCallIds returns %s",
+ ad.droid.telecomCallGetCallIds())
+ result = False
+ for new_call_id in new_call_ids:
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ result = False
+ else:
+ ad.log.info("callProperties = %s",
+ ad.droid.telecomCallGetProperties(new_call_id))
+
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in 2nd call state")
+ result = False
+
+ if call_func(log, ad):
+ ad.log.info("2nd call is in %s state", call_func.__name__)
+ else:
+ ad.log.error("2nd call is not in %s state, voice in RAT %s",
+ call_func.__name__,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+ if not result:
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ elapsed_time = 0
+ while (elapsed_time < wait_time_in_call):
+ CHECK_INTERVAL = min(CHECK_INTERVAL,
+ wait_time_in_call - elapsed_time)
+ time.sleep(CHECK_INTERVAL)
+ elapsed_time += CHECK_INTERVAL
+ time_message = "at <%s>/<%s> second." % (elapsed_time,
+ wait_time_in_call)
+
+ if not end_first_call_before_answering_second_call or \
+ not call_waiting:
+ for ad, subid, call_func in [
+ (ad_caller, subid_caller, verify_caller_func),
+ (ad_callee, subid_callee, verify_callee_func)]:
+ if not call_func(log, ad):
+ ad.log.error(
+ "The first call NOT in correct %s state at %s,"
+ " voice in RAT %s",
+ call_func.__name__, time_message,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+ else:
+ ad.log.info("The first call in correct %s state at %s",
+ call_func.__name__, time_message)
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error(
+ "The first call audio is not in call state at %s",
+ time_message)
+ result = False
+ if not result:
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ if call_waiting:
+ for ad, call_func in [(ad_caller2, verify_caller2_func),
+ (ad_callee, verify_callee_func)]:
+ if not call_func(log, ad):
+ ad.log.error(
+ "The 2nd call NOT in correct %s state at %s,"
+ " voice in RAT %s",
+ call_func.__name__, time_message,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(subid))
+ result = False
+ else:
+ ad.log.info("The 2nd call in correct %s state at %s",
+ call_func.__name__, time_message)
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error(
+ "The 2nd call audio is not in call state at %s",
+ time_message)
+ result = False
+ if not result:
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return False
+
+ if not end_first_call_before_answering_second_call or not call_waiting:
+ ad_hangup.log.info("Hanging up the first call...")
+ for call_id in first_call_ids:
+ disconnect_call_by_id(log, ad_hangup, call_id)
+ time.sleep(5)
+
+ if ad_hangup2 and call_waiting:
+ if not hangup_call(log, ad_hangup2):
+ ad_hangup2.log.info("Failed to hang up the 2nd call")
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ result = False
+ return False
+ finally:
+ if not result:
+ for ad in (ad_caller, ad_callee, ad_caller2):
+ last_call_drop_reason(ad, begin_time)
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.log.info("In call. End now.")
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+
+ if ad_hangup or not result:
+ for ad in (ad_caller, ad_callee):
+ if not wait_for_call_id_clearing(
+ ad, getattr(ad, "caller_ids", [])):
+ result = False
+
+ if call_waiting:
+ if ad_hangup2 or not result:
+ for ad in (ad_caller2, ad_callee):
+ if not wait_for_call_id_clearing(
+ ad, getattr(ad, "caller_ids", [])):
+ result = False
+ if not call_waiting:
+ set_call_waiting(log, ad_callee, enable=1)
+ return result
+
+def wait_for_call_id_clearing(ad,
+ previous_ids,
+ timeout=MAX_WAIT_TIME_CALL_DROP):
+ while timeout > 0:
+ new_call_ids = ad.droid.telecomCallGetCallIds()
+ if len(new_call_ids) <= len(previous_ids):
+ return True
+ time.sleep(5)
+ timeout = timeout - 5
+ ad.log.error("Call id clearing failed. Before: %s; After: %s",
+ previous_ids, new_call_ids)
+ return False
+
+
+def last_call_drop_reason(ad, begin_time=None):
+ reasons = ad.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause", begin_time)
+ reason_string = ""
+ if reasons:
+ log_msg = "Logcat call drop reasons:"
+ for reason in reasons:
+ log_msg = "%s\n\t%s" % (log_msg, reason["log_message"])
+ if "ril reason str" in reason["log_message"]:
+ reason_string = reason["log_message"].split(":")[-1].strip()
+ ad.log.info(log_msg)
+ reasons = ad.search_logcat("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION",
+ begin_time)
+ if reasons:
+ ad.log.warning("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION is seen")
+ ad.log.info("last call dumpsys: %s",
+ sorted(dumpsys_last_call_info(ad).items()))
+ return reason_string
+
+
+def phone_number_formatter(input_string, formatter=None):
+ """Get expected format of input phone number string.
+
+ Args:
+ input_string: (string) input phone number.
+ The input could be 10/11/12 digital, with or without " "/"-"/"."
+ formatter: (int) expected format, this could be 7/10/11/12
+ if formatter is 7: output string would be 7 digital number.
+ if formatter is 10: output string would be 10 digital (standard) number.
+ if formatter is 11: output string would be "1" + 10 digital number.
+ if formatter is 12: output string would be "+1" + 10 digital number.
+
+ Returns:
+ If no error happen, return phone number in expected format.
+ Else, return None.
+ """
+ if not input_string:
+ return ""
+ # make sure input_string is 10 digital
+ # Remove white spaces, dashes, dots
+ input_string = input_string.replace(" ", "").replace("-", "").replace(
+ ".", "").lstrip("0")
+ if not formatter:
+ return input_string
+ # Remove +81 and add 0 for Japan Carriers only.
+ if (len(input_string) == 13 and input_string[0:3] == "+81"):
+ input_string = "0" + input_string[3:]
+ return input_string
+ # Remove "1" or "+1"from front
+ if (len(input_string) == PHONE_NUMBER_STRING_FORMAT_11_DIGIT
+ and input_string[0] == "1"):
+ input_string = input_string[1:]
+ elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_12_DIGIT
+ and input_string[0:2] == "+1"):
+ input_string = input_string[2:]
+ elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_7_DIGIT
+ and formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT):
+ return input_string
+ elif len(input_string) != PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
+ return None
+ # change input_string according to format
+ if formatter == PHONE_NUMBER_STRING_FORMAT_12_DIGIT:
+ input_string = "+1" + input_string
+ elif formatter == PHONE_NUMBER_STRING_FORMAT_11_DIGIT:
+ input_string = "1" + input_string
+ elif formatter == PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
+ input_string = input_string
+ elif formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT:
+ input_string = input_string[3:]
+ else:
+ return None
+ return input_string
+
+
+def get_internet_connection_type(log, ad):
+ """Get current active connection type name.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ Returns:
+ current active connection type name.
+ """
+ if not ad.droid.connectivityNetworkIsConnected():
+ return 'none'
+ return connection_type_from_type_string(
+ ad.droid.connectivityNetworkGetActiveConnectionTypeName())
+
+
+def verify_http_connection(log,
+ ad,
+ url="https://www.google.com",
+ retry=5,
+ retry_interval=15,
+ expected_state=True):
+ """Make ping request and return status.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+ url: Optional. The ping request will be made to this URL.
+ Default Value is "http://www.google.com/".
+
+ """
+ if not getattr(ad, "data_droid", None):
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ else:
+ try:
+ if not ad.data_droid.is_live:
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ except Exception:
+ ad.log.info("Start new sl4a session for file download")
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ for i in range(0, retry + 1):
+ try:
+ http_response = ad.data_droid.httpPing(url)
+ except Exception as e:
+ ad.log.info("httpPing with %s", e)
+ http_response = None
+ if (expected_state and http_response) or (not expected_state
+ and not http_response):
+ ad.log.info("Http ping response for %s meet expected %s", url,
+ expected_state)
+ return True
+ if i < retry:
+ time.sleep(retry_interval)
+ ad.log.error("Http ping to %s is %s after %s second, expecting %s", url,
+ http_response, i * retry_interval, expected_state)
+ return False
+
+
+def _generate_file_directory_and_file_name(url, out_path):
+ file_name = url.split("/")[-1]
+ if not out_path:
+ file_directory = "/sdcard/Download/"
+ elif not out_path.endswith("/"):
+ file_directory, file_name = os.path.split(out_path)
+ else:
+ file_directory = out_path
+ return file_directory, file_name
+
+
+def _check_file_existance(ad, file_path, expected_file_size=None):
+ """Check file existance by file_path. If expected_file_size
+ is provided, then also check if the file meet the file size requirement.
+ """
+ out = None
+ try:
+ out = ad.adb.shell('stat -c "%%s" %s' % file_path)
+ except AdbError:
+ pass
+ # Handle some old version adb returns error message "No such" into std_out
+ if out and "No such" not in out:
+ if expected_file_size:
+ file_size = int(out)
+ if file_size >= expected_file_size:
+ ad.log.info("File %s of size %s exists", file_path, file_size)
+ return True
+ else:
+ ad.log.info("File %s is of size %s, does not meet expected %s",
+ file_path, file_size, expected_file_size)
+ return False
+ else:
+ ad.log.info("File %s exists", file_path)
+ return True
+ else:
+ ad.log.info("File %s does not exist.", file_path)
+ return False
+
+
+def check_curl_availability(ad):
+ if not hasattr(ad, "curl_capable"):
+ try:
+ out = ad.adb.shell("/data/curl --version")
+ if not out or "not found" in out:
+ setattr(ad, "curl_capable", False)
+ ad.log.info("curl is unavailable, use chrome to download file")
+ else:
+ setattr(ad, "curl_capable", True)
+ except Exception:
+ setattr(ad, "curl_capable", False)
+ ad.log.info("curl is unavailable, use chrome to download file")
+ return ad.curl_capable
+
+
+def start_youtube_video(ad, url="https://www.youtube.com/watch?v=pSJoP0LR8CQ"):
+ ad.log.info("Open an youtube video")
+ for _ in range(3):
+ ad.ensure_screen_on()
+ ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+ if wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
+ ad.log.info("Started a video in youtube, audio is in MUSIC state")
+ return True
+ ad.log.info("Audio is not in MUSIC state. Quit Youtube.")
+ for _ in range(3):
+ ad.send_keycode("BACK")
+ time.sleep(1)
+ time.sleep(3)
+ return False
+
+
+def active_file_download_task(log, ad, file_name="5MB", method="curl"):
+ # files available for download on the same website:
+ # 1GB.zip, 512MB.zip, 200MB.zip, 50MB.zip, 20MB.zip, 10MB.zip, 5MB.zip
+ # download file by adb command, as phone call will use sl4a
+ file_size_map = {
+ '1MB': 1000000,
+ '5MB': 5000000,
+ '10MB': 10000000,
+ '20MB': 20000000,
+ '50MB': 50000000,
+ '100MB': 100000000,
+ '200MB': 200000000,
+ '512MB': 512000000
+ }
+ url_map = {
+ "1MB": [
+ "http://146.148.91.8/download/1MB.zip",
+ "http://ipv4.download.thinkbroadband.com/1MB.zip"
+ ],
+ "5MB": [
+ "http://146.148.91.8/download/5MB.zip",
+ "http://212.183.159.230/5MB.zip",
+ "http://ipv4.download.thinkbroadband.com/5MB.zip"
+ ],
+ "10MB": [
+ "http://146.148.91.8/download/10MB.zip",
+ "http://212.183.159.230/10MB.zip",
+ "http://ipv4.download.thinkbroadband.com/10MB.zip",
+ "http://lax.futurehosting.com/test.zip",
+ "http://ovh.net/files/10Mio.dat"
+ ],
+ "20MB": [
+ "http://146.148.91.8/download/20MB.zip",
+ "http://212.183.159.230/20MB.zip",
+ "http://ipv4.download.thinkbroadband.com/20MB.zip"
+ ],
+ "50MB": [
+ "http://146.148.91.8/download/50MB.zip",
+ "http://212.183.159.230/50MB.zip",
+ "http://ipv4.download.thinkbroadband.com/50MB.zip"
+ ],
+ "100MB": [
+ "http://146.148.91.8/download/100MB.zip",
+ "http://212.183.159.230/100MB.zip",
+ "http://ipv4.download.thinkbroadband.com/100MB.zip",
+ "http://speedtest-ca.turnkeyinternet.net/100mb.bin",
+ "http://ovh.net/files/100Mio.dat",
+ "http://lax.futurehosting.com/test100.zip"
+ ],
+ "200MB": [
+ "http://146.148.91.8/download/200MB.zip",
+ "http://212.183.159.230/200MB.zip",
+ "http://ipv4.download.thinkbroadband.com/200MB.zip"
+ ],
+ "512MB": [
+ "http://146.148.91.8/download/512MB.zip",
+ "http://212.183.159.230/512MB.zip",
+ "http://ipv4.download.thinkbroadband.com/512MB.zip"
+ ]
+ }
+
+ file_size = file_size_map.get(file_name)
+ file_urls = url_map.get(file_name)
+ file_url = None
+ for url in file_urls:
+ url_splits = url.split("/")
+ if verify_http_connection(log, ad, url=url, retry=1):
+ output_path = "/sdcard/Download/%s" % url_splits[-1]
+ file_url = url
+ break
+ if not file_url:
+ ad.log.error("No url is available to download %s", file_name)
+ return False
+ timeout = min(max(file_size / 100000, 600), 3600)
+ if method == "sl4a":
+ return (http_file_download_by_sl4a, (ad, file_url, output_path,
+ file_size, True, timeout))
+ if method == "curl" and check_curl_availability(ad):
+ return (http_file_download_by_curl, (ad, file_url, output_path,
+ file_size, True, timeout))
+ elif method == "sl4a" or method == "curl":
+ return (http_file_download_by_sl4a, (ad, file_url, output_path,
+ file_size, True, timeout))
+ else:
+ return (http_file_download_by_chrome, (ad, file_url, file_size, True,
+ timeout))
+
+
+def active_file_download_test(log, ad, file_name="5MB", method="sl4a"):
+ task = active_file_download_task(log, ad, file_name, method=method)
+ if not task:
+ return False
+ return task[0](*task[1])
+
+
+def verify_internet_connection_by_ping(log,
+ ad,
+ retries=1,
+ expected_state=True,
+ timeout=60):
+ """Verify internet connection by ping test.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+
+ """
+ begin_time = get_current_epoch_time()
+ ip_addr = "54.230.144.105"
+ for dest in ("www.google.com", "www.amazon.com", ip_addr):
+ for i in range(retries):
+ ad.log.info("Ping %s - attempt %d", dest, i + 1)
+ result = adb_shell_ping(
+ ad, count=5, timeout=timeout, loss_tolerance=40, dest_ip=dest)
+ if result == expected_state:
+ ad.log.info(
+ "Internet connection by pinging to %s is %s as expected",
+ dest, expected_state)
+ if dest == ip_addr:
+ ad.log.warning("Suspect dns failure")
+ ad.log.info("DNS config: %s",
+ ad.adb.shell("getprop | grep dns").replace(
+ "\n", " "))
+ return False
+ return True
+ else:
+ ad.log.warning(
+ "Internet connection test by pinging %s is %s, expecting %s",
+ dest, result, expected_state)
+ if get_current_epoch_time() - begin_time < timeout * 1000:
+ time.sleep(5)
+ ad.log.error("Ping test doesn't meet expected %s", expected_state)
+ return False
+
+
+def verify_internet_connection(log, ad, retries=3, expected_state=True):
+ """Verify internet connection by ping test and http connection.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+
+ """
+ if ad.droid.connectivityNetworkIsConnected() != expected_state:
+ ad.log.info("NetworkIsConnected = %s, expecting %s",
+ not expected_state, expected_state)
+ if verify_internet_connection_by_ping(
+ log, ad, retries=retries, expected_state=expected_state):
+ return True
+ for url in ("https://www.google.com", "https://www.amazon.com"):
+ if verify_http_connection(
+ log, ad, url=url, retry=retries,
+ expected_state=expected_state):
+ return True
+ ad.log.info("DNS config: %s", " ".join(
+ ad.adb.shell("getprop | grep dns").split()))
+ ad.log.info("Interface info:\n%s", ad.adb.shell("ifconfig"))
+ ad.log.info("NetworkAgentInfo: %s",
+ ad.adb.shell("dumpsys connectivity | grep NetworkAgentInfo"))
+ return False
+
+
+def iperf_test_with_options(log,
+ ad,
+ iperf_server,
+ iperf_option,
+ timeout=180,
+ rate_dict=None,
+ blocking=True,
+ log_file_path=None):
+ """Iperf adb run helper.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+ iperf_server: The iperf host url".
+ iperf_option: The options to pass to iperf client
+ timeout: timeout for file download to complete.
+ rate_dict: dictionary that can be passed in to save data
+ blocking: run iperf in blocking mode if True
+ log_file_path: location to save logs
+ Returns:
+ True if IPerf runs without throwing an exception
+ """
+ try:
+ if log_file_path:
+ ad.adb.shell("rm %s" % log_file_path, ignore_status=True)
+ ad.log.info("Running adb iperf test with server %s", iperf_server)
+ ad.log.info("IPerf options are %s", iperf_option)
+ if not blocking:
+ ad.run_iperf_client_nb(
+ iperf_server,
+ iperf_option,
+ timeout=timeout + 60,
+ log_file_path=log_file_path)
+ return True
+ result, data = ad.run_iperf_client(
+ iperf_server, iperf_option, timeout=timeout + 60)
+ ad.log.info("IPerf test result with server %s is %s", iperf_server,
+ result)
+ if result:
+ iperf_str = ''.join(data)
+ iperf_result = ipf.IPerfResult(iperf_str)
+ if "-u" in iperf_option:
+ udp_rate = iperf_result.avg_rate
+ if udp_rate is None:
+ ad.log.warning(
+ "UDP rate is none, IPerf server returned error: %s",
+ iperf_result.error)
+ ad.log.info("IPerf3 udp speed is %sbps", udp_rate)
+ else:
+ tx_rate = iperf_result.avg_send_rate
+ rx_rate = iperf_result.avg_receive_rate
+ if (tx_rate or rx_rate) is None:
+ ad.log.warning(
+ "A TCP rate is none, IPerf server returned error: %s",
+ iperf_result.error)
+ ad.log.info(
+ "IPerf3 upload speed is %sbps, download speed is %sbps",
+ tx_rate, rx_rate)
+ if rate_dict is not None:
+ rate_dict["Uplink"] = tx_rate
+ rate_dict["Downlink"] = rx_rate
+ return result
+ except AdbError as e:
+ ad.log.warning("Fail to run iperf test with exception %s", e)
+ raise
+
+
+def iperf_udp_test_by_adb(log,
+ ad,
+ iperf_server,
+ port_num=None,
+ reverse=False,
+ timeout=180,
+ limit_rate=None,
+ omit=10,
+ ipv6=False,
+ rate_dict=None,
+ blocking=True,
+ log_file_path=None):
+ """Iperf test by adb using UDP.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+ iperf_Server: The iperf host url".
+ port_num: TCP/UDP server port
+ reverse: whether to test download instead of upload
+ timeout: timeout for file download to complete.
+ limit_rate: iperf bandwidth option. None by default
+ omit: the omit option provided in iperf command.
+ ipv6: whether to run the test as ipv6
+ rate_dict: dictionary that can be passed in to save data
+ blocking: run iperf in blocking mode if True
+ log_file_path: location to save logs
+ """
+ iperf_option = "-u -i 1 -t %s -O %s -J" % (timeout, omit)
+ if limit_rate:
+ iperf_option += " -b %s" % limit_rate
+ if port_num:
+ iperf_option += " -p %s" % port_num
+ if ipv6:
+ iperf_option += " -6"
+ if reverse:
+ iperf_option += " -R"
+ try:
+ return iperf_test_with_options(log,
+ ad,
+ iperf_server,
+ iperf_option,
+ timeout,
+ rate_dict,
+ blocking,
+ log_file_path)
+ except AdbError:
+ return False
+
+def iperf_test_by_adb(log,
+ ad,
+ iperf_server,
+ port_num=None,
+ reverse=False,
+ timeout=180,
+ limit_rate=None,
+ omit=10,
+ ipv6=False,
+ rate_dict=None,
+ blocking=True,
+ log_file_path=None):
+ """Iperf test by adb using TCP.
+
+ Args:
+ log: log object
+ ad: Android Device Object.
+ iperf_server: The iperf host url".
+ port_num: TCP/UDP server port
+ reverse: whether to test download instead of upload
+ timeout: timeout for file download to complete.
+ limit_rate: iperf bandwidth option. None by default
+ omit: the omit option provided in iperf command.
+ ipv6: whether to run the test as ipv6
+ rate_dict: dictionary that can be passed in to save data
+ blocking: run iperf in blocking mode if True
+ log_file_path: location to save logs
+ """
+ iperf_option = "-t %s -O %s -J" % (timeout, omit)
+ if limit_rate:
+ iperf_option += " -b %s" % limit_rate
+ if port_num:
+ iperf_option += " -p %s" % port_num
+ if ipv6:
+ iperf_option += " -6"
+ if reverse:
+ iperf_option += " -R"
+ try:
+ return iperf_test_with_options(log,
+ ad,
+ iperf_server,
+ iperf_option,
+ timeout,
+ rate_dict,
+ blocking,
+ log_file_path)
+ except AdbError:
+ return False
+
+
+def http_file_download_by_curl(ad,
+ url,
+ out_path=None,
+ expected_file_size=None,
+ remove_file_after_check=True,
+ timeout=3600,
+ limit_rate=None,
+ retry=3):
+ """Download http file by adb curl.
+
+ Args:
+ ad: Android Device Object.
+ url: The url that file to be downloaded from".
+ out_path: Optional. Where to download file to.
+ out_path is /sdcard/Download/ by default.
+ expected_file_size: Optional. Provided if checking the download file meet
+ expected file size in unit of byte.
+ remove_file_after_check: Whether to remove the downloaded file after
+ check.
+ timeout: timeout for file download to complete.
+ limit_rate: download rate in bps. None, if do not apply rate limit.
+ retry: the retry request times provided in curl command.
+ """
+ file_directory, file_name = _generate_file_directory_and_file_name(
+ url, out_path)
+ file_path = os.path.join(file_directory, file_name)
+ curl_cmd = "/data/curl"
+ if limit_rate:
+ curl_cmd += " --limit-rate %s" % limit_rate
+ if retry:
+ curl_cmd += " --retry %s" % retry
+ curl_cmd += " --url %s > %s" % (url, file_path)
+ try:
+ ad.log.info("Download %s to %s by adb shell command %s", url,
+ file_path, curl_cmd)
+
+ ad.adb.shell(curl_cmd, timeout=timeout)
+ if _check_file_existance(ad, file_path, expected_file_size):
+ ad.log.info("%s is downloaded to %s successfully", url, file_path)
+ return True
+ else:
+ ad.log.warning("Fail to download %s", url)
+ return False
+ except Exception as e:
+ ad.log.warning("Download %s failed with exception %s", url, e)
+ for cmd in ("ls -lh /data/local/tmp/tcpdump/",
+ "ls -lh /sdcard/Download/",
+ "ls -lh /data/vendor/radio/diag_logs/logs/",
+ "df -h",
+ "du -d 4 -h /data"):
+ out = ad.adb.shell(cmd)
+ ad.log.debug("%s", out)
+ return False
+ finally:
+ if remove_file_after_check:
+ ad.log.info("Remove the downloaded file %s", file_path)
+ ad.adb.shell("rm %s" % file_path, ignore_status=True)
+
+
+def open_url_by_adb(ad, url):
+ ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+
+
+def http_file_download_by_chrome(ad,
+ url,
+ expected_file_size=None,
+ remove_file_after_check=True,
+ timeout=3600):
+ """Download http file by chrome.
+
+ Args:
+ ad: Android Device Object.
+ url: The url that file to be downloaded from".
+ expected_file_size: Optional. Provided if checking the download file meet
+ expected file size in unit of byte.
+ remove_file_after_check: Whether to remove the downloaded file after
+ check.
+ timeout: timeout for file download to complete.
+ """
+ chrome_apk = "com.android.chrome"
+ file_directory, file_name = _generate_file_directory_and_file_name(
+ url, "/sdcard/Download/")
+ file_path = os.path.join(file_directory, file_name)
+ # Remove pre-existing file
+ ad.force_stop_apk(chrome_apk)
+ file_to_be_delete = os.path.join(file_directory, "*%s*" % file_name)
+ ad.adb.shell("rm -f %s" % file_to_be_delete)
+ ad.adb.shell("rm -rf /sdcard/Download/.*")
+ ad.adb.shell("rm -f /sdcard/Download/.*")
+ data_accounting = {
+ "total_rx_bytes": ad.droid.getTotalRxBytes(),
+ "mobile_rx_bytes": ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage": get_mobile_data_usage(ad, None, None),
+ "chrome_mobile_data_usage": get_mobile_data_usage(
+ ad, None, chrome_apk)
+ }
+ ad.log.debug("Before downloading: %s", data_accounting)
+ ad.log.info("Download %s with timeout %s", url, timeout)
+ ad.ensure_screen_on()
+ open_url_by_adb(ad, url)
+ elapse_time = 0
+ result = True
+ while elapse_time < timeout:
+ time.sleep(30)
+ if _check_file_existance(ad, file_path, expected_file_size):
+ ad.log.info("%s is downloaded successfully", url)
+ if remove_file_after_check:
+ ad.log.info("Remove the downloaded file %s", file_path)
+ ad.adb.shell("rm -f %s" % file_to_be_delete)
+ ad.adb.shell("rm -rf /sdcard/Download/.*")
+ ad.adb.shell("rm -f /sdcard/Download/.*")
+ #time.sleep(30)
+ new_data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "chrome_mobile_data_usage":
+ get_mobile_data_usage(ad, None, chrome_apk)
+ }
+ ad.log.info("After downloading: %s", new_data_accounting)
+ accounting_diff = {
+ key: value - data_accounting[key]
+ for key, value in new_data_accounting.items()
+ }
+ ad.log.debug("Data accounting difference: %s", accounting_diff)
+ if getattr(ad, "on_mobile_data", False):
+ for key, value in accounting_diff.items():
+ if value < expected_file_size:
+ ad.log.warning("%s diff is %s less than %s", key,
+ value, expected_file_size)
+ ad.data_accounting["%s_failure" % key] += 1
+ else:
+ for key, value in accounting_diff.items():
+ if value >= expected_file_size:
+ ad.log.error("%s diff is %s. File download is "
+ "consuming mobile data", key, value)
+ result = False
+ return result
+ elif _check_file_existance(ad, "%s.crdownload" % file_path):
+ ad.log.info("Chrome is downloading %s", url)
+ elif elapse_time < 60:
+ # download not started, retry download wit chrome again
+ open_url_by_adb(ad, url)
+ else:
+ ad.log.error("Unable to download file from %s", url)
+ break
+ elapse_time += 30
+ ad.log.warning("Fail to download file from %s", url)
+ ad.force_stop_apk("com.android.chrome")
+ ad.adb.shell("rm -f %s" % file_to_be_delete)
+ ad.adb.shell("rm -rf /sdcard/Download/.*")
+ ad.adb.shell("rm -f /sdcard/Download/.*")
+ return False
+
+
+def http_file_download_by_sl4a(ad,
+ url,
+ out_path=None,
+ expected_file_size=None,
+ remove_file_after_check=True,
+ timeout=300):
+ """Download http file by sl4a RPC call.
+
+ Args:
+ ad: Android Device Object.
+ url: The url that file to be downloaded from".
+ out_path: Optional. Where to download file to.
+ out_path is /sdcard/Download/ by default.
+ expected_file_size: Optional. Provided if checking the download file meet
+ expected file size in unit of byte.
+ remove_file_after_check: Whether to remove the downloaded file after
+ check.
+ timeout: timeout for file download to complete.
+ """
+ file_folder, file_name = _generate_file_directory_and_file_name(
+ url, out_path)
+ file_path = os.path.join(file_folder, file_name)
+ ad.adb.shell("rm -f %s" % file_path)
+ accounting_apk = SL4A_APK_NAME
+ result = True
+ try:
+ if not getattr(ad, "data_droid", None):
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ else:
+ try:
+ if not ad.data_droid.is_live:
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ except Exception:
+ ad.log.info("Start new sl4a session for file download")
+ ad.data_droid, ad.data_ed = ad.get_droid()
+ ad.data_ed.start()
+ data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "sl4a_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.debug("Before downloading: %s", data_accounting)
+ ad.log.info("Download file from %s to %s by sl4a RPC call", url,
+ file_path)
+ try:
+ ad.data_droid.httpDownloadFile(url, file_path, timeout=timeout)
+ except Exception as e:
+ ad.log.warning("SL4A file download error: %s", e)
+ for cmd in ("ls -lh /data/local/tmp/tcpdump/",
+ "ls -lh /sdcard/Download/",
+ "ls -lh /data/vendor/radio/diag_logs/logs/",
+ "df -h",
+ "du -d 4 -h /data"):
+ out = ad.adb.shell(cmd)
+ ad.log.debug("%s", out)
+ ad.data_droid.terminate()
+ return False
+ if _check_file_existance(ad, file_path, expected_file_size):
+ ad.log.info("%s is downloaded successfully", url)
+ new_data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "sl4a_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.debug("After downloading: %s", new_data_accounting)
+ accounting_diff = {
+ key: value - data_accounting[key]
+ for key, value in new_data_accounting.items()
+ }
+ ad.log.debug("Data accounting difference: %s", accounting_diff)
+ if getattr(ad, "on_mobile_data", False):
+ for key, value in accounting_diff.items():
+ if value < expected_file_size:
+ ad.log.debug("%s diff is %s less than %s", key,
+ value, expected_file_size)
+ ad.data_accounting["%s_failure"] += 1
+ else:
+ for key, value in accounting_diff.items():
+ if value >= expected_file_size:
+ ad.log.error("%s diff is %s. File download is "
+ "consuming mobile data", key, value)
+ result = False
+ return result
+ else:
+ ad.log.warning("Fail to download %s", url)
+ return False
+ except Exception as e:
+ ad.log.error("Download %s failed with exception %s", url, e)
+ raise
+ finally:
+ if remove_file_after_check:
+ ad.log.info("Remove the downloaded file %s", file_path)
+ ad.adb.shell("rm %s" % file_path, ignore_status=True)
+
+
+def get_wifi_usage(ad, sid=None, apk=None):
+ if not sid:
+ sid = ad.droid.subscriptionGetDefaultDataSubId()
+ current_time = int(time.time() * 1000)
+ begin_time = current_time - 10 * 24 * 60 * 60 * 1000
+ end_time = current_time + 10 * 24 * 60 * 60 * 1000
+
+ if apk:
+ uid = ad.get_apk_uid(apk)
+ ad.log.debug("apk %s uid = %s", apk, uid)
+ try:
+ return ad.droid.connectivityQueryDetailsForUid(
+ TYPE_WIFI,
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time, uid)
+ except:
+ return ad.droid.connectivityQueryDetailsForUid(
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time, uid)
+ else:
+ try:
+ return ad.droid.connectivityQuerySummaryForDevice(
+ TYPE_WIFI,
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time)
+ except:
+ return ad.droid.connectivityQuerySummaryForDevice(
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time)
+
+
+def get_mobile_data_usage(ad, sid=None, apk=None):
+ if not sid:
+ sid = ad.droid.subscriptionGetDefaultDataSubId()
+ current_time = int(time.time() * 1000)
+ begin_time = current_time - 10 * 24 * 60 * 60 * 1000
+ end_time = current_time + 10 * 24 * 60 * 60 * 1000
+
+ if apk:
+ uid = ad.get_apk_uid(apk)
+ ad.log.debug("apk %s uid = %s", apk, uid)
+ try:
+ usage_info = ad.droid.getMobileDataUsageInfoForUid(uid, sid)
+ ad.log.debug("Mobile data usage info for uid %s = %s", uid,
+ usage_info)
+ return usage_info["UsageLevel"]
+ except:
+ try:
+ return ad.droid.connectivityQueryDetailsForUid(
+ TYPE_MOBILE,
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time, uid)
+ except:
+ return ad.droid.connectivityQueryDetailsForUid(
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time, uid)
+ else:
+ try:
+ usage_info = ad.droid.getMobileDataUsageInfo(sid)
+ ad.log.debug("Mobile data usage info = %s", usage_info)
+ return usage_info["UsageLevel"]
+ except:
+ try:
+ return ad.droid.connectivityQuerySummaryForDevice(
+ TYPE_MOBILE,
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time)
+ except:
+ return ad.droid.connectivityQuerySummaryForDevice(
+ ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+ begin_time, end_time)
+
+
+def set_mobile_data_usage_limit(ad, limit, subscriber_id=None):
+ if not subscriber_id:
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ ad.log.debug("Set subscriber mobile data usage limit to %s", limit)
+ ad.droid.logV("Setting subscriber mobile data usage limit to %s" % limit)
+ try:
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, str(limit))
+ except:
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, limit)
+
+
+def remove_mobile_data_usage_limit(ad, subscriber_id=None):
+ if not subscriber_id:
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ ad.log.debug("Remove subscriber mobile data usage limit")
+ ad.droid.logV(
+ "Setting subscriber mobile data usage limit to -1, unlimited")
+ try:
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, "-1")
+ except:
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, -1)
+
+
+def trigger_modem_crash(ad, timeout=120):
+ cmd = "echo restart > /sys/kernel/debug/msm_subsys/modem"
+ ad.log.info("Triggering Modem Crash from kernel using adb command %s", cmd)
+ ad.adb.shell(cmd)
+ time.sleep(timeout)
+ return True
+
+
+def trigger_modem_crash_by_modem(ad, timeout=120):
+ begin_time = get_device_epoch_time(ad)
+ ad.adb.shell(
+ "setprop persist.vendor.sys.modem.diag.mdlog false",
+ ignore_status=True)
+ # Legacy pixels use persist.sys.modem.diag.mdlog.
+ ad.adb.shell(
+ "setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
+ disable_qxdm_logger(ad)
+ cmd = ('am instrument -w -e request "4b 25 03 00" '
+ '"com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemCommandInstrumentation"')
+ ad.log.info("Crash modem by %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout) # sleep time for sl4a stability
+ reasons = ad.search_logcat("modem subsystem failure reason", begin_time)
+ if reasons:
+ ad.log.info("Modem crash is triggered successfully")
+ ad.log.info(reasons[-1]["log_message"])
+ return True
+ else:
+ ad.log.warning("There is no modem subsystem failure reason logcat")
+ return False
+
+
+def phone_switch_to_msim_mode(ad, retries=3, timeout=60):
+ result = False
+ if not ad.is_apk_installed("com.google.mdstest"):
+ raise signals.TestAbortClass("mdstest is not installed")
+ mode = ad.droid.telephonyGetPhoneCount()
+ if mode == 2:
+ ad.log.info("Device already in MSIM mode")
+ return True
+ for i in range(retries):
+ ad.adb.shell(
+ "setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
+ ad.adb.shell(
+ "setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
+ disable_qxdm_logger(ad)
+ cmd = ('am instrument -w -e request "WriteEFS" -e item '
+ '"/google/pixel_multisim_config" -e data "02 00 00 00" '
+ '"com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemConfigInstrumentation"')
+ ad.log.info("Switch to MSIM mode by using %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout)
+ ad.adb.shell("setprop persist.radio.multisim.config dsds")
+ reboot_device(ad)
+ # Verify if device is really in msim mode
+ mode = ad.droid.telephonyGetPhoneCount()
+ if mode == 2:
+ ad.log.info("Device correctly switched to MSIM mode")
+ result = True
+ if "Sprint" in ad.adb.getprop("gsm.sim.operator.alpha"):
+ cmd = ('am instrument -w -e request "WriteEFS" -e item '
+ '"/google/pixel_dsds_imei_mapping_slot_record" -e data "03"'
+ ' "com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemConfigInstrumentation"')
+ ad.log.info("Switch Sprint to IMEI1 slot using %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout)
+ reboot_device(ad)
+ break
+ else:
+ ad.log.warning("Attempt %d - failed to switch to MSIM", (i + 1))
+ return result
+
+
+def phone_switch_to_ssim_mode(ad, retries=3, timeout=30):
+ result = False
+ if not ad.is_apk_installed("com.google.mdstest"):
+ raise signals.TestAbortClass("mdstest is not installed")
+ mode = ad.droid.telephonyGetPhoneCount()
+ if mode == 1:
+ ad.log.info("Device already in SSIM mode")
+ return True
+ for i in range(retries):
+ ad.adb.shell(
+ "setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
+ ad.adb.shell(
+ "setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
+ disable_qxdm_logger(ad)
+ cmds = ('am instrument -w -e request "WriteEFS" -e item '
+ '"/google/pixel_multisim_config" -e data "01 00 00 00" '
+ '"com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemConfigInstrumentation"',
+ 'am instrument -w -e request "WriteEFS" -e item "/nv/item_files'
+ '/modem/uim/uimdrv/uim_extended_slot_mapping_config" -e data '
+ '"00 01 02 01" "com.google.mdstest/com.google.mdstest.'
+ 'instrument.ModemConfigInstrumentation"')
+ for cmd in cmds:
+ ad.log.info("Switch to SSIM mode by using %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout)
+ ad.adb.shell("setprop persist.radio.multisim.config ssss")
+ reboot_device(ad)
+ # Verify if device is really in ssim mode
+ mode = ad.droid.telephonyGetPhoneCount()
+ if mode == 1:
+ ad.log.info("Device correctly switched to SSIM mode")
+ result = True
+ break
+ else:
+ ad.log.warning("Attempt %d - failed to switch to SSIM", (i + 1))
+ return result
+
+
+def lock_lte_band_by_mds(ad, band):
+ disable_qxdm_logger(ad)
+ ad.log.info("Write band %s locking to efs file", band)
+ if band == "4":
+ item_string = (
+ "4B 13 26 00 08 00 00 00 40 00 08 00 0B 00 08 00 00 00 00 00 00 00 "
+ "2F 6E 76 2F 69 74 65 6D 5F 66 69 6C 65 73 2F 6D 6F 64 65 6D 2F 6D "
+ "6D 6F 64 65 2F 6C 74 65 5F 62 61 6E 64 70 72 65 66 00")
+ elif band == "13":
+ item_string = (
+ "4B 13 26 00 08 00 00 00 40 00 08 00 0A 00 00 10 00 00 00 00 00 00 "
+ "2F 6E 76 2F 69 74 65 6D 5F 66 69 6C 65 73 2F 6D 6F 64 65 6D 2F 6D "
+ "6D 6F 64 65 2F 6C 74 65 5F 62 61 6E 64 70 72 65 66 00")
+ else:
+ ad.log.error("Band %s is not supported", band)
+ return False
+ cmd = ('am instrument -w -e request "%s" com.google.mdstest/com.google.'
+ 'mdstest.instrument.ModemCommandInstrumentation')
+ for _ in range(3):
+ if "SUCCESS" in ad.adb.shell(cmd % item_string, ignore_status=True):
+ break
+ else:
+ ad.log.error("Fail to write band by %s" % (cmd % item_string))
+ return False
+
+ # EFS Sync
+ item_string = "4B 13 30 00 2A 00 2F 00"
+
+ for _ in range(3):
+ if "SUCCESS" in ad.adb.shell(cmd % item_string, ignore_status=True):
+ break
+ else:
+ ad.log.error("Fail to sync efs by %s" % (cmd % item_string))
+ return False
+ time.sleep(5)
+ reboot_device(ad)
+
+
+def _connection_state_change(_event, target_state, connection_type):
+ if connection_type:
+ if 'TypeName' not in _event['data']:
+ return False
+ connection_type_string_in_event = _event['data']['TypeName']
+ cur_type = connection_type_from_type_string(
+ connection_type_string_in_event)
+ if cur_type != connection_type:
+ log.info(
+ "_connection_state_change expect: %s, received: %s <type %s>",
+ connection_type, connection_type_string_in_event, cur_type)
+ return False
+
+ if 'isConnected' in _event['data'] and _event['data']['isConnected'] == target_state:
+ return True
+ return False
+
+
+def wait_for_cell_data_connection(
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+ """Wait for data connection status to be expected value for default
+ data subscription.
+
+ Wait for the data connection status to be DATA_STATE_CONNECTED
+ or DATA_STATE_DISCONNECTED.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ state: Expected status: True or False.
+ If True, it will wait for status to be DATA_STATE_CONNECTED.
+ If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+ timeout_value: wait for cell data timeout value.
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ sub_id = get_default_data_sub_id(ad)
+ return wait_for_cell_data_connection_for_subscription(
+ log, ad, sub_id, state, timeout_value)
+
+
+def _is_data_connection_state_match(log, ad, expected_data_connection_state):
+ return (expected_data_connection_state ==
+ ad.droid.telephonyGetDataConnectionState())
+
+
+def _is_network_connected_state_match(log, ad,
+ expected_network_connected_state):
+ return (expected_network_connected_state ==
+ ad.droid.connectivityNetworkIsConnected())
+
+
+def wait_for_cell_data_connection_for_subscription(
+ log,
+ ad,
+ sub_id,
+ state,
+ timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+ """Wait for data connection status to be expected value for specified
+ subscrption id.
+
+ Wait for the data connection status to be DATA_STATE_CONNECTED
+ or DATA_STATE_DISCONNECTED.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ sub_id: subscription Id
+ state: Expected status: True or False.
+ If True, it will wait for status to be DATA_STATE_CONNECTED.
+ If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+ timeout_value: wait for cell data timeout value.
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ state_str = {
+ True: DATA_STATE_CONNECTED,
+ False: DATA_STATE_DISCONNECTED
+ }[state]
+
+ data_state = ad.droid.telephonyGetDataConnectionState()
+ if not state and ad.droid.telephonyGetDataConnectionState() == state_str:
+ return True
+
+ ad.ed.clear_events(EventDataConnectionStateChanged)
+ ad.droid.telephonyStartTrackingDataConnectionStateChangeForSubscription(
+ sub_id)
+ ad.droid.connectivityStartTrackingConnectivityStateChange()
+ try:
+ ad.log.info("User data enabled for sub_id %s: %s", sub_id,
+ ad.droid.telephonyIsDataEnabledForSubscription(sub_id))
+ data_state = ad.droid.telephonyGetDataConnectionState()
+ ad.log.info("Data connection state is %s", data_state)
+ ad.log.info("Network is connected: %s",
+ ad.droid.connectivityNetworkIsConnected())
+ if data_state == state_str:
+ return _wait_for_nw_data_connection(
+ log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
+
+ try:
+ ad.ed.wait_for_event(
+ EventDataConnectionStateChanged,
+ is_event_match,
+ timeout=timeout_value,
+ field=DataConnectionStateContainer.DATA_CONNECTION_STATE,
+ value=state_str)
+ except Empty:
+ ad.log.info("No expected event EventDataConnectionStateChanged %s",
+ state_str)
+
+ # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+ # data connection state.
+ # Otherwise, the network state will not be correct.
+ # The bug is tracked here: b/20921915
+
+ # Previously we use _is_data_connection_state_match,
+ # but telephonyGetDataConnectionState sometimes return wrong value.
+ # The bug is tracked here: b/22612607
+ # So we use _is_network_connected_state_match.
+
+ if _wait_for_droid_in_state(log, ad, timeout_value,
+ _is_network_connected_state_match, state):
+ return _wait_for_nw_data_connection(
+ log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
+ else:
+ return False
+
+ finally:
+ ad.droid.telephonyStopTrackingDataConnectionStateChangeForSubscription(
+ sub_id)
+
+
+def wait_for_wifi_data_connection(
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+ """Wait for data connection status to be expected value and connection is by WiFi.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ state: Expected status: True or False.
+ If True, it will wait for status to be DATA_STATE_CONNECTED.
+ If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+ timeout_value: wait for network data timeout value.
+ This is optional, default value is MAX_WAIT_TIME_NW_SELECTION
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ad.log.info("wait_for_wifi_data_connection")
+ return _wait_for_nw_data_connection(
+ log, ad, state, NETWORK_CONNECTION_TYPE_WIFI, timeout_value)
+
+
+def wait_for_data_connection(
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+ """Wait for data connection status to be expected value.
+
+ Wait for the data connection status to be DATA_STATE_CONNECTED
+ or DATA_STATE_DISCONNECTED.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ state: Expected status: True or False.
+ If True, it will wait for status to be DATA_STATE_CONNECTED.
+ If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+ timeout_value: wait for network data timeout value.
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return _wait_for_nw_data_connection(log, ad, state, None, timeout_value)
+
+
+def _wait_for_nw_data_connection(
+ log,
+ ad,
+ is_connected,
+ connection_type=None,
+ timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+ """Wait for data connection status to be expected value.
+
+ Wait for the data connection status to be DATA_STATE_CONNECTED
+ or DATA_STATE_DISCONNECTED.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ is_connected: Expected connection status: True or False.
+ If True, it will wait for status to be DATA_STATE_CONNECTED.
+ If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+ connection_type: expected connection type.
+ This is optional, if it is None, then any connection type will return True.
+ timeout_value: wait for network data timeout value.
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ad.ed.clear_events(EventConnectivityChanged)
+ ad.droid.connectivityStartTrackingConnectivityStateChange()
+ try:
+ cur_data_connection_state = ad.droid.connectivityNetworkIsConnected()
+ if is_connected == cur_data_connection_state:
+ current_type = get_internet_connection_type(log, ad)
+ ad.log.info("current data connection type: %s", current_type)
+ if not connection_type:
+ return True
+ else:
+ if not is_connected and current_type != connection_type:
+ ad.log.info("data connection not on %s!", connection_type)
+ return True
+ elif is_connected and current_type == connection_type:
+ ad.log.info("data connection on %s as expected",
+ connection_type)
+ return True
+ else:
+ ad.log.info("current data connection state: %s target: %s",
+ cur_data_connection_state, is_connected)
+
+ try:
+ event = ad.ed.wait_for_event(
+ EventConnectivityChanged, _connection_state_change,
+ timeout_value, is_connected, connection_type)
+ ad.log.info("Got event: %s", event)
+ except Empty:
+ pass
+
+ log.info(
+ "_wait_for_nw_data_connection: check connection after wait event.")
+ # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+ # data connection state.
+ # Otherwise, the network state will not be correct.
+ # The bug is tracked here: b/20921915
+ if _wait_for_droid_in_state(log, ad, timeout_value,
+ _is_network_connected_state_match,
+ is_connected):
+ current_type = get_internet_connection_type(log, ad)
+ ad.log.info("current data connection type: %s", current_type)
+ if not connection_type:
+ return True
+ else:
+ if not is_connected and current_type != connection_type:
+ ad.log.info("data connection not on %s", connection_type)
+ return True
+ elif is_connected and current_type == connection_type:
+ ad.log.info("after event wait, data connection on %s",
+ connection_type)
+ return True
+ else:
+ return False
+ else:
+ return False
+ except Exception as e:
+ ad.log.error("Exception error %s", str(e))
+ return False
+ finally:
+ ad.droid.connectivityStopTrackingConnectivityStateChange()
+
+
+def get_cell_data_roaming_state_by_adb(ad):
+ """Get Cell Data Roaming state. True for enabled, False for disabled"""
+ state_mapping = {"1": True, "0": False}
+ return state_mapping[ad.adb.shell("settings get global data_roaming")]
+
+
+def set_cell_data_roaming_state_by_adb(ad, state):
+ """Set Cell Data Roaming state."""
+ state_mapping = {True: "1", False: "0"}
+ ad.log.info("Set data roaming to %s", state)
+ ad.adb.shell("settings put global data_roaming %s" % state_mapping[state])
+
+
+def toggle_cell_data_roaming(ad, state):
+ """Enable cell data roaming for default data subscription.
+
+ Wait for the data roaming status to be DATA_STATE_CONNECTED
+ or DATA_STATE_DISCONNECTED.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+ state: True or False for enable or disable cell data roaming.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ state_int = {True: DATA_ROAMING_ENABLE, False: DATA_ROAMING_DISABLE}[state]
+ action_str = {True: "Enable", False: "Disable"}[state]
+ if ad.droid.connectivityCheckDataRoamingMode() == state:
+ ad.log.info("Data roaming is already in state %s", state)
+ return True
+ if not ad.droid.connectivitySetDataRoaming(state_int):
+ ad.error.info("Fail to config data roaming into state %s", state)
+ return False
+ if ad.droid.connectivityCheckDataRoamingMode() == state:
+ ad.log.info("Data roaming is configured into state %s", state)
+ return True
+ else:
+ ad.log.error("Data roaming is not configured into state %s", state)
+ return False
+
+
+def verify_incall_state(log, ads, expected_status):
+ """Verify phones in incall state or not.
+
+ Verify if all phones in the array <ads> are in <expected_status>.
+
+ Args:
+ log: Log object.
+ ads: Array of Android Device Object. All droid in this array will be tested.
+ expected_status: If True, verify all Phones in incall state.
+ If False, verify all Phones not in incall state.
+
+ """
+ result = True
+ for ad in ads:
+ if ad.droid.telecomIsInCall() is not expected_status:
+ ad.log.error("InCall status:%s, expected:%s",
+ ad.droid.telecomIsInCall(), expected_status)
+ result = False
+ return result
+
+
+def verify_active_call_number(log, ad, expected_number):
+ """Verify the number of current active call.
+
+ Verify if the number of current active call in <ad> is
+ equal to <expected_number>.
+
+ Args:
+ ad: Android Device Object.
+ expected_number: Expected active call number.
+ """
+ calls = ad.droid.telecomCallGetCallIds()
+ if calls is None:
+ actual_number = 0
+ else:
+ actual_number = len(calls)
+ if actual_number != expected_number:
+ ad.log.error("Active Call number is %s, expecting", actual_number,
+ expected_number)
+ return False
+ return True
+
+
+def num_active_calls(log, ad):
+ """Get the count of current active calls.
+
+ Args:
+ log: Log object.
+ ad: Android Device Object.
+
+ Returns:
+ Count of current active calls.
+ """
+ calls = ad.droid.telecomCallGetCallIds()
+ return len(calls) if calls else 0
+
+
+def show_enhanced_4g_lte(ad, sub_id):
+ result = True
+ capabilities = ad.telephony["subscription"][sub_id].get("capabilities", [])
+ if capabilities:
+ if "hide_enhanced_4g_lte" in capabilities:
+ result = False
+ ad.log.info(
+ '"Enhanced 4G LTE MODE" is hidden for sub ID %s.', sub_id)
+ show_enhanced_4g_lte_mode = getattr(
+ ad, "show_enhanced_4g_lte_mode", False)
+ if show_enhanced_4g_lte_mode in ["true", "True"]:
+ current_voice_sub_id = get_outgoing_voice_sub_id(ad)
+ if sub_id != current_voice_sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+
+ ad.log.info(
+ 'Show "Enhanced 4G LTE MODE" forcibly for sub ID %s.',
+ sub_id)
+ ad.adb.shell(
+ "am broadcast \
+ -a com.google.android.carrier.action.LOCAL_OVERRIDE \
+ -n com.google.android.carrier/.ConfigOverridingReceiver \
+ --ez hide_enhanced_4g_lte_bool false")
+ ad.telephony["subscription"][sub_id]["capabilities"].remove(
+ "hide_enhanced_4g_lte")
+
+ if sub_id != current_voice_sub_id:
+ set_incoming_voice_sub_id(ad, current_voice_sub_id)
+
+ result = True
+ return result
+
+
+def toggle_volte(log, ad, new_state=None):
+ """Toggle enable/disable VoLTE for default voice subscription.
+
+ Args:
+ ad: Android device object.
+ new_state: VoLTE mode state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+
+ Raises:
+ TelTestUtilsError if platform does not support VoLTE.
+ """
+ return toggle_volte_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad), new_state)
+
+
+def toggle_volte_for_subscription(log, ad, sub_id, new_state=None):
+ """Toggle enable/disable VoLTE for specified voice subscription.
+
+ Args:
+ ad: Android device object.
+ sub_id: Optional. If not assigned the default sub ID for voice call will
+ be used.
+ new_state: VoLTE mode state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+
+ """
+ if not show_enhanced_4g_lte(ad, sub_id):
+ return False
+
+ current_state = None
+ result = True
+
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+
+ try:
+ current_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id)
+ except Exception as e:
+ ad.log.warning(e)
+
+ if current_state is not None:
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.log.info(
+ "Toggle Enhanced 4G LTE Mode from %s to %s on sub_id %s",
+ current_state, new_state, sub_id)
+ ad.droid.imsMmTelSetAdvancedCallingEnabled(sub_id, new_state)
+ check_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id)
+ if check_state != new_state:
+ ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, still \
+ set to %s on sub_id %s", new_state, check_state, sub_id)
+ result = False
+ return result
+ else:
+ # TODO: b/26293960 No framework API available to set IMS by SubId.
+ voice_sub_id_changed = False
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+
+ # b/139641554
+ ad.terminate_all_sessions()
+ bring_up_sl4a(ad)
+
+ if not ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
+ ad.log.info(
+ "Enhanced 4G Lte Mode Setting is not enabled by platform for \
+ sub ID %s.", sub_id)
+ return False
+
+ current_state = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser()
+ ad.log.info("Current state of Enhanced 4G Lte Mode Setting for sub \
+ ID %s: %s", sub_id, current_state)
+ ad.log.info("New desired state of Enhanced 4G Lte Mode Setting for sub \
+ ID %s: %s", sub_id, new_state)
+
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.log.info(
+ "Toggle Enhanced 4G LTE Mode from %s to %s for sub ID %s.",
+ current_state, new_state, sub_id)
+ ad.droid.imsSetEnhanced4gMode(new_state)
+ time.sleep(5)
+
+ check_state = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser()
+ if check_state != new_state:
+ ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, \
+ still set to %s on sub_id %s", new_state, check_state, sub_id)
+ result = False
+
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+
+ return result
+
+
+def toggle_wfc(log, ad, new_state=None):
+ """ Toggle WFC enable/disable
+
+ Args:
+ log: Log object
+ ad: Android device object.
+ new_state: WFC state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+ """
+ return toggle_wfc_for_subscription(
+ log, ad, new_state, get_outgoing_voice_sub_id(ad))
+
+
+def toggle_wfc_for_subscription(log, ad, new_state=None, sub_id=None):
+ """ Toggle WFC enable/disable for specified voice subscription.
+
+ Args:
+ ad: Android device object.
+ sub_id: Optional. If not assigned the default sub ID for voice call will
+ be used.
+ new_state: WFC state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+ """
+ current_state = None
+ result = True
+
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+
+ try:
+ current_state = ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id)
+ except Exception as e:
+ ad.log.warning(e)
+
+ if current_state is not None:
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.log.info(
+ "Toggle Enhanced 4G LTE Mode from %s to %s on sub_id %s",
+ current_state, new_state, sub_id)
+ ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, new_state)
+ check_state = ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id)
+ if check_state != new_state:
+ ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, \
+ still set to %s on sub_id %s", new_state, check_state, sub_id)
+ result = False
+ return result
+ else:
+ voice_sub_id_changed = False
+ if not sub_id:
+ sub_id = get_outgoing_voice_sub_id(ad)
+ else:
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+
+ # b/139641554
+ ad.terminate_all_sessions()
+ bring_up_sl4a(ad)
+
+ if not ad.droid.imsIsWfcEnabledByPlatform():
+ ad.log.info("WFC is not enabled by platform for sub ID %s.", sub_id)
+ return False
+
+ current_state = ad.droid.imsIsWfcEnabledByUser()
+ ad.log.info("Current state of WFC Setting for sub ID %s: %s",
+ sub_id, current_state)
+ ad.log.info("New desired state of WFC Setting for sub ID %s: %s",
+ sub_id, new_state)
+
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.log.info("Toggle WFC user enabled from %s to %s for sub ID %s",
+ current_state, new_state, sub_id)
+ ad.droid.imsSetWfcSetting(new_state)
+
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+
+ return True
+
+def is_enhanced_4g_lte_mode_setting_enabled(ad, sub_id, enabled_by="platform"):
+ voice_sub_id_changed = False
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+ if enabled_by == "platform":
+ res = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform()
+ else:
+ res = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser()
+ if not res:
+ ad.log.info("Enhanced 4G Lte Mode Setting is NOT enabled by %s for sub \
+ ID %s.", enabled_by, sub_id)
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ return False
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ ad.log.info("Enhanced 4G Lte Mode Setting is enabled by %s for sub ID %s.",
+ enabled_by, sub_id)
+ return True
+
+def set_enhanced_4g_mode(ad, sub_id, state):
+ voice_sub_id_changed = False
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+
+ ad.droid.imsSetEnhanced4gMode(state)
+ time.sleep(5)
+
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+
+def wait_for_enhanced_4g_lte_setting(log,
+ ad,
+ sub_id,
+ max_time=MAX_WAIT_TIME_FOR_STATE_CHANGE):
+ """Wait for android device to enable enhance 4G LTE setting.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device report VoLTE enabled bit true within max_time.
+ Return False if timeout.
+ """
+ return wait_for_state(
+ is_enhanced_4g_lte_mode_setting_enabled,
+ True,
+ max_time,
+ WAIT_TIME_BETWEEN_STATE_CHECK,
+ ad,
+ sub_id,
+ enabled_by="platform")
+
+
+def set_wfc_mode(log, ad, wfc_mode):
+ """Set WFC enable/disable and mode.
+
+ Args:
+ log: Log object
+ ad: Android device object.
+ wfc_mode: WFC mode to set to.
+ Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
+
+ Returns:
+ True if success. False if ad does not support WFC or error happened.
+ """
+ return set_wfc_mode_for_subscription(
+ ad, wfc_mode, get_outgoing_voice_sub_id(ad))
+
+
+def set_wfc_mode_for_subscription(ad, wfc_mode, sub_id=None):
+ """Set WFC enable/disable and mode subscription based
+
+ Args:
+ ad: Android device object.
+ wfc_mode: WFC mode to set to.
+ Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED.
+ sub_id: subscription Id
+
+ Returns:
+ True if success. False if ad does not support WFC or error happened.
+ """
+ if wfc_mode not in [
+ WFC_MODE_WIFI_ONLY,
+ WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED,
+ WFC_MODE_DISABLED]:
+
+ ad.log.error("Given WFC mode (%s) is not correct.", wfc_mode)
+ return False
+
+ current_mode = None
+ result = True
+
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+
+ try:
+ current_mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id)
+ ad.log.info("Current WFC mode of sub ID: %s", current_mode)
+ except Exception as e:
+ ad.log.warning(e)
+
+ if current_mode is not None:
+ try:
+ if not ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id):
+ if wfc_mode is WFC_MODE_DISABLED:
+ return True
+ ad.log.info(
+ "WFC is disabled for sub ID %s. Enabling WFC...", sub_id)
+ ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, True)
+
+ if wfc_mode is WFC_MODE_DISABLED:
+ ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, False)
+ return True
+
+ ad.log.info("Set wfc mode to %s for sub ID %s.", wfc_mode, sub_id)
+ ad.droid.imsMmTelSetVoWiFiModeSetting(sub_id, wfc_mode)
+ mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id)
+ if mode != wfc_mode:
+ ad.log.error("WFC mode for sub ID %s is %s, not in %s",
+ sub_id, mode, wfc_mode)
+ return False
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ return True
+ else:
+ voice_sub_id_changed = False
+ if not sub_id:
+ sub_id = get_outgoing_voice_sub_id(ad)
+ else:
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+
+ # b/139641554
+ ad.terminate_all_sessions()
+ bring_up_sl4a(ad)
+
+ if wfc_mode != WFC_MODE_DISABLED and wfc_mode not in ad.telephony[
+ "subscription"][get_outgoing_voice_sub_id(ad)].get("wfc_modes", []):
+ ad.log.error("WFC mode %s is not supported", wfc_mode)
+ raise signals.TestSkip("WFC mode %s is not supported" % wfc_mode)
+ try:
+ ad.log.info("Set wfc mode to %s", wfc_mode)
+ if wfc_mode != WFC_MODE_DISABLED:
+ start_adb_tcpdump(ad, interface="wlan0", mask="all")
+ if not ad.droid.imsIsWfcEnabledByPlatform():
+ if wfc_mode == WFC_MODE_DISABLED:
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ return True
+ else:
+ ad.log.error("WFC not supported by platform.")
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ return False
+ ad.droid.imsSetWfcMode(wfc_mode)
+ mode = ad.droid.imsGetWfcMode()
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ if mode != wfc_mode:
+ ad.log.error("WFC mode is %s, not in %s", mode, wfc_mode)
+ return False
+ except Exception as e:
+ log.error(e)
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+ return False
+ return True
+
+
+
+def set_ims_provisioning_for_subscription(ad, feature_flag, value, sub_id=None):
+ """ Sets Provisioning Values for Subscription Id
+
+ Args:
+ ad: Android device object.
+ sub_id: Subscription Id
+ feature_flag: voice or video
+ value: enable or disable
+
+ """
+ try:
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ ad.log.info("SubId %s - setprovisioning for %s to %s",
+ sub_id, feature_flag, value)
+ result = ad.droid.provisioningSetProvisioningIntValue(sub_id,
+ feature_flag, value)
+ if result == 0:
+ return True
+ return False
+ except Exception as e:
+ ad.log.error(e)
+ return False
+
+
+def get_ims_provisioning_for_subscription(ad, feature_flag, tech, sub_id=None):
+ """ Gets Provisioning Values for Subscription Id
+
+ Args:
+ ad: Android device object.
+ sub_id: Subscription Id
+ feature_flag: voice, video, ut, sms
+ tech: lte, iwlan
+
+ """
+ try:
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ result = ad.droid.provisioningGetProvisioningStatusForCapability(
+ sub_id, feature_flag, tech)
+ ad.log.info("SubId %s - getprovisioning for %s on %s - %s",
+ sub_id, feature_flag, tech, result)
+ return result
+ except Exception as e:
+ ad.log.error(e)
+ return False
+
+
+def get_carrier_provisioning_for_subscription(ad, feature_flag,
+ tech, sub_id=None):
+ """ Gets Provisioning Values for Subscription Id
+
+ Args:
+ ad: Android device object.
+ sub_id: Subscription Id
+ feature_flag: voice, video, ut, sms
+ tech: wlan, wwan
+
+ """
+ try:
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ result = ad.droid.imsMmTelIsSupported(sub_id, feature_flag, tech)
+ ad.log.info("SubId %s - imsMmTelIsSupported for %s on %s - %s",
+ sub_id, feature_flag, tech, result)
+ return result
+ except Exception as e:
+ ad.log.error(e)
+ return False
+
+def activate_wfc_on_device(log, ad):
+ """ Activates WiFi calling on device.
+
+ Required for certain network operators.
+
+ Args:
+ log: Log object
+ ad: Android device object
+
+ """
+ activate_wfc_on_device_for_subscription(log, ad,
+ ad.droid.subscriptionGetDefaultSubId())
+
+
+def activate_wfc_on_device_for_subscription(log, ad, sub_id):
+ """ Activates WiFi calling on device for a subscription.
+
+ Args:
+ log: Log object
+ ad: Android device object
+ sub_id: Subscription id (integer)
+
+ """
+ if not sub_id or INVALID_SUB_ID == sub_id:
+ ad.log.error("Subscription id invalid")
+ return
+ operator_name = get_operator_name(log, ad, sub_id)
+ if operator_name in (CARRIER_VZW, CARRIER_ATT, CARRIER_BELL, CARRIER_ROGERS,
+ CARRIER_TELUS, CARRIER_KOODO, CARRIER_VIDEOTRON, CARRIER_FRE):
+ ad.log.info("Activating WFC on operator : %s", operator_name)
+ if not ad.is_apk_installed("com.google.android.wfcactivation"):
+ ad.log.error("WFC Activation Failed, wfc activation apk not installed")
+ return
+ wfc_activate_cmd ="am start --ei EXTRA_LAUNCH_CARRIER_APP 0 --ei " \
+ "android.telephony.extra.SUBSCRIPTION_INDEX {} -n ".format(sub_id)
+ if CARRIER_ATT == operator_name:
+ ad.adb.shell("setprop dbg.att.force_wfc_nv_enabled true")
+ wfc_activate_cmd = wfc_activate_cmd+\
+ "\"com.google.android.wfcactivation/" \
+ ".WfcActivationActivity\""
+ elif CARRIER_VZW == operator_name:
+ ad.adb.shell("setprop dbg.vzw.force_wfc_nv_enabled true")
+ wfc_activate_cmd = wfc_activate_cmd + \
+ "\"com.google.android.wfcactivation/" \
+ ".VzwEmergencyAddressActivity\""
+ else:
+ wfc_activate_cmd = wfc_activate_cmd+ \
+ "\"com.google.android.wfcactivation/" \
+ ".can.WfcActivationCanadaActivity\""
+ ad.adb.shell(wfc_activate_cmd)
+
+
+def toggle_video_calling(log, ad, new_state=None):
+ """Toggle enable/disable Video calling for default voice subscription.
+
+ Args:
+ ad: Android device object.
+ new_state: Video mode state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+
+ Raises:
+ TelTestUtilsError if platform does not support Video calling.
+ """
+ if not ad.droid.imsIsVtEnabledByPlatform():
+ if new_state is not False:
+ raise TelTestUtilsError("VT not supported by platform.")
+ # if the user sets VT false and it's unavailable we just let it go
+ return False
+
+ current_state = ad.droid.imsIsVtEnabledByUser()
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.droid.imsSetVtSetting(new_state)
+ return True
+
+
+def toggle_video_calling_for_subscription(ad, new_state=None, sub_id=None):
+ """Toggle enable/disable Video calling for subscription.
+
+ Args:
+ ad: Android device object.
+ new_state: Video mode state to set to.
+ True for enable, False for disable.
+ If None, opposite of the current state.
+ sub_id: subscription Id
+
+ """
+ try:
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ current_state = ad.droid.imsMmTelIsVtSettingEnabled(sub_id)
+ if new_state is None:
+ new_state = not current_state
+ if new_state != current_state:
+ ad.log.info("SubId %s - Toggle VT from %s to %s", sub_id,
+ current_state, new_state)
+ ad.droid.imsMmTelSetVtSettingEnabled(sub_id, new_state)
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ return True
+
+
+def _wait_for_droid_in_state(log, ad, max_time, state_check_func, *args,
+ **kwargs):
+ while max_time >= 0:
+ if state_check_func(log, ad, *args, **kwargs):
+ return True
+
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
+
+ return False
+
+
+def _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_time, state_check_func, *args, **kwargs):
+ while max_time >= 0:
+ if state_check_func(log, ad, sub_id, *args, **kwargs):
+ return True
+
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
+
+ return False
+
+
+def _wait_for_droids_in_state(log, ads, max_time, state_check_func, *args,
+ **kwargs):
+ while max_time > 0:
+ success = True
+ for ad in ads:
+ if not state_check_func(log, ad, *args, **kwargs):
+ success = False
+ break
+ if success:
+ return True
+
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
+
+ return False
+
+
+def is_phone_in_call(log, ad):
+ """Return True if phone in call.
+
+ Args:
+ log: log object.
+ ad: android device.
+ """
+ try:
+ return ad.droid.telecomIsInCall()
+ except:
+ return "mCallState=2" in ad.adb.shell(
+ "dumpsys telephony.registry | grep mCallState")
+
+
+def is_phone_not_in_call(log, ad):
+ """Return True if phone not in call.
+
+ Args:
+ log: log object.
+ ad: android device.
+ """
+ in_call = ad.droid.telecomIsInCall()
+ call_state = ad.droid.telephonyGetCallState()
+ if in_call:
+ ad.log.info("Device is In Call")
+ if call_state != TELEPHONY_STATE_IDLE:
+ ad.log.info("Call_state is %s, not %s", call_state,
+ TELEPHONY_STATE_IDLE)
+ return ((not in_call) and (call_state == TELEPHONY_STATE_IDLE))
+
+
+def wait_for_droid_in_call(log, ad, max_time):
+ """Wait for android to be in call state.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ If phone become in call state within max_time, return True.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, is_phone_in_call)
+
+
+def is_phone_in_call_active(ad, call_id=None):
+ """Return True if phone in active call.
+
+ Args:
+ log: log object.
+ ad: android device.
+ call_id: the call id
+ """
+ if ad.droid.telecomIsInCall():
+ if not call_id:
+ call_id = ad.droid.telecomCallGetCallIds()[0]
+ call_state = ad.droid.telecomCallGetCallState(call_id)
+ ad.log.info("%s state is %s", call_id, call_state)
+ return call_state == "ACTIVE"
+ else:
+ ad.log.info("Not in telecomIsInCall")
+ return False
+
+
+def wait_for_in_call_active(ad,
+ timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
+ interval=WAIT_TIME_BETWEEN_STATE_CHECK,
+ call_id=None):
+ """Wait for call reach active state.
+
+ Args:
+ log: log object.
+ ad: android device.
+ call_id: the call id
+ """
+ if not call_id:
+ call_id = ad.droid.telecomCallGetCallIds()[0]
+ args = [ad, call_id]
+ if not wait_for_state(is_phone_in_call_active, True, timeout, interval,
+ *args):
+ ad.log.error("Call did not reach ACTIVE state")
+ return False
+ else:
+ return True
+
+
+def wait_for_telecom_ringing(log, ad, max_time=MAX_WAIT_TIME_TELECOM_RINGING):
+ """Wait for android to be in telecom ringing state.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time. This is optional.
+ Default Value is MAX_WAIT_TIME_TELECOM_RINGING.
+
+ Returns:
+ If phone become in telecom ringing state within max_time, return True.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(
+ log, ad, max_time, lambda log, ad: ad.droid.telecomIsRinging())
+
+
+def wait_for_droid_not_in_call(log, ad, max_time=MAX_WAIT_TIME_CALL_DROP):
+ """Wait for android to be not in call state.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ If phone become not in call state within max_time, return True.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, is_phone_not_in_call)
+
+
+def _is_attached(log, ad, voice_or_data):
+ return _is_attached_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+
+def _is_attached_for_subscription(log, ad, sub_id, voice_or_data):
+ rat = get_network_rat_for_subscription(log, ad, sub_id, voice_or_data)
+ ad.log.info("Sub_id %s network RAT is %s for %s", sub_id, rat,
+ voice_or_data)
+ return rat != RAT_UNKNOWN
+
+
+def is_voice_attached(log, ad):
+ return _is_attached_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), NETWORK_SERVICE_VOICE)
+
+
+def wait_for_voice_attach(log, ad, max_time=MAX_WAIT_TIME_NW_SELECTION):
+ """Wait for android device to attach on voice.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device attach voice within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
+ NETWORK_SERVICE_VOICE)
+
+
+def wait_for_voice_attach_for_subscription(
+ log, ad, sub_id, max_time=MAX_WAIT_TIME_NW_SELECTION):
+ """Wait for android device to attach on voice in subscription id.
+
+ Args:
+ log: log object.
+ ad: android device.
+ sub_id: subscription id.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device attach voice within max_time.
+ Return False if timeout.
+ """
+ if not _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_time, _is_attached_for_subscription,
+ NETWORK_SERVICE_VOICE):
+ return False
+
+ # TODO: b/26295983 if pone attach to 1xrtt from unknown, phone may not
+ # receive incoming call immediately.
+ if ad.droid.telephonyGetCurrentVoiceNetworkType() == RAT_1XRTT:
+ time.sleep(WAIT_TIME_1XRTT_VOICE_ATTACH)
+ return True
+
+
+def wait_for_data_attach(log, ad, max_time):
+ """Wait for android device to attach on data.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device attach data within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
+ NETWORK_SERVICE_DATA)
+
+
+def wait_for_data_attach_for_subscription(log, ad, sub_id, max_time):
+ """Wait for android device to attach on data in subscription id.
+
+ Args:
+ log: log object.
+ ad: android device.
+ sub_id: subscription id.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device attach data within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_time, _is_attached_for_subscription,
+ NETWORK_SERVICE_DATA)
+
+
+def is_ims_registered(log, ad, sub_id=None):
+ """Return True if IMS registered.
+
+ Args:
+ log: log object.
+ ad: android device.
+ sub_id: Optional. If not assigned the default sub ID of voice call will
+ be used.
+
+ Returns:
+ Return True if IMS registered.
+ Return False if IMS not registered.
+ """
+ if not sub_id:
+ return ad.droid.telephonyIsImsRegistered()
+ else:
+ return change_voice_subid_temporarily(
+ ad, sub_id, ad.droid.telephonyIsImsRegistered)
+
+
+def wait_for_ims_registered(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
+ """Wait for android device to register on ims.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device register ims successfully within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, is_ims_registered)
+
+def is_volte_available(log, ad, sub_id):
+ """Return True if VoLTE is available.
+
+ Args:
+ log: log object.
+ ad: android device.
+ sub_id: Optional. If not assigned the default sub ID of voice call will
+ be used.
+
+ Returns:
+ Return True if VoLTE is available.
+ Return False if VoLTE is not available.
+ """
+ if not sub_id:
+ return ad.droid.telephonyIsVolteAvailable()
+ else:
+ return change_voice_subid_temporarily(
+ ad, sub_id, ad.droid.telephonyIsVolteAvailable)
+
+def is_volte_enabled(log, ad, sub_id=None):
+ """Return True if VoLTE feature bit is True.
+
+ Args:
+ log: log object.
+ ad: android device.
+ sub_id: Optional. If not assigned the default sub ID of voice call will
+ be used.
+
+ Returns:
+ Return True if VoLTE feature bit is True and IMS registered.
+ Return False if VoLTE feature bit is False or IMS not registered.
+ """
+ if not is_ims_registered(log, ad, sub_id):
+ ad.log.info("IMS is not registered for sub ID %s.", sub_id)
+ return False
+ if not is_volte_available(log, ad, sub_id):
+ ad.log.info("IMS is registered for sub ID %s, IsVolteCallingAvailable \
+ is False", sub_id)
+ return False
+ else:
+ ad.log.info("IMS is registered for sub ID %s, IsVolteCallingAvailable \
+ is True", sub_id)
+ return True
+
+
+def is_video_enabled(log, ad):
+ """Return True if Video Calling feature bit is True.
+
+ Args:
+ log: log object.
+ ad: android device.
+
+ Returns:
+ Return True if Video Calling feature bit is True and IMS registered.
+ Return False if Video Calling feature bit is False or IMS not registered.
+ """
+ video_status = ad.droid.telephonyIsVideoCallingAvailable()
+ if video_status is True and is_ims_registered(log, ad) is False:
+ ad.log.error(
+ "Error! Video Call is Available, but IMS is not registered.")
+ return False
+ return video_status
+
+
+def wait_for_volte_enabled(
+ log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED,sub_id=None):
+ """Wait for android device to report VoLTE enabled bit true.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device report VoLTE enabled bit true within max_time.
+ Return False if timeout.
+ """
+ if not sub_id:
+ return _wait_for_droid_in_state(log, ad, max_time, is_volte_enabled)
+ else:
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_time, is_volte_enabled)
+
+
+def wait_for_video_enabled(log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED):
+ """Wait for android device to report Video Telephony enabled bit true.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+
+ Returns:
+ Return True if device report Video Telephony enabled bit true within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, is_video_enabled)
+
+
+def is_wfc_enabled(log, ad):
+ """Return True if WiFi Calling feature bit is True.
+
+ Args:
+ log: log object.
+ ad: android device.
+
+ Returns:
+ Return True if WiFi Calling feature bit is True and IMS registered.
+ Return False if WiFi Calling feature bit is False or IMS not registered.
+ """
+ if not is_ims_registered(log, ad):
+ ad.log.info("IMS is not registered.")
+ return False
+ if not ad.droid.telephonyIsWifiCallingAvailable():
+ ad.log.info("IMS is registered, IsWifiCallingAvailable is False")
+ return False
+ else:
+ ad.log.info("IMS is registered, IsWifiCallingAvailable is True")
+ return True
+
+
+def wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
+ """Wait for android device to report WiFi Calling enabled bit true.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+ Default value is MAX_WAIT_TIME_WFC_ENABLED.
+
+ Returns:
+ Return True if device report WiFi Calling enabled bit true within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(log, ad, max_time, is_wfc_enabled)
+
+
+def wait_for_wfc_disabled(log, ad, max_time=MAX_WAIT_TIME_WFC_DISABLED):
+ """Wait for android device to report WiFi Calling enabled bit false.
+
+ Args:
+ log: log object.
+ ad: android device.
+ max_time: maximal wait time.
+ Default value is MAX_WAIT_TIME_WFC_DISABLED.
+
+ Returns:
+ Return True if device report WiFi Calling enabled bit false within max_time.
+ Return False if timeout.
+ """
+ return _wait_for_droid_in_state(
+ log, ad, max_time, lambda log, ad: not is_wfc_enabled(log, ad))
+
+
+def get_phone_number(log, ad):
+ """Get phone number for default subscription
+
+ Args:
+ log: log object.
+ ad: Android device object.
+
+ Returns:
+ Phone number.
+ """
+ return get_phone_number_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def get_phone_number_for_subscription(log, ad, subid):
+ """Get phone number for subscription
+
+ Args:
+ log: log object.
+ ad: Android device object.
+ subid: subscription id.
+
+ Returns:
+ Phone number.
+ """
+ number = None
+ try:
+ number = ad.telephony['subscription'][subid]['phone_num']
+ except KeyError:
+ number = ad.droid.telephonyGetLine1NumberForSubscription(subid)
+ return number
+
+
+def set_phone_number(log, ad, phone_num):
+ """Set phone number for default subscription
+
+ Args:
+ log: log object.
+ ad: Android device object.
+ phone_num: phone number string.
+
+ Returns:
+ True if success.
+ """
+ return set_phone_number_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad),
+ phone_num)
+
+
+def set_phone_number_for_subscription(log, ad, subid, phone_num):
+ """Set phone number for subscription
+
+ Args:
+ log: log object.
+ ad: Android device object.
+ subid: subscription id.
+ phone_num: phone number string.
+
+ Returns:
+ True if success.
+ """
+ try:
+ ad.telephony['subscription'][subid]['phone_num'] = phone_num
+ except Exception:
+ return False
+ return True
+
+
+def get_operator_name(log, ad, subId=None):
+ """Get operator name (e.g. vzw, tmo) of droid.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription ID
+ Optional, default is None
+
+ Returns:
+ Operator name.
+ """
+ try:
+ if subId is not None:
+ result = operator_name_from_plmn_id(
+ ad.droid.telephonyGetNetworkOperatorForSubscription(subId))
+ else:
+ result = operator_name_from_plmn_id(
+ ad.droid.telephonyGetNetworkOperator())
+ except KeyError:
+ try:
+ if subId is not None:
+ result = ad.droid.telephonyGetNetworkOperatorNameForSubscription(
+ subId)
+ else:
+ result = ad.droid.telephonyGetNetworkOperatorName()
+ result = operator_name_from_network_name(result)
+ except Exception:
+ result = CARRIER_UNKNOWN
+ ad.log.info("Operator Name is %s", result)
+ return result
+
+
+def get_model_name(ad):
+ """Get android device model name
+
+ Args:
+ ad: Android device object
+
+ Returns:
+ model name string
+ """
+ # TODO: Create translate table.
+ model = ad.model
+ if (model.startswith(AOSP_PREFIX)):
+ model = model[len(AOSP_PREFIX):]
+ return model
+
+
+def is_sms_match(event, phonenumber_tx, text):
+ """Return True if 'text' equals to event['data']['Text']
+ and phone number match.
+
+ Args:
+ event: Event object to verify.
+ phonenumber_tx: phone number for sender.
+ text: text string to verify.
+
+ Returns:
+ Return True if 'text' equals to event['data']['Text']
+ and phone number match.
+ """
+ return (check_phone_number_match(event['data']['Sender'], phonenumber_tx)
+ and event['data']['Text'].strip() == text)
+
+
+def is_sms_partial_match(event, phonenumber_tx, text):
+ """Return True if 'text' starts with event['data']['Text']
+ and phone number match.
+
+ Args:
+ event: Event object to verify.
+ phonenumber_tx: phone number for sender.
+ text: text string to verify.
+
+ Returns:
+ Return True if 'text' starts with event['data']['Text']
+ and phone number match.
+ """
+ event_text = event['data']['Text'].strip()
+ if event_text.startswith("("):
+ event_text = event_text.split(")")[-1]
+ return (check_phone_number_match(event['data']['Sender'], phonenumber_tx)
+ and text.startswith(event_text))
+
+
+def sms_send_receive_verify(log,
+ ad_tx,
+ ad_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE,
+ expected_result=True,
+ slot_id_rx=None):
+ """Send SMS, receive SMS, and verify content and sender's number.
+
+ Send (several) SMS from droid_tx to droid_rx.
+ Verify SMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object
+ ad_rx: Receiver's Android Device Object
+ array_message: the array of message to send/receive
+ slot_id_rx: the slot on the Receiver's android device (0/1)
+ """
+ subid_tx = get_outgoing_message_sub_id(ad_tx)
+ if slot_id_rx is None:
+ subid_rx = get_incoming_message_sub_id(ad_rx)
+ else:
+ subid_rx = get_subid_from_slot_index(log, ad_rx, slot_id_rx)
+
+ result = sms_send_receive_verify_for_subscription(
+ log, ad_tx, ad_rx, subid_tx, subid_rx, array_message, max_wait_time)
+ if result != expected_result:
+ log_messaging_screen_shot(ad_tx, test_name="sms_tx")
+ log_messaging_screen_shot(ad_rx, test_name="sms_rx")
+ return result == expected_result
+
+
+def wait_for_matching_sms(log,
+ ad_rx,
+ phonenumber_tx,
+ text,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE,
+ allow_multi_part_long_sms=True):
+ """Wait for matching incoming SMS.
+
+ Args:
+ log: Log object.
+ ad_rx: Receiver's Android Device Object
+ phonenumber_tx: Sender's phone number.
+ text: SMS content string.
+ allow_multi_part_long_sms: is long SMS allowed to be received as
+ multiple short SMS. This is optional, default value is True.
+
+ Returns:
+ True if matching incoming SMS is received.
+ """
+ if not allow_multi_part_long_sms:
+ try:
+ ad_rx.messaging_ed.wait_for_event(EventSmsReceived, is_sms_match,
+ max_wait_time, phonenumber_tx,
+ text)
+ ad_rx.log.info("Got event %s", EventSmsReceived)
+ return True
+ except Empty:
+ ad_rx.log.error("No matched SMS received event.")
+ return False
+ else:
+ try:
+ received_sms = ''
+ remaining_text = text
+ while (remaining_text != ''):
+ event = ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived, is_sms_partial_match, max_wait_time,
+ phonenumber_tx, remaining_text)
+ event_text = event['data']['Text'].split(")")[-1].strip()
+ event_text_length = len(event_text)
+ ad_rx.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length,
+ phonenumber_tx)
+ remaining_text = remaining_text[event_text_length:]
+ received_sms += event_text
+ ad_rx.log.info("Received SMS of length %s", len(received_sms))
+ return True
+ except Empty:
+ ad_rx.log.error(
+ "Missing SMS received event of text length %s from %s",
+ len(remaining_text), phonenumber_tx)
+ if received_sms != '':
+ ad_rx.log.error(
+ "Only received partial matched SMS of length %s",
+ len(received_sms))
+ return False
+
+
+def is_mms_match(event, phonenumber_tx, text):
+ """Return True if 'text' equals to event['data']['Text']
+ and phone number match.
+
+ Args:
+ event: Event object to verify.
+ phonenumber_tx: phone number for sender.
+ text: text string to verify.
+
+ Returns:
+ Return True if 'text' equals to event['data']['Text']
+ and phone number match.
+ """
+ #TODO: add mms matching after mms message parser is added in sl4a. b/34276948
+ return True
+
+
+def wait_for_matching_mms(log,
+ ad_rx,
+ phonenumber_tx,
+ text,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ """Wait for matching incoming SMS.
+
+ Args:
+ log: Log object.
+ ad_rx: Receiver's Android Device Object
+ phonenumber_tx: Sender's phone number.
+ text: SMS content string.
+ allow_multi_part_long_sms: is long SMS allowed to be received as
+ multiple short SMS. This is optional, default value is True.
+
+ Returns:
+ True if matching incoming SMS is received.
+ """
+ try:
+ #TODO: add mms matching after mms message parser is added in sl4a. b/34276948
+ ad_rx.messaging_ed.wait_for_event(EventMmsDownloaded, is_mms_match,
+ max_wait_time, phonenumber_tx, text)
+ ad_rx.log.info("Got event %s", EventMmsDownloaded)
+ return True
+ except Empty:
+ ad_rx.log.warning("No matched MMS downloaded event.")
+ return False
+
+
+def sms_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ """Send SMS, receive SMS, and verify content and sender's number.
+
+ Send (several) SMS from droid_tx to droid_rx.
+ Verify SMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object..
+ ad_rx: Receiver's Android Device Object.
+ subid_tx: Sender's subsciption ID to be used for SMS
+ subid_rx: Receiver's subsciption ID to be used for SMS
+ array_message: the array of message to send/receive
+ """
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+
+ for ad in (ad_tx, ad_rx):
+ ad.send_keycode("BACK")
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ ad.messaging_ed.clear_all_events()
+ ad.messaging_droid.logI(
+ "Start sms_send_receive_verify_for_subscription test")
+ except Exception:
+ ad.log.info("Create new sl4a session for messaging")
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
+ for text in array_message:
+ length = len(text)
+ ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx, phonenumber_rx, length, text)
+ try:
+ ad_rx.messaging_ed.clear_events(EventSmsReceived)
+ ad_tx.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx.messaging_ed.clear_events(EventSmsSentFailure)
+ ad_rx.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ time.sleep(1) #sleep 100ms after starting event tracking
+ ad_tx.messaging_droid.logI("Sending SMS of length %s" % length)
+ ad_rx.messaging_droid.logI("Expecting SMS of length %s" % length)
+ ad_tx.messaging_droid.smsSendTextMessage(phonenumber_rx, text,
+ True)
+ try:
+ events = ad_tx.messaging_ed.pop_events(
+ "(%s|%s|%s|%s)" %
+ (EventSmsSentSuccess, EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure), max_wait_time)
+ for event in events:
+ ad_tx.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and event["data"].get("Reason"):
+ ad_tx.log.error("%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ return False
+ elif event["name"] == EventSmsSentSuccess or event["name"] == EventSmsDeliverSuccess:
+ break
+ except Empty:
+ ad_tx.log.error("No %s or %s event for SMS of length %s.",
+ EventSmsSentSuccess, EventSmsSentFailure,
+ length)
+ return False
+
+ if not wait_for_matching_sms(
+ log,
+ ad_rx,
+ phonenumber_tx,
+ text,
+ max_wait_time,
+ allow_multi_part_long_sms=True):
+ ad_rx.log.error("No matching received SMS of length %s.",
+ length)
+ return False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingSmsMessage()
+ return True
+
+
+def mms_send_receive_verify(log,
+ ad_tx,
+ ad_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE,
+ expected_result=True,
+ slot_id_rx=None):
+ """Send MMS, receive MMS, and verify content and sender's number.
+
+ Send (several) MMS from droid_tx to droid_rx.
+ Verify MMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object
+ ad_rx: Receiver's Android Device Object
+ array_message: the array of message to send/receive
+ """
+ subid_tx = get_outgoing_message_sub_id(ad_tx)
+ if slot_id_rx is None:
+ subid_rx = get_incoming_message_sub_id(ad_rx)
+ else:
+ subid_rx = get_subid_from_slot_index(log, ad_rx, slot_id_rx)
+
+ result = mms_send_receive_verify_for_subscription(
+ log, ad_tx, ad_rx, subid_tx, subid_rx, array_message, max_wait_time)
+ if result != expected_result:
+ log_messaging_screen_shot(ad_tx, test_name="mms_tx")
+ log_messaging_screen_shot(ad_rx, test_name="mms_rx")
+ return result == expected_result
+
+
+def sms_mms_send_logcat_check(ad, type, begin_time):
+ type = type.upper()
+ log_results = ad.search_logcat(
+ "%s Message sent successfully" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found %s sent successful log message: %s", type,
+ log_results[-1]["log_message"])
+ return True
+ else:
+ log_results = ad.search_logcat(
+ "ProcessSentMessageAction: Done sending %s message" % type,
+ begin_time=begin_time)
+ if log_results:
+ for log_result in log_results:
+ if "status is SUCCEEDED" in log_result["log_message"]:
+ ad.log.info(
+ "Found BugleDataModel %s send succeed log message: %s",
+ type, log_result["log_message"])
+ return True
+ return False
+
+
+def sms_mms_receive_logcat_check(ad, type, begin_time):
+ type = type.upper()
+ smshandle_logs = ad.search_logcat(
+ "InboundSmsHandler: No broadcast sent on processing EVENT_BROADCAST_SMS",
+ begin_time=begin_time)
+ if smshandle_logs:
+ ad.log.warning("Found %s", smshandle_logs[-1]["log_message"])
+ log_results = ad.search_logcat(
+ "New %s Received" % type, begin_time=begin_time) or \
+ ad.search_logcat("New %s Downloaded" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found SL4A %s received log message: %s", type,
+ log_results[-1]["log_message"])
+ return True
+ else:
+ log_results = ad.search_logcat(
+ "Received %s message" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found %s received log message: %s", type,
+ log_results[-1]["log_message"])
+ log_results = ad.search_logcat(
+ "ProcessDownloadedMmsAction", begin_time=begin_time)
+ for log_result in log_results:
+ ad.log.info("Found %s", log_result["log_message"])
+ if "status is SUCCEEDED" in log_result["log_message"]:
+ ad.log.info("Download succeed with ProcessDownloadedMmsAction")
+ return True
+ return False
+
+
+#TODO: add mms matching after mms message parser is added in sl4a. b/34276948
+def mms_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_payload,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ """Send MMS, receive MMS, and verify content and sender's number.
+
+ Send (several) MMS from droid_tx to droid_rx.
+ Verify MMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object..
+ ad_rx: Receiver's Android Device Object.
+ subid_tx: Sender's subsciption ID to be used for SMS
+ subid_rx: Receiver's subsciption ID to be used for SMS
+ array_message: the array of message to send/receive
+ """
+
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+ toggle_enforce = False
+
+ for ad in (ad_tx, ad_rx):
+ ad.send_keycode("BACK")
+ if "Permissive" not in ad.adb.shell("su root getenforce"):
+ ad.adb.shell("su root setenforce 0")
+ toggle_enforce = True
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ ad.messaging_ed.clear_all_events()
+ ad.messaging_droid.logI(
+ "Start mms_send_receive_verify_for_subscription test")
+ except Exception:
+ ad.log.info("Create new sl4a session for messaging")
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
+ for subject, message, filename in array_payload:
+ ad_tx.messaging_ed.clear_events(EventMmsSentSuccess)
+ ad_tx.messaging_ed.clear_events(EventMmsSentFailure)
+ ad_rx.messaging_ed.clear_events(EventMmsDownloaded)
+ ad_rx.messaging_droid.smsStartTrackingIncomingMmsMessage()
+ ad_tx.log.info(
+ "Sending MMS from %s to %s, subject: %s, message: %s, file: %s.",
+ phonenumber_tx, phonenumber_rx, subject, message, filename)
+ try:
+ ad_tx.messaging_droid.smsSendMultimediaMessage(
+ phonenumber_rx, subject, message, phonenumber_tx, filename)
+ try:
+ events = ad_tx.messaging_ed.pop_events(
+ "(%s|%s)" % (EventMmsSentSuccess,
+ EventMmsSentFailure), max_wait_time)
+ for event in events:
+ ad_tx.log.info("Got event %s", event["name"])
+ if event["name"] == EventMmsSentFailure:
+ if event.get("data") and event["data"].get("Reason"):
+ ad_tx.log.error("%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ return False
+ elif event["name"] == EventMmsSentSuccess:
+ break
+ except Empty:
+ ad_tx.log.warning("No %s or %s event.", EventMmsSentSuccess,
+ EventMmsSentFailure)
+ return False
+
+ if not wait_for_matching_mms(log, ad_rx, phonenumber_tx,
+ message, max_wait_time):
+ return False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingMmsMessage()
+ for ad in (ad_tx, ad_rx):
+ if toggle_enforce:
+ ad.send_keycode("BACK")
+ ad.adb.shell("su root setenforce 1")
+ return True
+
+
+def mms_receive_verify_after_call_hangup(
+ log, ad_tx, ad_rx, array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ """Verify the suspanded MMS during call will send out after call release.
+
+ Hangup call from droid_tx to droid_rx.
+ Verify MMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object
+ ad_rx: Receiver's Android Device Object
+ array_message: the array of message to send/receive
+ """
+ return mms_receive_verify_after_call_hangup_for_subscription(
+ log, ad_tx, ad_rx, get_outgoing_message_sub_id(ad_tx),
+ get_incoming_message_sub_id(ad_rx), array_message, max_wait_time)
+
+
+#TODO: add mms matching after mms message parser is added in sl4a. b/34276948
+def mms_receive_verify_after_call_hangup_for_subscription(
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_payload,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ """Verify the suspanded MMS during call will send out after call release.
+
+ Hangup call from droid_tx to droid_rx.
+ Verify MMS is sent, delivered and received.
+ Verify received content and sender's number are correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object..
+ ad_rx: Receiver's Android Device Object.
+ subid_tx: Sender's subsciption ID to be used for SMS
+ subid_rx: Receiver's subsciption ID to be used for SMS
+ array_message: the array of message to send/receive
+ """
+
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+ for ad in (ad_tx, ad_rx):
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ for subject, message, filename in array_payload:
+ ad_rx.log.info(
+ "Waiting MMS from %s to %s, subject: %s, message: %s, file: %s.",
+ phonenumber_tx, phonenumber_rx, subject, message, filename)
+ ad_rx.messaging_droid.smsStartTrackingIncomingMmsMessage()
+ time.sleep(5)
+ try:
+ hangup_call(log, ad_tx)
+ hangup_call(log, ad_rx)
+ try:
+ ad_tx.messaging_ed.pop_event(EventMmsSentSuccess,
+ max_wait_time)
+ ad_tx.log.info("Got event %s", EventMmsSentSuccess)
+ except Empty:
+ log.warning("No sent_success event.")
+ if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message):
+ return False
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingMmsMessage()
+ return True
+
+
+def ensure_preferred_network_type_for_subscription(
+ ad,
+ network_preference
+ ):
+ sub_id = ad.droid.subscriptionGetDefaultSubId()
+ if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id):
+ ad.log.error("Set sub_id %s Preferred Networks Type %s failed.",
+ sub_id, network_preference)
+ return True
+
+
+def ensure_network_rat(log,
+ ad,
+ network_preference,
+ rat_family,
+ voice_or_data=None,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ toggle_apm_after_setting=False):
+ """Ensure ad's current network is in expected rat_family.
+ """
+ return ensure_network_rat_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ rat_family, voice_or_data, max_wait_time, toggle_apm_after_setting)
+
+
+def ensure_network_rat_for_subscription(
+ log,
+ ad,
+ sub_id,
+ network_preference,
+ rat_family,
+ voice_or_data=None,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ toggle_apm_after_setting=False):
+ """Ensure ad's current network is in expected rat_family.
+ """
+ if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id):
+ ad.log.error("Set sub_id %s Preferred Networks Type %s failed.",
+ sub_id, network_preference)
+ return False
+ if is_droid_in_rat_family_for_subscription(log, ad, sub_id, rat_family,
+ voice_or_data):
+ ad.log.info("Sub_id %s in RAT %s for %s", sub_id, rat_family,
+ voice_or_data)
+ return True
+
+ if toggle_apm_after_setting:
+ toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ toggle_airplane_mode(log, ad, new_state=None, strict_checking=False)
+
+ result = wait_for_network_rat_for_subscription(
+ log, ad, sub_id, rat_family, max_wait_time, voice_or_data)
+
+ log.info(
+ "End of ensure_network_rat_for_subscription for %s. "
+ "Setting to %s, Expecting %s %s. Current: voice: %s(family: %s), "
+ "data: %s(family: %s)", ad.serial, network_preference, rat_family,
+ voice_or_data,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+ rat_family_from_rat(
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+ sub_id)),
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+ rat_family_from_rat(
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
+ sub_id)))
+ return result
+
+
+def ensure_network_preference(log,
+ ad,
+ network_preference,
+ voice_or_data=None,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ toggle_apm_after_setting=False):
+ """Ensure that current rat is within the device's preferred network rats.
+ """
+ return ensure_network_preference_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ voice_or_data, max_wait_time, toggle_apm_after_setting)
+
+
+def ensure_network_preference_for_subscription(
+ log,
+ ad,
+ sub_id,
+ network_preference,
+ voice_or_data=None,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ toggle_apm_after_setting=False):
+ """Ensure ad's network preference is <network_preference> for sub_id.
+ """
+ rat_family_list = rat_families_for_network_preference(network_preference)
+ if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id):
+ log.error("Set Preferred Networks failed.")
+ return False
+ if is_droid_in_rat_family_list_for_subscription(
+ log, ad, sub_id, rat_family_list, voice_or_data):
+ return True
+
+ if toggle_apm_after_setting:
+ toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ toggle_airplane_mode(log, ad, new_state=False, strict_checking=False)
+
+ result = wait_for_preferred_network_for_subscription(
+ log, ad, sub_id, network_preference, max_wait_time, voice_or_data)
+
+ ad.log.info(
+ "End of ensure_network_preference_for_subscription. "
+ "Setting to %s, Expecting %s %s. Current: voice: %s(family: %s), "
+ "data: %s(family: %s)", network_preference, rat_family_list,
+ voice_or_data,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+ rat_family_from_rat(
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+ sub_id)),
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+ rat_family_from_rat(
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
+ sub_id)))
+ return result
+
+
+def ensure_network_generation(log,
+ ad,
+ generation,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None,
+ toggle_apm_after_setting=False):
+ """Ensure ad's network is <network generation> for default subscription ID.
+
+ Set preferred network generation to <generation>.
+ Toggle ON/OFF airplane mode if necessary.
+ Wait for ad in expected network type.
+ """
+ return ensure_network_generation_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
+ max_wait_time, voice_or_data, toggle_apm_after_setting)
+
+
+def ensure_network_generation_for_subscription(
+ log,
+ ad,
+ sub_id,
+ generation,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None,
+ toggle_apm_after_setting=False):
+ """Ensure ad's network is <network generation> for specified subscription ID.
+
+ Set preferred network generation to <generation>.
+ Toggle ON/OFF airplane mode if necessary.
+ Wait for ad in expected network type.
+ """
+ ad.log.info(
+ "RAT network type voice: %s, data: %s",
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id))
+
+ try:
+ ad.log.info("Finding the network preference for generation %s for "
+ "operator %s phone type %s", generation,
+ ad.telephony["subscription"][sub_id]["operator"],
+ ad.telephony["subscription"][sub_id]["phone_type"])
+ network_preference = network_preference_for_generation(
+ generation, ad.telephony["subscription"][sub_id]["operator"],
+ ad.telephony["subscription"][sub_id]["phone_type"])
+ if ad.telephony["subscription"][sub_id]["operator"] == CARRIER_FRE \
+ and generation == GEN_4G:
+ network_preference = NETWORK_MODE_LTE_ONLY
+ ad.log.info("Network preference for %s is %s", generation,
+ network_preference)
+ rat_family = rat_family_for_generation(
+ generation, ad.telephony["subscription"][sub_id]["operator"],
+ ad.telephony["subscription"][sub_id]["phone_type"])
+ except KeyError as e:
+ ad.log.error("Failed to find a rat_family entry for generation %s"
+ " for subscriber id %s with error %s", generation,
+ sub_id, e)
+ return False
+
+ if not set_preferred_network_mode_pref(log, ad, sub_id,
+ network_preference):
+ return False
+
+ if hasattr(ad, "dsds") and voice_or_data == "data" and sub_id != get_default_data_sub_id(ad):
+ ad.log.info("MSIM - Non DDS, ignore data RAT")
+ return True
+
+ if is_droid_in_network_generation_for_subscription(
+ log, ad, sub_id, generation, voice_or_data):
+ return True
+
+ if toggle_apm_after_setting:
+ toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ toggle_airplane_mode(log, ad, new_state=False, strict_checking=False)
+
+ result = wait_for_network_generation_for_subscription(
+ log, ad, sub_id, generation, max_wait_time, voice_or_data)
+
+ ad.log.info(
+ "Ensure network %s %s %s. With network preference %s, "
+ "current: voice: %s(family: %s), data: %s(family: %s)", generation,
+ voice_or_data, result, network_preference,
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+ rat_generation_from_rat(
+ ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+ sub_id)),
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+ rat_generation_from_rat(
+ ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
+ sub_id)))
+ if not result:
+ get_telephony_signal_strength(ad)
+ return result
+
+
+def wait_for_network_rat(log,
+ ad,
+ rat_family,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return wait_for_network_rat_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ max_wait_time, voice_or_data)
+
+
+def wait_for_network_rat_for_subscription(
+ log,
+ ad,
+ sub_id,
+ rat_family,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_wait_time,
+ is_droid_in_rat_family_for_subscription, rat_family, voice_or_data)
+
+
+def wait_for_not_network_rat(log,
+ ad,
+ rat_family,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return wait_for_not_network_rat_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ max_wait_time, voice_or_data)
+
+
+def wait_for_not_network_rat_for_subscription(
+ log,
+ ad,
+ sub_id,
+ rat_family,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_wait_time,
+ lambda log, ad, sub_id, *args, **kwargs: not is_droid_in_rat_family_for_subscription(log, ad, sub_id, rat_family, voice_or_data)
+ )
+
+
+def wait_for_preferred_network(log,
+ ad,
+ network_preference,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return wait_for_preferred_network_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ max_wait_time, voice_or_data)
+
+
+def wait_for_preferred_network_for_subscription(
+ log,
+ ad,
+ sub_id,
+ network_preference,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ rat_family_list = rat_families_for_network_preference(network_preference)
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_wait_time,
+ is_droid_in_rat_family_list_for_subscription, rat_family_list,
+ voice_or_data)
+
+
+def wait_for_network_generation(log,
+ ad,
+ generation,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return wait_for_network_generation_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
+ max_wait_time, voice_or_data)
+
+
+def wait_for_network_generation_for_subscription(
+ log,
+ ad,
+ sub_id,
+ generation,
+ max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+ voice_or_data=None):
+ return _wait_for_droid_in_state_for_subscription(
+ log, ad, sub_id, max_wait_time,
+ is_droid_in_network_generation_for_subscription, generation,
+ voice_or_data)
+
+
+def is_droid_in_rat_family(log, ad, rat_family, voice_or_data=None):
+ return is_droid_in_rat_family_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ voice_or_data)
+
+
+def is_droid_in_rat_family_for_subscription(log,
+ ad,
+ sub_id,
+ rat_family,
+ voice_or_data=None):
+ return is_droid_in_rat_family_list_for_subscription(
+ log, ad, sub_id, [rat_family], voice_or_data)
+
+
+def is_droid_in_rat_familiy_list(log, ad, rat_family_list, voice_or_data=None):
+ return is_droid_in_rat_family_list_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family_list,
+ voice_or_data)
+
+
+def is_droid_in_rat_family_list_for_subscription(log,
+ ad,
+ sub_id,
+ rat_family_list,
+ voice_or_data=None):
+ service_list = [NETWORK_SERVICE_DATA, NETWORK_SERVICE_VOICE]
+ if voice_or_data:
+ service_list = [voice_or_data]
+
+ for service in service_list:
+ nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
+ if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
+ continue
+ if rat_family_from_rat(nw_rat) in rat_family_list:
+ return True
+ return False
+
+
+def is_droid_in_network_generation(log, ad, nw_gen, voice_or_data):
+ """Checks if a droid in expected network generation ("2g", "3g" or "4g").
+
+ Args:
+ log: log object.
+ ad: android device.
+ nw_gen: expected generation "4g", "3g", "2g".
+ voice_or_data: check voice network generation or data network generation
+ This parameter is optional. If voice_or_data is None, then if
+ either voice or data in expected generation, function will return True.
+
+ Returns:
+ True if droid in expected network generation. Otherwise False.
+ """
+ return is_droid_in_network_generation_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), nw_gen, voice_or_data)
+
+
+def is_droid_in_network_generation_for_subscription(log, ad, sub_id, nw_gen,
+ voice_or_data):
+ """Checks if a droid in expected network generation ("2g", "3g" or "4g").
+
+ Args:
+ log: log object.
+ ad: android device.
+ nw_gen: expected generation "4g", "3g", "2g".
+ voice_or_data: check voice network generation or data network generation
+ This parameter is optional. If voice_or_data is None, then if
+ either voice or data in expected generation, function will return True.
+
+ Returns:
+ True if droid in expected network generation. Otherwise False.
+ """
+ service_list = [NETWORK_SERVICE_DATA, NETWORK_SERVICE_VOICE]
+
+ if voice_or_data:
+ service_list = [voice_or_data]
+
+ for service in service_list:
+ nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
+ ad.log.info("%s network rat is %s", service, nw_rat)
+ if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
+ continue
+
+ if rat_generation_from_rat(nw_rat) == nw_gen:
+ ad.log.info("%s network rat %s is expected %s", service, nw_rat,
+ nw_gen)
+ return True
+ else:
+ ad.log.info("%s network rat %s is %s, does not meet expected %s",
+ service, nw_rat, rat_generation_from_rat(nw_rat),
+ nw_gen)
+ return False
+
+ return False
+
+
+def get_network_rat(log, ad, voice_or_data):
+ """Get current network type (Voice network type, or data network type)
+ for default subscription id
+
+ Args:
+ ad: Android Device Object
+ voice_or_data: Input parameter indicating to get voice network type or
+ data network type.
+
+ Returns:
+ Current voice/data network type.
+ """
+ return get_network_rat_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+
+def get_network_rat_for_subscription(log, ad, sub_id, voice_or_data):
+ """Get current network type (Voice network type, or data network type)
+ for specified subscription id
+
+ Args:
+ ad: Android Device Object
+ sub_id: subscription ID
+ voice_or_data: Input parameter indicating to get voice network type or
+ data network type.
+
+ Returns:
+ Current voice/data network type.
+ """
+ if voice_or_data == NETWORK_SERVICE_VOICE:
+ ret_val = ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+ sub_id)
+ elif voice_or_data == NETWORK_SERVICE_DATA:
+ ret_val = ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
+ sub_id)
+ else:
+ ret_val = ad.droid.telephonyGetNetworkTypeForSubscription(sub_id)
+
+ if ret_val is None:
+ log.error("get_network_rat(): Unexpected null return value")
+ return RAT_UNKNOWN
+ else:
+ return ret_val
+
+
+def get_network_gen(log, ad, voice_or_data):
+ """Get current network generation string (Voice network type, or data network type)
+
+ Args:
+ ad: Android Device Object
+ voice_or_data: Input parameter indicating to get voice network generation
+ or data network generation.
+
+ Returns:
+ Current voice/data network generation.
+ """
+ return get_network_gen_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+
+def get_network_gen_for_subscription(log, ad, sub_id, voice_or_data):
+ """Get current network generation string (Voice network type, or data network type)
+
+ Args:
+ ad: Android Device Object
+ voice_or_data: Input parameter indicating to get voice network generation
+ or data network generation.
+
+ Returns:
+ Current voice/data network generation.
+ """
+ try:
+ return rat_generation_from_rat(
+ get_network_rat_for_subscription(log, ad, sub_id, voice_or_data))
+ except KeyError as e:
+ ad.log.error("KeyError %s", e)
+ return GEN_UNKNOWN
+
+
+def check_voice_mail_count(log, ad, voice_mail_count_before,
+ voice_mail_count_after):
+ """function to check if voice mail count is correct after leaving a new voice message.
+ """
+ return get_voice_mail_count_check_function(get_operator_name(log, ad))(
+ voice_mail_count_before, voice_mail_count_after)
+
+
+def get_voice_mail_number(log, ad):
+ """function to get the voice mail number
+ """
+ voice_mail_number = get_voice_mail_check_number(get_operator_name(log, ad))
+ if voice_mail_number is None:
+ return get_phone_number(log, ad)
+ return voice_mail_number
+
+
+def ensure_phones_idle(log, ads, max_time=MAX_WAIT_TIME_CALL_DROP):
+ """Ensure ads idle (not in call).
+ """
+ result = True
+ for ad in ads:
+ if not ensure_phone_idle(log, ad, max_time=max_time):
+ result = False
+ return result
+
+
+def ensure_phone_idle(log, ad, max_time=MAX_WAIT_TIME_CALL_DROP, retry=2):
+ """Ensure ad idle (not in call).
+ """
+ while ad.droid.telecomIsInCall() and retry > 0:
+ ad.droid.telecomEndCall()
+ time.sleep(3)
+ retry -= 1
+ if not wait_for_droid_not_in_call(log, ad, max_time=max_time):
+ ad.log.error("Failed to end call")
+ return False
+ return True
+
+
+def ensure_phone_subscription(log, ad):
+ """Ensure Phone Subscription.
+ """
+ #check for sim and service
+ duration = 0
+ while duration < MAX_WAIT_TIME_NW_SELECTION:
+ subInfo = ad.droid.subscriptionGetAllSubInfoList()
+ if subInfo and len(subInfo) >= 1:
+ ad.log.debug("Find valid subcription %s", subInfo)
+ break
+ else:
+ ad.log.info("Did not find any subscription")
+ time.sleep(5)
+ duration += 5
+ else:
+ ad.log.error("Unable to find a valid subscription!")
+ return False
+ while duration < MAX_WAIT_TIME_NW_SELECTION:
+ data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
+ voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ if data_sub_id > INVALID_SUB_ID or voice_sub_id > INVALID_SUB_ID:
+ ad.log.debug("Find valid voice or data sub id")
+ break
+ else:
+ ad.log.info("Did not find valid data or voice sub id")
+ time.sleep(5)
+ duration += 5
+ else:
+ ad.log.error("Unable to find valid data or voice sub id")
+ return False
+ while duration < MAX_WAIT_TIME_NW_SELECTION:
+ data_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+ if data_sub_id > INVALID_SUB_ID:
+ data_rat = get_network_rat_for_subscription(
+ log, ad, data_sub_id, NETWORK_SERVICE_DATA)
+ else:
+ data_rat = RAT_UNKNOWN
+ if voice_sub_id > INVALID_SUB_ID:
+ voice_rat = get_network_rat_for_subscription(
+ log, ad, voice_sub_id, NETWORK_SERVICE_VOICE)
+ else:
+ voice_rat = RAT_UNKNOWN
+ if data_rat != RAT_UNKNOWN or voice_rat != RAT_UNKNOWN:
+ ad.log.info("Data sub_id %s in %s, voice sub_id %s in %s",
+ data_sub_id, data_rat, voice_sub_id, voice_rat)
+ return True
+ else:
+ ad.log.info("Did not attach for data or voice service")
+ time.sleep(5)
+ duration += 5
+ else:
+ ad.log.error("Did not attach for voice or data service")
+ return False
+
+
+def ensure_phone_default_state(log, ad, check_subscription=True, retry=2):
+ """Ensure ad in default state.
+ Phone not in call.
+ Phone have no stored WiFi network and WiFi disconnected.
+ Phone not in airplane mode.
+ """
+ result = True
+ if not toggle_airplane_mode(log, ad, False, False):
+ ad.log.error("Fail to turn off airplane mode")
+ result = False
+ try:
+ set_wifi_to_default(log, ad)
+ while ad.droid.telecomIsInCall() and retry > 0:
+ ad.droid.telecomEndCall()
+ time.sleep(3)
+ retry -= 1
+ if not wait_for_droid_not_in_call(log, ad):
+ ad.log.error("Failed to end call")
+ #ad.droid.telephonyFactoryReset()
+ data_roaming = getattr(ad, 'roaming', False)
+ if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
+ set_cell_data_roaming_state_by_adb(ad, data_roaming)
+ remove_mobile_data_usage_limit(ad)
+ if not wait_for_not_network_rat(
+ log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
+ ad.log.error("%s still in %s", NETWORK_SERVICE_DATA,
+ RAT_FAMILY_WLAN)
+ result = False
+
+ if check_subscription and not ensure_phone_subscription(log, ad):
+ ad.log.error("Unable to find a valid subscription!")
+ result = False
+ except Exception as e:
+ ad.log.error("%s failure, toggle APM instead", e)
+ toggle_airplane_mode_by_adb(log, ad, True)
+ toggle_airplane_mode_by_adb(log, ad, False)
+ ad.send_keycode("ENDCALL")
+ ad.adb.shell("settings put global wfc_ims_enabled 0")
+ ad.adb.shell("settings put global mobile_data 1")
+
+ return result
+
+
+def ensure_phones_default_state(log, ads, check_subscription=True):
+ """Ensure ads in default state.
+ Phone not in call.
+ Phone have no stored WiFi network and WiFi disconnected.
+ Phone not in airplane mode.
+
+ Returns:
+ True if all steps of restoring default state succeed.
+ False if any of the steps to restore default state fails.
+ """
+ tasks = []
+ for ad in ads:
+ tasks.append((ensure_phone_default_state, (log, ad,
+ check_subscription)))
+ if not multithread_func(log, tasks):
+ log.error("Ensure_phones_default_state Fail.")
+ return False
+ return True
+
+
+def check_is_wifi_connected(log, ad, wifi_ssid):
+ """Check if ad is connected to wifi wifi_ssid.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ wifi_ssid: WiFi network SSID.
+
+ Returns:
+ True if wifi is connected to wifi_ssid
+ False if wifi is not connected to wifi_ssid
+ """
+ wifi_info = ad.droid.wifiGetConnectionInfo()
+ if wifi_info["supplicant_state"] == "completed" and wifi_info["SSID"] == wifi_ssid:
+ ad.log.info("Wifi is connected to %s", wifi_ssid)
+ ad.on_mobile_data = False
+ return True
+ else:
+ ad.log.info("Wifi is not connected to %s", wifi_ssid)
+ ad.log.debug("Wifi connection_info=%s", wifi_info)
+ ad.on_mobile_data = True
+ return False
+
+
+def ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd=None, retries=3, apm=False):
+ """Ensure ad connected to wifi on network wifi_ssid.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ wifi_ssid: WiFi network SSID.
+ wifi_pwd: optional secure network password.
+ retries: the number of retries.
+
+ Returns:
+ True if wifi is connected to wifi_ssid
+ False if wifi is not connected to wifi_ssid
+ """
+ if not toggle_airplane_mode(log, ad, apm, strict_checking=False):
+ return False
+
+ network = {WIFI_SSID_KEY: wifi_ssid}
+ if wifi_pwd:
+ network[WIFI_PWD_KEY] = wifi_pwd
+ for i in range(retries):
+ if not ad.droid.wifiCheckState():
+ ad.log.info("Wifi state is down. Turn on Wifi")
+ ad.droid.wifiToggleState(True)
+ if check_is_wifi_connected(log, ad, wifi_ssid):
+ ad.log.info("Wifi is connected to %s", wifi_ssid)
+ return verify_internet_connection(log, ad, retries=3)
+ else:
+ ad.log.info("Connecting to wifi %s", wifi_ssid)
+ try:
+ ad.droid.wifiConnectByConfig(network)
+ except Exception:
+ ad.log.info("Connecting to wifi by wifiConnect instead")
+ ad.droid.wifiConnect(network)
+ time.sleep(20)
+ if check_is_wifi_connected(log, ad, wifi_ssid):
+ ad.log.info("Connected to Wifi %s", wifi_ssid)
+ return verify_internet_connection(log, ad, retries=3)
+ ad.log.info("Fail to connected to wifi %s", wifi_ssid)
+ return False
+
+
+def forget_all_wifi_networks(log, ad):
+ """Forget all stored wifi network information
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ if not ad.droid.wifiGetConfiguredNetworks():
+ ad.on_mobile_data = True
+ return True
+ try:
+ old_state = ad.droid.wifiCheckState()
+ wifi_test_utils.reset_wifi(ad)
+ wifi_toggle_state(log, ad, old_state)
+ except Exception as e:
+ log.error("forget_all_wifi_networks with exception: %s", e)
+ return False
+ ad.on_mobile_data = True
+ return True
+
+
+def wifi_reset(log, ad, disable_wifi=True):
+ """Forget all stored wifi networks and (optionally) disable WiFi
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+ disable_wifi: boolean to disable wifi, defaults to True
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ if not forget_all_wifi_networks(log, ad):
+ ad.log.error("Unable to forget all networks")
+ return False
+ if not wifi_toggle_state(log, ad, not disable_wifi):
+ ad.log.error("Failed to toggle WiFi state to %s!", not disable_wifi)
+ return False
+ return True
+
+
+def set_wifi_to_default(log, ad):
+ """Set wifi to default state (Wifi disabled and no configured network)
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ ad.droid.wifiFactoryReset()
+ ad.droid.wifiToggleState(False)
+ ad.on_mobile_data = True
+
+
+def wifi_toggle_state(log, ad, state, retries=3):
+ """Toggle the WiFi State
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+ state: True, False, or None
+
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ for i in range(retries):
+ if wifi_test_utils.wifi_toggle_state(ad, state, assert_on_fail=False):
+ ad.on_mobile_data = not state
+ return True
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ return False
+
+
+def start_wifi_tethering(log, ad, ssid, password, ap_band=None):
+ """Start a Tethering Session
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+ ssid: the name of the WiFi network
+ password: optional password, used for secure networks.
+ ap_band=DEPRECATED specification of 2G or 5G tethering
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ return wifi_test_utils._assert_on_fail_handler(
+ wifi_test_utils.start_wifi_tethering,
+ False,
+ ad,
+ ssid,
+ password,
+ band=ap_band)
+
+
+def stop_wifi_tethering(log, ad):
+ """Stop a Tethering Session
+
+ Args:
+ log: log object
+ ad: AndroidDevice object
+ Returns:
+ boolean success (True) or failure (False)
+ """
+ return wifi_test_utils._assert_on_fail_handler(
+ wifi_test_utils.stop_wifi_tethering, False, ad)
+
+
+def reset_preferred_network_type_to_allowable_range(log, ad):
+ """If preferred network type is not in allowable range, reset to GEN_4G
+ preferred network type.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ None
+ """
+ for sub_id, sub_info in ad.telephony["subscription"].items():
+ current_preference = \
+ ad.droid.telephonyGetPreferredNetworkTypesForSubscription(sub_id)
+ ad.log.debug("sub_id network preference is %s", current_preference)
+ try:
+ if current_preference not in get_allowable_network_preference(
+ sub_info["operator"], sub_info["phone_type"]):
+ network_preference = network_preference_for_generation(
+ GEN_4G, sub_info["operator"], sub_info["phone_type"])
+ ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id)
+ except KeyError:
+ pass
+
+
+def task_wrapper(task):
+ """Task wrapper for multithread_func
+
+ Args:
+ task[0]: function to be wrapped.
+ task[1]: function args.
+
+ Returns:
+ Return value of wrapped function call.
+ """
+ func = task[0]
+ params = task[1]
+ return func(*params)
+
+
+def run_multithread_func_async(log, task):
+ """Starts a multi-threaded function asynchronously.
+
+ Args:
+ log: log object.
+ task: a task to be executed in parallel.
+
+ Returns:
+ Future object representing the execution of the task.
+ """
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
+ try:
+ future_object = executor.submit(task_wrapper, task)
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ return future_object
+
+
+def run_multithread_func(log, tasks):
+ """Run multi-thread functions and return results.
+
+ Args:
+ log: log object.
+ tasks: a list of tasks to be executed in parallel.
+
+ Returns:
+ results for tasks.
+ """
+ MAX_NUMBER_OF_WORKERS = 10
+ number_of_workers = min(MAX_NUMBER_OF_WORKERS, len(tasks))
+ executor = concurrent.futures.ThreadPoolExecutor(
+ max_workers=number_of_workers)
+ if not log: log = logging
+ try:
+ results = list(executor.map(task_wrapper, tasks))
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ executor.shutdown()
+ if log:
+ log.info("multithread_func %s result: %s",
+ [task[0].__name__ for task in tasks], results)
+ return results
+
+
+def multithread_func(log, tasks):
+ """Multi-thread function wrapper.
+
+ Args:
+ log: log object.
+ tasks: tasks to be executed in parallel.
+
+ Returns:
+ True if all tasks return True.
+ False if any task return False.
+ """
+ results = run_multithread_func(log, tasks)
+ for r in results:
+ if not r:
+ return False
+ return True
+
+
+def multithread_func_and_check_results(log, tasks, expected_results):
+ """Multi-thread function wrapper.
+
+ Args:
+ log: log object.
+ tasks: tasks to be executed in parallel.
+ expected_results: check if the results from tasks match expected_results.
+
+ Returns:
+ True if expected_results are met.
+ False if expected_results are not met.
+ """
+ return_value = True
+ results = run_multithread_func(log, tasks)
+ log.info("multithread_func result: %s, expecting %s", results,
+ expected_results)
+ for task, result, expected_result in zip(tasks, results, expected_results):
+ if result != expected_result:
+ logging.info("Result for task %s is %s, expecting %s", task[0],
+ result, expected_result)
+ return_value = False
+ return return_value
+
+
+def set_phone_screen_on(log, ad, screen_on_time=MAX_SCREEN_ON_TIME):
+ """Set phone screen on time.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ screen_on_time: screen on time.
+ This is optional, default value is MAX_SCREEN_ON_TIME.
+ Returns:
+ True if set successfully.
+ """
+ ad.droid.setScreenTimeout(screen_on_time)
+ return screen_on_time == ad.droid.getScreenTimeout()
+
+
+def set_phone_silent_mode(log, ad, silent_mode=True):
+ """Set phone silent mode.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ silent_mode: set phone silent or not.
+ This is optional, default value is True (silent mode on).
+ Returns:
+ True if set successfully.
+ """
+ ad.droid.toggleRingerSilentMode(silent_mode)
+ ad.droid.setMediaVolume(0)
+ ad.droid.setVoiceCallVolume(0)
+ ad.droid.setAlarmVolume(0)
+ ad.adb.ensure_root()
+ ad.adb.shell("setprop ro.audio.silent 1", ignore_status=True)
+ ad.adb.shell("cmd notification set_dnd on", ignore_status=True)
+ return silent_mode == ad.droid.checkRingerSilentMode()
+
+
+def set_preferred_network_mode_pref(log,
+ ad,
+ sub_id,
+ network_preference,
+ timeout=WAIT_TIME_ANDROID_STATE_SETTLING):
+ """Set Preferred Network Mode for Sub_id
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id: Subscription ID.
+ network_preference: Network Mode Type
+ """
+ begin_time = get_device_epoch_time(ad)
+ if ad.droid.telephonyGetPreferredNetworkTypesForSubscription(
+ sub_id) == network_preference:
+ ad.log.info("Current ModePref for Sub %s is in %s", sub_id,
+ network_preference)
+ return True
+ ad.log.info("Setting ModePref to %s for Sub %s", network_preference,
+ sub_id)
+ while timeout >= 0:
+ if ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id):
+ return True
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ timeout = timeout - WAIT_TIME_BETWEEN_STATE_CHECK
+ error_msg = "Failed to set sub_id %s PreferredNetworkType to %s" % (
+ sub_id, network_preference)
+ search_results = ad.search_logcat(
+ "REQUEST_SET_PREFERRED_NETWORK_TYPE error", begin_time=begin_time)
+ if search_results:
+ log_message = search_results[-1]["log_message"]
+ if "DEVICE_IN_USE" in log_message:
+ error_msg = "%s due to DEVICE_IN_USE" % error_msg
+ else:
+ error_msg = "%s due to %s" % (error_msg, log_message)
+ ad.log.error(error_msg)
+ return False
+
+
+def set_preferred_mode_for_5g(ad, sub_id=None, mode=None):
+ """Set Preferred Network Mode for 5G NSA
+ Args:
+ ad: Android device object.
+ sub_id: Subscription ID.
+ mode: 5G Network Mode Type
+ """
+ if sub_id is None:
+ sub_id = ad.droid.subscriptionGetDefaultSubId()
+ if mode is None:
+ mode = NETWORK_MODE_NR_LTE_GSM_WCDMA
+ return set_preferred_network_mode_pref(ad.log, ad, sub_id, mode)
+
+
+
+def set_preferred_subid_for_sms(log, ad, sub_id):
+ """set subscription id for SMS
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id :Subscription ID.
+
+ """
+ ad.log.info("Setting subscription %s as preferred SMS SIM", sub_id)
+ ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
+ # Wait to make sure settings take effect
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return sub_id == ad.droid.subscriptionGetDefaultSmsSubId()
+
+
+def set_preferred_subid_for_data(log, ad, sub_id):
+ """set subscription id for data
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id :Subscription ID.
+
+ """
+ ad.log.info("Setting subscription %s as preferred Data SIM", sub_id)
+ ad.droid.subscriptionSetDefaultDataSubId(sub_id)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ # Wait to make sure settings take effect
+ # Data SIM change takes around 1 min
+ # Check whether data has changed to selected sim
+ if not wait_for_data_connection(log, ad, True,
+ MAX_WAIT_TIME_DATA_SUB_CHANGE):
+ log.error("Data Connection failed - Not able to switch Data SIM")
+ return False
+ return True
+
+
+def set_preferred_subid_for_voice(log, ad, sub_id):
+ """set subscription id for voice
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id :Subscription ID.
+
+ """
+ ad.log.info("Setting subscription %s as Voice SIM", sub_id)
+ ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
+ ad.droid.telecomSetUserSelectedOutgoingPhoneAccountBySubId(sub_id)
+ # Wait to make sure settings take effect
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return True
+
+
+def set_call_state_listen_level(log, ad, value, sub_id):
+ """Set call state listen level for subscription id.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ value: True or False
+ sub_id :Subscription ID.
+
+ Returns:
+ True or False
+ """
+ if sub_id == INVALID_SUB_ID:
+ log.error("Invalid Subscription ID")
+ return False
+ ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
+ "Foreground", value, sub_id)
+ ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
+ "Ringing", value, sub_id)
+ ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
+ "Background", value, sub_id)
+ return True
+
+
+def setup_sim(log, ad, sub_id, voice=False, sms=False, data=False):
+ """set subscription id for voice, sms and data
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id :Subscription ID.
+ voice: True if to set subscription as default voice subscription
+ sms: True if to set subscription as default sms subscription
+ data: True if to set subscription as default data subscription
+
+ """
+ if sub_id == INVALID_SUB_ID:
+ log.error("Invalid Subscription ID")
+ return False
+ else:
+ if voice:
+ if not set_preferred_subid_for_voice(log, ad, sub_id):
+ return False
+ if sms:
+ if not set_preferred_subid_for_sms(log, ad, sub_id):
+ return False
+ if data:
+ if not set_preferred_subid_for_data(log, ad, sub_id):
+ return False
+ return True
+
+
+def is_event_match(event, field, value):
+ """Return if <field> in "event" match <value> or not.
+
+ Args:
+ event: event to test. This event need to have <field>.
+ field: field to match.
+ value: value to match.
+
+ Returns:
+ True if <field> in "event" match <value>.
+ False otherwise.
+ """
+ return is_event_match_for_list(event, field, [value])
+
+
+def is_event_match_for_list(event, field, value_list):
+ """Return if <field> in "event" match any one of the value
+ in "value_list" or not.
+
+ Args:
+ event: event to test. This event need to have <field>.
+ field: field to match.
+ value_list: a list of value to match.
+
+ Returns:
+ True if <field> in "event" match one of the value in "value_list".
+ False otherwise.
+ """
+ try:
+ value_in_event = event['data'][field]
+ except KeyError:
+ return False
+ for value in value_list:
+ if value_in_event == value:
+ return True
+ return False
+
+
+def is_network_call_back_event_match(event, network_callback_id,
+ network_callback_event):
+ try:
+ return (
+ (network_callback_id == event['data'][NetworkCallbackContainer.ID])
+ and (network_callback_event == event['data']
+ [NetworkCallbackContainer.NETWORK_CALLBACK_EVENT]))
+ except KeyError:
+ return False
+
+
+def is_build_id(log, ad, build_id):
+ """Return if ad's build id is the same as input parameter build_id.
+
+ Args:
+ log: log object.
+ ad: android device object.
+ build_id: android build id.
+
+ Returns:
+ True if ad's build id is the same as input parameter build_id.
+ False otherwise.
+ """
+ actual_bid = ad.droid.getBuildID()
+
+ ad.log.info("BUILD DISPLAY: %s", ad.droid.getBuildDisplay())
+ #In case we want to log more stuff/more granularity...
+ #log.info("{} BUILD ID:{} ".format(ad.serial, ad.droid.getBuildID()))
+ #log.info("{} BUILD FINGERPRINT: {} "
+ # .format(ad.serial), ad.droid.getBuildFingerprint())
+ #log.info("{} BUILD TYPE: {} "
+ # .format(ad.serial), ad.droid.getBuildType())
+ #log.info("{} BUILD NUMBER: {} "
+ # .format(ad.serial), ad.droid.getBuildNumber())
+ if actual_bid.upper() != build_id.upper():
+ ad.log.error("%s: Incorrect Build ID", ad.model)
+ return False
+ return True
+
+
+def is_uri_equivalent(uri1, uri2):
+ """Check whether two input uris match or not.
+
+ Compare Uris.
+ If Uris are tel URI, it will only take the digit part
+ and compare as phone number.
+ Else, it will just do string compare.
+
+ Args:
+ uri1: 1st uri to be compared.
+ uri2: 2nd uri to be compared.
+
+ Returns:
+ True if two uris match. Otherwise False.
+ """
+
+ #If either is None/empty we return false
+ if not uri1 or not uri2:
+ return False
+
+ try:
+ if uri1.startswith('tel:') and uri2.startswith('tel:'):
+ uri1_number = get_number_from_tel_uri(uri1)
+ uri2_number = get_number_from_tel_uri(uri2)
+ return check_phone_number_match(uri1_number, uri2_number)
+ else:
+ return uri1 == uri2
+ except AttributeError as e:
+ return False
+
+
+def get_call_uri(ad, call_id):
+ """Get call's uri field.
+
+ Get Uri for call_id in ad.
+
+ Args:
+ ad: android device object.
+ call_id: the call id to get Uri from.
+
+ Returns:
+ call's Uri if call is active and have uri field. None otherwise.
+ """
+ try:
+ call_detail = ad.droid.telecomCallGetDetails(call_id)
+ return call_detail["Handle"]["Uri"]
+ except:
+ return None
+
+
+def get_number_from_tel_uri(uri):
+ """Get Uri number from tel uri
+
+ Args:
+ uri: input uri
+
+ Returns:
+ If input uri is tel uri, return the number part.
+ else return None.
+ """
+ if uri.startswith('tel:'):
+ uri_number = ''.join(
+ i for i in urllib.parse.unquote(uri) if i.isdigit())
+ return uri_number
+ else:
+ return None
+
+
+def find_qxdm_log_mask(ad, mask="default.cfg"):
+ """Find QXDM logger mask."""
+ if "/" not in mask:
+ # Call nexuslogger to generate log mask
+ start_nexuslogger(ad)
+ # Find the log mask path
+ for path in (DEFAULT_QXDM_LOG_PATH, "/data/diag_logs",
+ "/vendor/etc/mdlog/"):
+ out = ad.adb.shell(
+ "find %s -type f -iname %s" % (path, mask), ignore_status=True)
+ if out and "No such" not in out and "Permission denied" not in out:
+ if path.startswith("/vendor/"):
+ setattr(ad, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH)
+ else:
+ setattr(ad, "qxdm_log_path", path)
+ return out.split("\n")[0]
+ if mask in ad.adb.shell("ls /vendor/etc/mdlog/"):
+ setattr(ad, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH)
+ return "%s/%s" % ("/vendor/etc/mdlog/", mask)
+ else:
+ out = ad.adb.shell("ls %s" % mask, ignore_status=True)
+ if out and "No such" not in out:
+ qxdm_log_path, cfg_name = os.path.split(mask)
+ setattr(ad, "qxdm_log_path", qxdm_log_path)
+ return mask
+ ad.log.warning("Could NOT find QXDM logger mask path for %s", mask)
+
+
+def set_qxdm_logger_command(ad, mask=None):
+ """Set QXDM logger always on.
+
+ Args:
+ ad: android device object.
+
+ """
+ ## Neet to check if log mask will be generated without starting nexus logger
+ masks = []
+ mask_path = None
+ if mask:
+ masks = [mask]
+ masks.extend(["QC_Default.cfg", "default.cfg"])
+ for mask in masks:
+ mask_path = find_qxdm_log_mask(ad, mask)
+ if mask_path: break
+ if not mask_path:
+ ad.log.error("Cannot find QXDM mask %s", mask)
+ ad.qxdm_logger_command = None
+ return False
+ else:
+ ad.log.info("Use QXDM log mask %s", mask_path)
+ ad.log.debug("qxdm_log_path = %s", ad.qxdm_log_path)
+ output_path = os.path.join(ad.qxdm_log_path, "logs")
+ ad.qxdm_logger_command = ("diag_mdlog -f %s -o %s -s 90 -c" %
+ (mask_path, output_path))
+ for prop in ("persist.sys.modem.diag.mdlog",
+ "persist.vendor.sys.modem.diag.mdlog"):
+ if ad.adb.getprop(prop):
+ # Enable qxdm always on if supported
+ for conf_path in ("/data/vendor/radio/diag_logs",
+ "/vendor/etc/mdlog"):
+ if "diag.conf" in ad.adb.shell(
+ "ls %s" % conf_path, ignore_status=True):
+ conf_path = "%s/diag.conf" % conf_path
+ ad.adb.shell('echo "%s" > %s' %
+ (ad.qxdm_logger_command, conf_path),
+ ignore_status=True)
+ break
+ ad.adb.shell("setprop %s true" % prop, ignore_status=True)
+ break
+ return True
+
+
+def start_sdm_logger(ad):
+ """Start SDM logger."""
+ if not getattr(ad, "sdm_log", True): return
+ # Delete existing SDM logs which were created 15 mins prior
+ ad.sdm_log_path = DEFAULT_SDM_LOG_PATH
+ file_count = ad.adb.shell(
+ "find %s -type f -iname sbuff_[0-9]*.sdm* | wc -l" % ad.sdm_log_path)
+ if int(file_count) > 3:
+ seconds = 15 * 60
+ # Remove sdm logs modified more than specified seconds ago
+ ad.adb.shell(
+ "find %s -type f -iname sbuff_[0-9]*.sdm* -not -mtime -%ss -delete" %
+ (ad.sdm_log_path, seconds))
+ # start logging
+ cmd = "setprop vendor.sys.modem.logging.enable true"
+ ad.log.debug("start sdm logging")
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(5)
+
+
+def stop_sdm_logger(ad):
+ """Stop SDM logger."""
+ cmd = "setprop vendor.sys.modem.logging.enable false"
+ ad.log.debug("stop sdm logging")
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(5)
+
+
+def stop_qxdm_logger(ad):
+ """Stop QXDM logger."""
+ for cmd in ("diag_mdlog -k", "killall diag_mdlog"):
+ output = ad.adb.shell("ps -ef | grep mdlog") or ""
+ if "diag_mdlog" not in output:
+ break
+ ad.log.debug("Kill the existing qxdm process")
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(5)
+
+
+def start_qxdm_logger(ad, begin_time=None):
+ """Start QXDM logger."""
+ if not getattr(ad, "qxdm_log", True): return
+ # Delete existing QXDM logs 5 minutes earlier than the begin_time
+ current_time = get_current_epoch_time()
+ if getattr(ad, "qxdm_log_path", None):
+ seconds = None
+ file_count = ad.adb.shell(
+ "find %s -type f -iname *.qmdl | wc -l" % ad.qxdm_log_path)
+ if int(file_count) > 50:
+ if begin_time:
+ # if begin_time specified, delete old qxdm logs modified
+ # 10 minutes before begin time
+ seconds = int((current_time - begin_time) / 1000.0) + 10 * 60
+ else:
+ # if begin_time is not specified, delete old qxdm logs modified
+ # 15 minutes before current time
+ seconds = 15 * 60
+ if seconds:
+ # Remove qxdm logs modified more than specified seconds ago
+ ad.adb.shell(
+ "find %s -type f -iname *.qmdl -not -mtime -%ss -delete" %
+ (ad.qxdm_log_path, seconds))
+ ad.adb.shell(
+ "find %s -type f -iname *.xml -not -mtime -%ss -delete" %
+ (ad.qxdm_log_path, seconds))
+ if getattr(ad, "qxdm_logger_command", None):
+ output = ad.adb.shell("ps -ef | grep mdlog") or ""
+ if ad.qxdm_logger_command not in output:
+ ad.log.debug("QXDM logging command %s is not running",
+ ad.qxdm_logger_command)
+ if "diag_mdlog" in output:
+ # Kill the existing non-matching diag_mdlog process
+ # Only one diag_mdlog process can be run
+ stop_qxdm_logger(ad)
+ ad.log.info("Start QXDM logger")
+ ad.adb.shell_nb(ad.qxdm_logger_command)
+ time.sleep(10)
+ else:
+ run_time = check_qxdm_logger_run_time(ad)
+ if run_time < 600:
+ # the last diag_mdlog started within 10 minutes ago
+ # no need to restart
+ return True
+ if ad.search_logcat(
+ "Diag_Lib: diag: In delete_log",
+ begin_time=current_time -
+ run_time) or not ad.get_file_names(
+ ad.qxdm_log_path,
+ begin_time=current_time - 600000,
+ match_string="*.qmdl"):
+ # diag_mdlog starts deleting files or no qmdl logs were
+ # modified in the past 10 minutes
+ ad.log.debug("Quit existing diag_mdlog and start a new one")
+ stop_qxdm_logger(ad)
+ ad.adb.shell_nb(ad.qxdm_logger_command)
+ time.sleep(10)
+ return True
+
+
+def disable_qxdm_logger(ad):
+ for prop in ("persist.sys.modem.diag.mdlog",
+ "persist.vendor.sys.modem.diag.mdlog",
+ "vendor.sys.modem.diag.mdlog_on"):
+ if ad.adb.getprop(prop):
+ ad.adb.shell("setprop %s false" % prop, ignore_status=True)
+ for apk in ("com.android.nexuslogger", "com.android.pixellogger"):
+ if ad.is_apk_installed(apk) and ad.is_apk_running(apk):
+ ad.force_stop_apk(apk)
+ stop_qxdm_logger(ad)
+ return True
+
+
+def check_qxdm_logger_run_time(ad):
+ output = ad.adb.shell("ps -eo etime,cmd | grep diag_mdlog")
+ result = re.search(r"(\d+):(\d+):(\d+) diag_mdlog", output)
+ if result:
+ return int(result.group(1)) * 60 * 60 + int(
+ result.group(2)) * 60 + int(result.group(3))
+ else:
+ result = re.search(r"(\d+):(\d+) diag_mdlog", output)
+ if result:
+ return int(result.group(1)) * 60 + int(result.group(2))
+ else:
+ return 0
+
+
+def start_qxdm_loggers(log, ads, begin_time=None):
+ tasks = [(start_qxdm_logger, [ad, begin_time]) for ad in ads
+ if getattr(ad, "qxdm_log", True)]
+ if tasks: run_multithread_func(log, tasks)
+
+
+def stop_qxdm_loggers(log, ads):
+ tasks = [(stop_qxdm_logger, [ad]) for ad in ads]
+ run_multithread_func(log, tasks)
+
+
+def start_sdm_loggers(log, ads):
+ tasks = [(start_sdm_logger, [ad]) for ad in ads
+ if getattr(ad, "sdm_log", True)]
+ if tasks: run_multithread_func(log, tasks)
+
+
+def stop_sdm_loggers(log, ads):
+ tasks = [(stop_sdm_logger, [ad]) for ad in ads]
+ run_multithread_func(log, tasks)
+
+
+def start_nexuslogger(ad):
+ """Start Nexus/Pixel Logger Apk."""
+ qxdm_logger_apk = None
+ for apk, activity in (("com.android.nexuslogger", ".MainActivity"),
+ ("com.android.pixellogger",
+ ".ui.main.MainActivity")):
+ if ad.is_apk_installed(apk):
+ qxdm_logger_apk = apk
+ break
+ if not qxdm_logger_apk: return
+ if ad.is_apk_running(qxdm_logger_apk):
+ if "granted=true" in ad.adb.shell(
+ "dumpsys package %s | grep WRITE_EXTERN" % qxdm_logger_apk):
+ return True
+ else:
+ ad.log.info("Kill %s" % qxdm_logger_apk)
+ ad.force_stop_apk(qxdm_logger_apk)
+ time.sleep(5)
+ for perm in ("READ", "WRITE"):
+ ad.adb.shell("pm grant %s android.permission.%s_EXTERNAL_STORAGE" %
+ (qxdm_logger_apk, perm))
+ time.sleep(2)
+ for i in range(3):
+ ad.unlock_screen()
+ ad.log.info("Start %s Attempt %d" % (qxdm_logger_apk, i + 1))
+ ad.adb.shell("am start -n %s/%s" % (qxdm_logger_apk, activity))
+ time.sleep(5)
+ if ad.is_apk_running(qxdm_logger_apk):
+ ad.send_keycode("HOME")
+ return True
+ return False
+
+
+def check_qxdm_logger_mask(ad, mask_file="QC_Default.cfg"):
+ """Check if QXDM logger always on is set.
+
+ Args:
+ ad: android device object.
+
+ """
+ output = ad.adb.shell(
+ "ls /data/vendor/radio/diag_logs/", ignore_status=True)
+ if not output or "No such" in output:
+ return True
+ if mask_file not in ad.adb.shell(
+ "cat /data/vendor/radio/diag_logs/diag.conf", ignore_status=True):
+ return False
+ return True
+
+
+def start_tcpdumps(ads,
+ test_name="",
+ begin_time=None,
+ interface="any",
+ mask="all"):
+ for ad in ads:
+ try:
+ start_adb_tcpdump(
+ ad,
+ test_name=test_name,
+ begin_time=begin_time,
+ interface=interface,
+ mask=mask)
+ except Exception as e:
+ ad.log.warning("Fail to start tcpdump due to %s", e)
+
+
+def start_adb_tcpdump(ad,
+ test_name="",
+ begin_time=None,
+ interface="any",
+ mask="all"):
+ """Start tcpdump on any iface
+
+ Args:
+ ad: android device object.
+ test_name: tcpdump file name will have this
+
+ """
+ out = ad.adb.shell("ls -l /data/local/tmp/tcpdump/", ignore_status=True)
+ if "No such file" in out or not out:
+ ad.adb.shell("mkdir /data/local/tmp/tcpdump")
+ else:
+ ad.adb.shell(
+ "find /data/local/tmp/tcpdump -type f -not -mtime -1800s -delete",
+ ignore_status=True)
+ ad.adb.shell(
+ "find /data/local/tmp/tcpdump -type f -size +5G -delete",
+ ignore_status=True)
+
+ if not begin_time:
+ begin_time = get_current_epoch_time()
+
+ out = ad.adb.shell(
+ 'ifconfig | grep -v -E "r_|-rmnet" | grep -E "lan|data"',
+ ignore_status=True,
+ timeout=180)
+ intfs = re.findall(r"(\S+).*", out)
+ if interface and interface not in ("any", "all"):
+ if interface not in intfs: return
+ intfs = [interface]
+
+ out = ad.adb.shell("ps -ef | grep tcpdump")
+ cmds = []
+ for intf in intfs:
+ if intf in out:
+ ad.log.info("tcpdump on interface %s is already running", intf)
+ continue
+ else:
+ log_file_name = "/data/local/tmp/tcpdump/tcpdump_%s_%s_%s_%s.pcap" \
+ % (ad.serial, intf, test_name, begin_time)
+ if mask == "ims":
+ cmds.append(
+ "adb -s %s shell tcpdump -i %s -s0 -n -p udp port 500 or "
+ "udp port 4500 -w %s" % (ad.serial, intf, log_file_name))
+ else:
+ cmds.append("adb -s %s shell tcpdump -i %s -s0 -w %s" %
+ (ad.serial, intf, log_file_name))
+ for cmd in cmds:
+ ad.log.info(cmd)
+ try:
+ start_standing_subprocess(cmd, 10)
+ except Exception as e:
+ ad.log.error(e)
+ if cmds:
+ time.sleep(5)
+
+
+def stop_tcpdumps(ads):
+ for ad in ads:
+ stop_adb_tcpdump(ad)
+
+
+def stop_adb_tcpdump(ad, interface="any"):
+ """Stops tcpdump on any iface
+ Pulls the tcpdump file in the tcpdump dir
+
+ Args:
+ ad: android device object.
+
+ """
+ if interface == "any":
+ try:
+ ad.adb.shell("killall -9 tcpdump", ignore_status=True)
+ except Exception as e:
+ ad.log.error("Killing tcpdump with exception %s", e)
+ else:
+ out = ad.adb.shell("ps -ef | grep tcpdump | grep %s" % interface)
+ if "tcpdump -i" in out:
+ pids = re.findall(r"\S+\s+(\d+).*tcpdump -i", out)
+ for pid in pids:
+ ad.adb.shell("kill -9 %s" % pid)
+ ad.adb.shell(
+ "find /data/local/tmp/tcpdump -type f -not -mtime -1800s -delete",
+ ignore_status=True)
+
+
+def get_tcpdump_log(ad, test_name="", begin_time=None):
+ """Stops tcpdump on any iface
+ Pulls the tcpdump file in the tcpdump dir
+ Zips all tcpdump files
+
+ Args:
+ ad: android device object.
+ test_name: test case name
+ begin_time: test begin time
+ """
+ logs = ad.get_file_names("/data/local/tmp/tcpdump", begin_time=begin_time)
+ if logs:
+ ad.log.info("Pulling tcpdumps %s", logs)
+ log_path = os.path.join(
+ ad.device_log_path, "TCPDUMP_%s_%s" % (ad.model, ad.serial))
+ os.makedirs(log_path, exist_ok=True)
+ ad.pull_files(logs, log_path)
+ shutil.make_archive(log_path, "zip", log_path)
+ shutil.rmtree(log_path)
+ return True
+
+
+def fastboot_wipe(ad, skip_setup_wizard=True):
+ """Wipe the device in fastboot mode.
+
+ Pull sl4a apk from device. Terminate all sl4a sessions,
+ Reboot the device to bootloader, wipe the device by fastboot.
+ Reboot the device. wait for device to complete booting
+ Re-intall and start an sl4a session.
+ """
+ status = True
+ # Pull sl4a apk from device
+ out = ad.adb.shell("pm path %s" % SL4A_APK_NAME)
+ result = re.search(r"package:(.*)", out)
+ if not result:
+ ad.log.error("Couldn't find sl4a apk")
+ else:
+ sl4a_apk = result.group(1)
+ ad.log.info("Get sl4a apk from %s", sl4a_apk)
+ ad.pull_files([sl4a_apk], "/tmp/")
+ ad.stop_services()
+ attemps = 3
+ for i in range(1, attemps + 1):
+ try:
+ if ad.serial in list_adb_devices():
+ ad.log.info("Reboot to bootloader")
+ ad.adb.reboot("bootloader", ignore_status=True)
+ time.sleep(10)
+ if ad.serial in list_fastboot_devices():
+ ad.log.info("Wipe in fastboot")
+ ad.fastboot._w(timeout=300, ignore_status=True)
+ time.sleep(30)
+ ad.log.info("Reboot in fastboot")
+ ad.fastboot.reboot()
+ ad.wait_for_boot_completion()
+ ad.root_adb()
+ if ad.skip_sl4a:
+ break
+ if ad.is_sl4a_installed():
+ break
+ ad.log.info("Re-install sl4a")
+ ad.adb.shell("settings put global verifier_verify_adb_installs 0")
+ ad.adb.install("-r /tmp/base.apk")
+ time.sleep(10)
+ break
+ except Exception as e:
+ ad.log.warning(e)
+ if i == attemps:
+ abort_all_tests(log, str(e))
+ time.sleep(5)
+ try:
+ ad.start_adb_logcat()
+ except:
+ ad.log.error("Failed to start adb logcat!")
+ if skip_setup_wizard:
+ ad.exit_setup_wizard()
+ if getattr(ad, "qxdm_log", True):
+ set_qxdm_logger_command(ad, mask=getattr(ad, "qxdm_log_mask", None))
+ start_qxdm_logger(ad)
+ if ad.skip_sl4a: return status
+ bring_up_sl4a(ad)
+ synchronize_device_time(ad)
+ set_phone_silent_mode(ad.log, ad)
+ # Activate WFC on Verizon, AT&T and Canada operators as per # b/33187374 &
+ # b/122327716
+ activate_wfc_on_device(ad.log, ad)
+ return status
+
+def install_carriersettings_apk(ad, carriersettingsapk, skip_setup_wizard=True):
+ """ Carrier Setting Installation Steps
+
+ Pull sl4a apk from device. Terminate all sl4a sessions,
+ Reboot the device to bootloader, wipe the device by fastboot.
+ Reboot the device. wait for device to complete booting
+ """
+ status = True
+ if carriersettingsapk is None:
+ ad.log.warning("CarrierSettingsApk is not provided, aborting")
+ return False
+ ad.log.info("Push carriersettings apk to the Android device.")
+ android_apk_path = "/product/priv-app/CarrierSettings/CarrierSettings.apk"
+ ad.adb.push("%s %s" % (carriersettingsapk, android_apk_path))
+ ad.stop_services()
+
+ attempts = 3
+ for i in range(1, attempts + 1):
+ try:
+ if ad.serial in list_adb_devices():
+ ad.log.info("Reboot to bootloader")
+ ad.adb.reboot("bootloader", ignore_status=True)
+ time.sleep(30)
+ if ad.serial in list_fastboot_devices():
+ ad.log.info("Reboot in fastboot")
+ ad.fastboot.reboot()
+ ad.wait_for_boot_completion()
+ ad.root_adb()
+ if ad.is_sl4a_installed():
+ break
+ time.sleep(10)
+ break
+ except Exception as e:
+ ad.log.warning(e)
+ if i == attempts:
+ abort_all_tests(log, str(e))
+ time.sleep(5)
+ try:
+ ad.start_adb_logcat()
+ except:
+ ad.log.error("Failed to start adb logcat!")
+ if skip_setup_wizard:
+ ad.exit_setup_wizard()
+ return status
+
+
+def bring_up_sl4a(ad, attemps=3):
+ for i in range(attemps):
+ try:
+ droid, ed = ad.get_droid()
+ ed.start()
+ ad.log.info("Brought up new sl4a session")
+ break
+ except Exception as e:
+ if i < attemps - 1:
+ ad.log.info(e)
+ time.sleep(10)
+ else:
+ ad.log.error(e)
+ raise
+
+
+def reboot_device(ad, recover_sim_state=True):
+ sim_state = is_sim_ready(ad.log, ad)
+ ad.reboot()
+ if ad.qxdm_log:
+ start_qxdm_logger(ad)
+ ad.unlock_screen()
+ if recover_sim_state:
+ if not unlock_sim(ad):
+ ad.log.error("Unable to unlock SIM")
+ return False
+ if sim_state and not _wait_for_droid_in_state(
+ log, ad, MAX_WAIT_TIME_FOR_STATE_CHANGE, is_sim_ready):
+ ad.log.error("Sim state didn't reach pre-reboot ready state")
+ return False
+ return True
+
+
+def unlocking_device(ad, device_password=None):
+ """First unlock device attempt, required after reboot"""
+ ad.unlock_screen(device_password)
+ time.sleep(2)
+ ad.adb.wait_for_device(timeout=180)
+ if not ad.is_waiting_for_unlock_pin():
+ return True
+ else:
+ ad.unlock_screen(device_password)
+ time.sleep(2)
+ ad.adb.wait_for_device(timeout=180)
+ if ad.wait_for_window_ready():
+ return True
+ ad.log.error("Unable to unlock to user window")
+ return False
+
+
+def refresh_sl4a_session(ad):
+ try:
+ ad.droid.logI("Checking SL4A connection")
+ ad.log.debug("Existing sl4a session is active")
+ return True
+ except Exception as e:
+ ad.log.warning("Existing sl4a session is NOT active: %s", e)
+ try:
+ ad.terminate_all_sessions()
+ except Exception as e:
+ ad.log.info("terminate_all_sessions with error %s", e)
+ ad.ensure_screen_on()
+ ad.log.info("Open new sl4a connection")
+ bring_up_sl4a(ad)
+
+
+def reset_device_password(ad, device_password=None):
+ # Enable or Disable Device Password per test bed config
+ unlock_sim(ad)
+ screen_lock = ad.is_screen_lock_enabled()
+ if device_password:
+ try:
+ refresh_sl4a_session(ad)
+ ad.droid.setDevicePassword(device_password)
+ except Exception as e:
+ ad.log.warning("setDevicePassword failed with %s", e)
+ try:
+ ad.droid.setDevicePassword(device_password, "1111")
+ except Exception as e:
+ ad.log.warning(
+ "setDevicePassword providing previous password error: %s",
+ e)
+ time.sleep(2)
+ if screen_lock:
+ # existing password changed
+ return
+ else:
+ # enable device password and log in for the first time
+ ad.log.info("Enable device password")
+ ad.adb.wait_for_device(timeout=180)
+ else:
+ if not screen_lock:
+ # no existing password, do not set password
+ return
+ else:
+ # password is enabled on the device
+ # need to disable the password and log in on the first time
+ # with unlocking with a swipe
+ ad.log.info("Disable device password")
+ ad.unlock_screen(password="1111")
+ refresh_sl4a_session(ad)
+ ad.ensure_screen_on()
+ try:
+ ad.droid.disableDevicePassword()
+ except Exception as e:
+ ad.log.warning("disableDevicePassword failed with %s", e)
+ fastboot_wipe(ad)
+ time.sleep(2)
+ ad.adb.wait_for_device(timeout=180)
+ refresh_sl4a_session(ad)
+ if not ad.is_adb_logcat_on:
+ ad.start_adb_logcat()
+
+
+def get_sim_state(ad):
+ try:
+ state = ad.droid.telephonyGetSimState()
+ except Exception as e:
+ ad.log.error(e)
+ state = ad.adb.getprop("gsm.sim.state")
+ return state
+
+
+def is_sim_locked(ad):
+ return get_sim_state(ad) == SIM_STATE_PIN_REQUIRED
+
+
+def is_sim_lock_enabled(ad):
+ # TODO: add sl4a fascade to check if sim is locked
+ return getattr(ad, "is_sim_locked", False)
+
+
+def unlock_sim(ad):
+ #The puk and pin can be provided in testbed config file.
+ #"AndroidDevice": [{"serial": "84B5T15A29018214",
+ # "adb_logcat_param": "-b all",
+ # "puk": "12345678",
+ # "puk_pin": "1234"}]
+ if not is_sim_locked(ad):
+ return True
+ else:
+ ad.is_sim_locked = True
+ puk_pin = getattr(ad, "puk_pin", "1111")
+ try:
+ if not hasattr(ad, 'puk'):
+ ad.log.info("Enter SIM pin code")
+ ad.droid.telephonySupplyPin(puk_pin)
+ else:
+ ad.log.info("Enter PUK code and pin")
+ ad.droid.telephonySupplyPuk(ad.puk, puk_pin)
+ except:
+ # if sl4a is not available, use adb command
+ ad.unlock_screen(puk_pin)
+ if is_sim_locked(ad):
+ ad.unlock_screen(puk_pin)
+ time.sleep(30)
+ return not is_sim_locked(ad)
+
+
+def send_dialer_secret_code(ad, secret_code):
+ """Send dialer secret code.
+
+ ad: android device controller
+ secret_code: the secret code to be sent to dialer. the string between
+ code prefix *#*# and code postfix #*#*. *#*#<xxx>#*#*
+ """
+ action = 'android.provider.Telephony.SECRET_CODE'
+ uri = 'android_secret_code://%s' % secret_code
+ intent = ad.droid.makeIntent(
+ action,
+ uri,
+ None, # type
+ None, # extras
+ None, # categories,
+ None, # packagename,
+ None, # classname,
+ 0x01000000) # flags
+ ad.log.info('Issuing dialer secret dialer code: %s', secret_code)
+ ad.droid.sendBroadcastIntent(intent)
+
+
+def enable_radio_log_on(ad):
+ if ad.adb.getprop("persist.vendor.radio.adb_log_on") != "1":
+ ad.log.info("Enable radio adb_log_on and reboot")
+ adb_disable_verity(ad)
+ ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
+ reboot_device(ad)
+
+
+def adb_disable_verity(ad):
+ if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
+ ad.adb.disable_verity()
+ reboot_device(ad)
+ ad.adb.remount()
+
+
+def recover_build_id(ad):
+ build_fingerprint = ad.adb.getprop(
+ "ro.vendor.build.fingerprint") or ad.adb.getprop(
+ "ro.build.fingerprint")
+ if not build_fingerprint:
+ return
+ build_id = build_fingerprint.split("/")[3]
+ if ad.adb.getprop("ro.build.id") != build_id:
+ build_id_override(ad, build_id)
+
+def enable_privacy_usage_diagnostics(ad):
+ try:
+ ad.ensure_screen_on()
+ ad.send_keycode('HOME')
+ # open the UI page on which we need to enable the setting
+ cmd = ('am start -n com.google.android.gms/com.google.android.gms.'
+ 'usagereporting.settings.UsageReportingActivity')
+ ad.adb.shell(cmd)
+ # perform the toggle
+ ad.send_keycode('TAB')
+ ad.send_keycode('ENTER')
+ except Exception:
+ ad.log.info("Unable to toggle Usage and Diagnostics")
+
+def build_id_override(ad, new_build_id=None, postfix=None):
+ build_fingerprint = ad.adb.getprop(
+ "ro.build.fingerprint") or ad.adb.getprop(
+ "ro.vendor.build.fingerprint")
+ if build_fingerprint:
+ build_id = build_fingerprint.split("/")[3]
+ else:
+ build_id = None
+ existing_build_id = ad.adb.getprop("ro.build.id")
+ if postfix is not None and postfix in build_id:
+ ad.log.info("Build id already contains %s", postfix)
+ return
+ if not new_build_id:
+ if postfix and build_id:
+ new_build_id = "%s.%s" % (build_id, postfix)
+ if not new_build_id or existing_build_id == new_build_id:
+ return
+ ad.log.info("Override build id %s with %s", existing_build_id,
+ new_build_id)
+ enable_privacy_usage_diagnostics(ad)
+ adb_disable_verity(ad)
+ ad.adb.remount()
+ if "backup.prop" not in ad.adb.shell("ls /sdcard/"):
+ ad.adb.shell("cp /system/build.prop /sdcard/backup.prop")
+ ad.adb.shell("cat /system/build.prop | grep -v ro.build.id > /sdcard/test.prop")
+ ad.adb.shell("echo ro.build.id=%s >> /sdcard/test.prop" % new_build_id)
+ ad.adb.shell("cp /sdcard/test.prop /system/build.prop")
+ reboot_device(ad)
+ ad.log.info("ro.build.id = %s", ad.adb.getprop("ro.build.id"))
+
+
+def enable_connectivity_metrics(ad):
+ cmds = [
+ "pm enable com.android.connectivity.metrics",
+ "am startservice -a com.google.android.gms.usagereporting.OPTIN_UR",
+ "am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
+ " -e usagestats:connectivity_metrics:enable_data_collection 1",
+ "am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
+ " -e usagestats:connectivity_metrics:telephony_snapshot_period_millis 180000"
+ # By default it turn on all modules
+ #"am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
+ #" -e usagestats:connectivity_metrics:data_collection_bitmap 62"
+ ]
+ for cmd in cmds:
+ ad.adb.shell(cmd, ignore_status=True)
+
+
+def force_connectivity_metrics_upload(ad):
+ cmd = "cmd jobscheduler run --force com.android.connectivity.metrics %s"
+ for job_id in [2, 3, 5, 4, 1, 6]:
+ ad.adb.shell(cmd % job_id, ignore_status=True)
+
+
+def system_file_push(ad, src_file_path, dst_file_path):
+ """Push system file on a device.
+
+ Push system file need to change some system setting and remount.
+ """
+ cmd = "%s %s" % (src_file_path, dst_file_path)
+ out = ad.adb.push(cmd, timeout=300, ignore_status=True)
+ skip_sl4a = True if "sl4a.apk" in src_file_path else False
+ if "Read-only file system" in out:
+ ad.log.info("Change read-only file system")
+ adb_disable_verity(ad)
+ out = ad.adb.push(cmd, timeout=300, ignore_status=True)
+ if "Read-only file system" in out:
+ ad.reboot(skip_sl4a)
+ out = ad.adb.push(cmd, timeout=300, ignore_status=True)
+ if "error" in out:
+ ad.log.error("%s failed with %s", cmd, out)
+ return False
+ else:
+ ad.log.info("push %s succeed")
+ if skip_sl4a: ad.reboot(skip_sl4a)
+ return True
+ else:
+ return True
+ elif "error" in out:
+ return False
+ else:
+ return True
+
+
+def flash_radio(ad, file_path, skip_setup_wizard=True):
+ """Flash radio image."""
+ ad.stop_services()
+ ad.log.info("Reboot to bootloader")
+ ad.adb.reboot_bootloader(ignore_status=True)
+ ad.log.info("Flash radio in fastboot")
+ try:
+ ad.fastboot.flash("radio %s" % file_path, timeout=300)
+ except Exception as e:
+ ad.log.error(e)
+ ad.fastboot.reboot("bootloader")
+ time.sleep(5)
+ output = ad.fastboot.getvar("version-baseband")
+ result = re.search(r"version-baseband: (\S+)", output)
+ if not result:
+ ad.log.error("fastboot getvar version-baseband output = %s", output)
+ abort_all_tests(ad.log, "Radio version-baseband is not provided")
+ fastboot_radio_version_output = result.group(1)
+ for _ in range(2):
+ try:
+ ad.log.info("Reboot in fastboot")
+ ad.fastboot.reboot()
+ ad.wait_for_boot_completion()
+ break
+ except Exception as e:
+ ad.log.error("Exception error %s", e)
+ ad.root_adb()
+ adb_radio_version_output = ad.adb.getprop("gsm.version.baseband")
+ ad.log.info("adb getprop gsm.version.baseband = %s",
+ adb_radio_version_output)
+ if adb_radio_version_output != fastboot_radio_version_output:
+ msg = ("fastboot radio version output %s does not match with adb"
+ " radio version output %s" % (fastboot_radio_version_output,
+ adb_radio_version_output))
+ abort_all_tests(ad.log, msg)
+ if not ad.ensure_screen_on():
+ ad.log.error("User window cannot come up")
+ ad.start_services(skip_setup_wizard=skip_setup_wizard)
+ unlock_sim(ad)
+
+
+def set_preferred_apn_by_adb(ad, pref_apn_name):
+ """Select Pref APN
+ Set Preferred APN on UI using content query/insert
+ It needs apn name as arg, and it will match with plmn id
+ """
+ try:
+ plmn_id = get_plmn_by_adb(ad)
+ out = ad.adb.shell("content query --uri content://telephony/carriers "
+ "--where \"apn='%s' and numeric='%s'\"" %
+ (pref_apn_name, plmn_id))
+ if "No result found" in out:
+ ad.log.warning("Cannot find APN %s on device", pref_apn_name)
+ return False
+ else:
+ apn_id = re.search(r'_id=(\d+)', out).group(1)
+ ad.log.info("APN ID is %s", apn_id)
+ ad.adb.shell("content insert --uri content:"
+ "//telephony/carriers/preferapn --bind apn_id:i:%s" %
+ (apn_id))
+ out = ad.adb.shell("content query --uri "
+ "content://telephony/carriers/preferapn")
+ if "No result found" in out:
+ ad.log.error("Failed to set prefer APN %s", pref_apn_name)
+ return False
+ elif apn_id == re.search(r'_id=(\d+)', out).group(1):
+ ad.log.info("Preferred APN set to %s", pref_apn_name)
+ return True
+ except Exception as e:
+ ad.log.error("Exception while setting pref apn %s", e)
+ return True
+
+
+def check_apm_mode_on_by_serial(ad, serial_id):
+ try:
+ apm_check_cmd = "|".join(("adb -s %s shell dumpsys wifi" % serial_id,
+ "grep -i airplanemodeon", "cut -f2 -d ' '"))
+ output = exe_cmd(apm_check_cmd)
+ if output.decode("utf-8").split("\n")[0] == "true":
+ return True
+ else:
+ return False
+ except Exception as e:
+ ad.log.warning("Exception during check apm mode on %s", e)
+ return True
+
+
+def set_apm_mode_on_by_serial(ad, serial_id):
+ try:
+ cmd1 = "adb -s %s shell settings put global airplane_mode_on 1" % serial_id
+ cmd2 = "adb -s %s shell am broadcast -a android.intent.action.AIRPLANE_MODE" % serial_id
+ exe_cmd(cmd1)
+ exe_cmd(cmd2)
+ except Exception as e:
+ ad.log.warning("Exception during set apm mode on %s", e)
+ return True
+
+
+def print_radio_info(ad, extra_msg=""):
+ for prop in ("gsm.version.baseband", "persist.radio.ver_info",
+ "persist.radio.cnv.ver_info"):
+ output = ad.adb.getprop(prop)
+ ad.log.info("%s%s = %s", extra_msg, prop, output)
+
+
+def wait_for_state(state_check_func,
+ state,
+ max_wait_time=MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ checking_interval=WAIT_TIME_BETWEEN_STATE_CHECK,
+ *args,
+ **kwargs):
+ while max_wait_time >= 0:
+ if state_check_func(*args, **kwargs) == state:
+ return True
+ time.sleep(checking_interval)
+ max_wait_time -= checking_interval
+ return False
+
+
+def power_off_sim(ad, sim_slot_id=None,
+ timeout=MAX_WAIT_TIME_FOR_STATE_CHANGE):
+ try:
+ if sim_slot_id is None:
+ ad.droid.telephonySetSimPowerState(CARD_POWER_DOWN)
+ verify_func = ad.droid.telephonyGetSimState
+ verify_args = []
+ else:
+ ad.droid.telephonySetSimStateForSlotId(sim_slot_id,
+ CARD_POWER_DOWN)
+ verify_func = ad.droid.telephonyGetSimStateForSlotId
+ verify_args = [sim_slot_id]
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ while timeout > 0:
+ sim_state = verify_func(*verify_args)
+ if sim_state in (SIM_STATE_UNKNOWN, SIM_STATE_ABSENT):
+ ad.log.info("SIM slot is powered off, SIM state is %s", sim_state)
+ return True
+ timeout = timeout - WAIT_TIME_BETWEEN_STATE_CHECK
+ time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+ ad.log.warning("Fail to power off SIM slot, sim_state=%s",
+ verify_func(*verify_args))
+ return False
+
+
+def power_on_sim(ad, sim_slot_id=None):
+ try:
+ if sim_slot_id is None:
+ ad.droid.telephonySetSimPowerState(CARD_POWER_UP)
+ verify_func = ad.droid.telephonyGetSimState
+ verify_args = []
+ else:
+ ad.droid.telephonySetSimStateForSlotId(sim_slot_id, CARD_POWER_UP)
+ verify_func = ad.droid.telephonyGetSimStateForSlotId
+ verify_args = [sim_slot_id]
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ if wait_for_state(verify_func, SIM_STATE_READY,
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK, *verify_args):
+ ad.log.info("SIM slot is powered on, SIM state is READY")
+ return True
+ elif verify_func(*verify_args) == SIM_STATE_PIN_REQUIRED:
+ ad.log.info("SIM is pin locked")
+ return True
+ else:
+ ad.log.error("Fail to power on SIM slot")
+ return False
+
+
+def extract_test_log(log, src_file, dst_file, test_tag):
+ os.makedirs(os.path.dirname(dst_file), exist_ok=True)
+ cmd = "grep -n '%s' %s" % (test_tag, src_file)
+ result = job.run(cmd, ignore_status=True)
+ if not result.stdout or result.exit_status == 1:
+ log.warning("Command %s returns %s", cmd, result)
+ return
+ line_nums = re.findall(r"(\d+).*", result.stdout)
+ if line_nums:
+ begin_line = int(line_nums[0])
+ end_line = int(line_nums[-1])
+ if end_line - begin_line <= 5:
+ result = job.run("wc -l < %s" % src_file)
+ if result.stdout:
+ end_line = int(result.stdout)
+ log.info("Extract %s from line %s to line %s to %s", src_file,
+ begin_line, end_line, dst_file)
+ job.run("awk 'NR >= %s && NR <= %s' %s > %s" % (begin_line, end_line,
+ src_file, dst_file))
+
+
+def get_device_epoch_time(ad):
+ return int(1000 * float(ad.adb.shell("date +%s.%N")))
+
+
+def synchronize_device_time(ad):
+ ad.adb.shell("put global auto_time 0", ignore_status=True)
+ try:
+ ad.adb.droid.setTime(get_current_epoch_time())
+ except Exception:
+ try:
+ ad.adb.shell("date `date +%m%d%H%M%G.%S`")
+ except Exception:
+ pass
+ try:
+ ad.adb.shell(
+ "am broadcast -a android.intent.action.TIME_SET",
+ ignore_status=True)
+ except Exception:
+ pass
+
+
+def revert_default_telephony_setting(ad):
+ toggle_airplane_mode_by_adb(ad.log, ad, True)
+ default_data_roaming = int(
+ ad.adb.getprop("ro.com.android.dataroaming") == 'true')
+ default_network_preference = int(
+ ad.adb.getprop("ro.telephony.default_network"))
+ ad.log.info("Default data roaming %s, network preference %s",
+ default_data_roaming, default_network_preference)
+ new_data_roaming = abs(default_data_roaming - 1)
+ new_network_preference = abs(default_network_preference - 1)
+ ad.log.info(
+ "Set data roaming = %s, mobile data = 0, network preference = %s",
+ new_data_roaming, new_network_preference)
+ ad.adb.shell("settings put global mobile_data 0")
+ ad.adb.shell("settings put global data_roaming %s" % new_data_roaming)
+ ad.adb.shell("settings put global preferred_network_mode %s" %
+ new_network_preference)
+
+
+def verify_default_telephony_setting(ad):
+ ad.log.info("carrier_config: %s", dumpsys_carrier_config(ad))
+ default_data_roaming = int(
+ ad.adb.getprop("ro.com.android.dataroaming") == 'true')
+ default_network_preference = int(
+ ad.adb.getprop("ro.telephony.default_network"))
+ ad.log.info("Default data roaming %s, network preference %s",
+ default_data_roaming, default_network_preference)
+ data_roaming = int(ad.adb.shell("settings get global data_roaming"))
+ mobile_data = int(ad.adb.shell("settings get global mobile_data"))
+ network_preference = int(
+ ad.adb.shell("settings get global preferred_network_mode"))
+ airplane_mode = int(ad.adb.shell("settings get global airplane_mode_on"))
+ result = True
+ ad.log.info("data_roaming = %s, mobile_data = %s, "
+ "network_perference = %s, airplane_mode = %s", data_roaming,
+ mobile_data, network_preference, airplane_mode)
+ if airplane_mode:
+ ad.log.error("Airplane mode is on")
+ result = False
+ if data_roaming != default_data_roaming:
+ ad.log.error("Data roaming is %s, expecting %s", data_roaming,
+ default_data_roaming)
+ result = False
+ if not mobile_data:
+ ad.log.error("Mobile data is off")
+ result = False
+ if network_preference != default_network_preference:
+ ad.log.error("preferred_network_mode is %s, expecting %s",
+ network_preference, default_network_preference)
+ result = False
+ return result
+
+
+def log_messaging_screen_shot(ad, test_name=""):
+ ad.ensure_screen_on()
+ ad.send_keycode("HOME")
+ ad.adb.shell("am start -n com.google.android.apps.messaging/.ui."
+ "ConversationListActivity")
+ log_screen_shot(ad, test_name)
+ ad.adb.shell("am start -n com.google.android.apps.messaging/com.google."
+ "android.apps.messaging.ui.conversation.ConversationActivity"
+ " -e conversation_id 1")
+ log_screen_shot(ad, test_name)
+ ad.send_keycode("HOME")
+
+
+def log_screen_shot(ad, test_name=""):
+ file_name = "/sdcard/Pictures/screencap"
+ if test_name:
+ file_name = "%s_%s" % (file_name, test_name)
+ file_name = "%s_%s.png" % (file_name, utils.get_current_epoch_time())
+ try:
+ ad.adb.shell("screencap -p %s" % file_name)
+ except:
+ ad.log.error("Fail to log screen shot to %s", file_name)
+
+
+def get_screen_shot_log(ad, test_name="", begin_time=None):
+ logs = ad.get_file_names("/sdcard/Pictures", begin_time=begin_time)
+ if logs:
+ ad.log.info("Pulling %s", logs)
+ log_path = os.path.join(ad.device_log_path, "Screenshot_%s" % ad.serial)
+ os.makedirs(log_path, exist_ok=True)
+ ad.pull_files(logs, log_path)
+ ad.adb.shell("rm -rf /sdcard/Pictures/screencap_*", ignore_status=True)
+
+
+def get_screen_shot_logs(ads, test_name="", begin_time=None):
+ for ad in ads:
+ get_screen_shot_log(ad, test_name=test_name, begin_time=begin_time)
+
+
+def get_carrier_id_version(ad):
+ out = ad.adb.shell("dumpsys activity service TelephonyDebugService | " \
+ "grep -i carrier_list_version")
+ if out and ":" in out:
+ version = out.split(':')[1].lstrip()
+ else:
+ version = "0"
+ ad.log.debug("Carrier Config Version is %s", version)
+ return version
+
+
+def get_carrier_config_version(ad):
+ out = ad.adb.shell("dumpsys carrier_config | grep version_string")
+ if out and "-" in out:
+ version = out.split('-')[1]
+ else:
+ version = "0"
+ ad.log.debug("Carrier Config Version is %s", version)
+ return version
+
+def get_er_db_id_version(ad):
+ out = ad.adb.shell("dumpsys activity service TelephonyDebugService | \
+ grep -i \"Database Version\"")
+ if out and ":" in out:
+ version = out.split(':', 2)[2].lstrip()
+ else:
+ version = "0"
+ ad.log.debug("Emergency database Version is %s", version)
+ return version
+
+def get_database_content(ad):
+ out = ad.adb.shell("dumpsys activity service TelephonyDebugService | \
+ egrep -i \EmergencyNumber:Number-54321")
+ if out:
+ return True
+ result = ad.adb.shell(r"dumpsys activity service TelephonyDebugService | \
+ egrep -i \updateOtaEmergencyNumberListDatabaseAndNotify")
+ ad.log.error("Emergency Number is incorrect. %s ", result)
+ return False
+
+def add_whitelisted_account(ad, user_account,user_password, retries=3):
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.error("GoogleAccountUtil is not installed")
+ return False
+ for _ in range(retries):
+ ad.ensure_screen_on()
+ output = ad.adb.shell(
+ 'am instrument -w -e account "%s@gmail.com" -e password '
+ '"%s" -e sync true -e wait-for-checkin false '
+ 'com.google.android.tradefed.account/.AddAccount' %
+ (user_account, user_password))
+ if "result=SUCCESS" in output:
+ ad.log.info("Google account is added successfully")
+ return True
+ ad.log.error("Failed to add google account - %s", output)
+ return False
+
+
+def install_googleaccountutil_apk(ad, account_util):
+ ad.log.info("Install account_util %s", account_util)
+ ad.ensure_screen_on()
+ ad.adb.install("-r %s" % account_util, timeout=300, ignore_status=True)
+ time.sleep(3)
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.info("com.google.android.tradefed.account is not installed")
+ return False
+ return True
+
+
+def install_googlefi_apk(ad, fi_util):
+ ad.log.info("Install fi_util %s", fi_util)
+ ad.ensure_screen_on()
+ ad.adb.install("-r -g --user 0 %s" % fi_util,
+ timeout=300, ignore_status=True)
+ time.sleep(3)
+ if not check_fi_apk_installed(ad):
+ return False
+ return True
+
+
+def check_fi_apk_installed(ad):
+ if not ad.is_apk_installed("com.google.android.apps.tycho"):
+ ad.log.warning("com.google.android.apps.tycho is not installed")
+ return False
+ return True
+
+
+def add_google_account(ad, retries=3):
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.error("GoogleAccountUtil is not installed")
+ return False
+ for _ in range(retries):
+ ad.ensure_screen_on()
+ output = ad.adb.shell(
+ 'am instrument -w -e account "%s@gmail.com" -e password '
+ '"%s" -e sync true -e wait-for-checkin false '
+ 'com.google.android.tradefed.account/.AddAccount' %
+ (ad.user_account, ad.user_password))
+ if "result=SUCCESS" in output:
+ ad.log.info("Google account is added successfully")
+ return True
+ ad.log.error("Failed to add google account - %s", output)
+ return False
+
+
+def remove_google_account(ad, retries=3):
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.error("GoogleAccountUtil is not installed")
+ return False
+ for _ in range(retries):
+ ad.ensure_screen_on()
+ output = ad.adb.shell(
+ 'am instrument -w '
+ 'com.google.android.tradefed.account/.RemoveAccounts')
+ if "result=SUCCESS" in output:
+ ad.log.info("google account is removed successfully")
+ return True
+ ad.log.error("Fail to remove google account due to %s", output)
+ return False
+
+
+def my_current_screen_content(ad, content):
+ ad.adb.shell("uiautomator dump --window=WINDOW")
+ out = ad.adb.shell("cat /sdcard/window_dump.xml | grep -E '%s'" % content)
+ if not out:
+ ad.log.warning("NOT FOUND - %s", content)
+ return False
+ return True
+
+
+def activate_esim_using_suw(ad):
+ _START_SUW = ('am start -a android.intent.action.MAIN -n '
+ 'com.google.android.setupwizard/.SetupWizardTestActivity')
+ _STOP_SUW = ('am start -a com.android.setupwizard.EXIT')
+
+ toggle_airplane_mode(ad.log, ad, new_state=False, strict_checking=False)
+ ad.adb.shell("settings put system screen_off_timeout 1800000")
+ ad.ensure_screen_on()
+ ad.send_keycode("MENU")
+ ad.send_keycode("HOME")
+ for _ in range(3):
+ ad.log.info("Attempt %d - activating eSIM", (_ + 1))
+ ad.adb.shell(_START_SUW)
+ time.sleep(10)
+ log_screen_shot(ad, "start_suw")
+ for _ in range(4):
+ ad.send_keycode("TAB")
+ time.sleep(0.5)
+ ad.send_keycode("ENTER")
+ time.sleep(15)
+ log_screen_shot(ad, "activate_esim")
+ get_screen_shot_log(ad)
+ ad.adb.shell(_STOP_SUW)
+ time.sleep(5)
+ current_sim = get_sim_state(ad)
+ ad.log.info("Current SIM status is %s", current_sim)
+ if current_sim not in (SIM_STATE_ABSENT, SIM_STATE_UNKNOWN):
+ break
+ return True
+
+def activate_google_fi_account(ad, retries=10):
+ _FI_APK = "com.google.android.apps.tycho"
+ _FI_ACTIVATE_CMD = ('am start -c android.intent.category.DEFAULT -n '
+ 'com.google.android.apps.tycho/.AccountDetailsActivity --ez '
+ 'in_setup_wizard false --ez force_show_account_chooser '
+ 'false')
+ toggle_airplane_mode(ad.log, ad, new_state=False, strict_checking=False)
+ ad.adb.shell("settings put system screen_off_timeout 1800000")
+ page_match_dict = {
+ "SelectAccount" : "Choose an account to use",
+ "Setup" : "Activate Google Fi to use your device for calls",
+ "Switch" : "Switch to the Google Fi mobile network",
+ "WiFi" : "Fi to download your SIM",
+ "Connect" : "Connect to the Google Fi mobile network",
+ "Move" : "Move number",
+ "Data" : "first turn on mobile data",
+ "Activate" : "This takes a minute or two, sometimes longer",
+ "Welcome" : "Welcome to Google Fi",
+ "Account" : "Your current cycle ends in"
+ }
+ page_list = ["Account", "Setup", "WiFi", "Switch", "Connect",
+ "Activate", "Move", "Welcome", "Data"]
+ for _ in range(retries):
+ ad.force_stop_apk(_FI_APK)
+ ad.ensure_screen_on()
+ ad.send_keycode("MENU")
+ ad.send_keycode("HOME")
+ ad.adb.shell(_FI_ACTIVATE_CMD)
+ time.sleep(15)
+ for page in page_list:
+ if my_current_screen_content(ad, page_match_dict[page]):
+ ad.log.info("Ready for Step %s", page)
+ log_screen_shot(ad, "fi_activation_step_%s" % page)
+ if page in ("Setup", "Switch", "Connect", "WiFi"):
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ time.sleep(30)
+ elif page == "Move" or page == "SelectAccount":
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ time.sleep(5)
+ elif page == "Welcome":
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ ad.log.info("Activation SUCCESS using Fi App")
+ time.sleep(5)
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ return True
+ elif page == "Activate":
+ time.sleep(60)
+ if my_current_screen_content(ad, page_match_dict[page]):
+ time.sleep(60)
+ elif page == "Account":
+ return True
+ elif page == "Data":
+ ad.log.error("Mobile Data is turned OFF by default")
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ else:
+ ad.log.info("NOT FOUND - Page %s", page)
+ log_screen_shot(ad, "fi_activation_step_%s_failure" % page)
+ get_screen_shot_log(ad)
+ return False
+
+
+def check_google_fi_activated(ad, retries=20):
+ if check_fi_apk_installed(ad):
+ _FI_APK = "com.google.android.apps.tycho"
+ _FI_LAUNCH_CMD = ("am start -n %s/%s.AccountDetailsActivity" \
+ % (_FI_APK, _FI_APK))
+ toggle_airplane_mode(ad.log, ad, new_state=False, strict_checking=False)
+ ad.adb.shell("settings put system screen_off_timeout 1800000")
+ ad.force_stop_apk(_FI_APK)
+ ad.ensure_screen_on()
+ ad.send_keycode("HOME")
+ ad.adb.shell(_FI_LAUNCH_CMD)
+ time.sleep(10)
+ if not my_current_screen_content(ad, "Your current cycle ends in"):
+ ad.log.warning("Fi is not activated")
+ return False
+ ad.send_keycode("HOME")
+ return True
+ else:
+ ad.log.info("Fi Apk is not yet installed")
+ return False
+
+
+def cleanup_configupdater(ad):
+ cmds = ('rm -rf /data/data/com.google.android.configupdater/shared_prefs',
+ 'rm /data/misc/carrierid/carrier_list.pb',
+ 'setprop persist.telephony.test.carrierid.ota true',
+ 'rm /data/user_de/0/com.android.providers.telephony/shared_prefs'
+ '/CarrierIdProvider.xml')
+ for cmd in cmds:
+ ad.log.info("Cleanup ConfigUpdater - %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+
+
+def pull_carrier_id_files(ad, carrier_id_path):
+ os.makedirs(carrier_id_path, exist_ok=True)
+ ad.log.info("Pull CarrierId Files")
+ cmds = ('/data/data/com.google.android.configupdater/shared_prefs/',
+ '/data/misc/carrierid/',
+ '/data/user_de/0/com.android.providers.telephony/shared_prefs/',
+ '/data/data/com.android.providers.downloads/databases/downloads.db')
+ for cmd in cmds:
+ cmd = cmd + " %s" % carrier_id_path
+ ad.adb.pull(cmd, timeout=30, ignore_status=True)
+
+
+def bring_up_connectivity_monitor(ad):
+ monitor_apk = None
+ for apk in ("com.google.telephonymonitor",
+ "com.google.android.connectivitymonitor"):
+ if ad.is_apk_installed(apk):
+ ad.log.info("apk %s is installed", apk)
+ monitor_apk = apk
+ break
+ if not monitor_apk:
+ ad.log.info("ConnectivityMonitor|TelephonyMonitor is not installed")
+ return False
+ toggle_connectivity_monitor_setting(ad, True)
+
+ if not ad.is_apk_running(monitor_apk):
+ ad.log.info("%s is not running", monitor_apk)
+ # Reboot
+ ad.log.info("reboot to bring up %s", monitor_apk)
+ reboot_device(ad)
+ for i in range(30):
+ if ad.is_apk_running(monitor_apk):
+ ad.log.info("%s is running after reboot", monitor_apk)
+ return True
+ else:
+ ad.log.info(
+ "%s is not running after reboot. Wait and check again",
+ monitor_apk)
+ time.sleep(30)
+ ad.log.error("%s is not running after reboot", monitor_apk)
+ return False
+ else:
+ ad.log.info("%s is running", monitor_apk)
+ return True
+
+
+def get_host_ip_address(ad):
+ cmd = "|".join(("ifconfig", "grep eno1 -A1", "grep inet", "awk '{$1=$1};1'", "cut -d ' ' -f 2"))
+ destination_ip = exe_cmd(cmd)
+ destination_ip = (destination_ip.decode("utf-8")).split("\n")[0]
+ ad.log.info("Host IP is %s", destination_ip)
+ return destination_ip
+
+
+def load_scone_cat_simulate_data(ad, simulate_data, sub_id=None):
+ """ Load radio simulate data
+ ad: android device controller
+ simulate_data: JSON object of simulate data
+ sub_id: RIL sub id, should be 0 or 1
+ """
+ ad.log.info("load_scone_cat_simulate_data")
+
+ #Check RIL sub id
+ if sub_id is None or sub_id > 1:
+ ad.log.error("The value of RIL sub_id should be 0 or 1")
+ return False
+
+ action = "com.google.android.apps.scone.cat.action.SetSimulateData"
+
+ #add sub id
+ simulate_data["SubId"] = sub_id
+ try:
+ #dump json
+ extra = json.dumps(simulate_data)
+ ad.log.info("send simulate_data=[%s]" % extra)
+ #send data
+ ad.adb.shell("am broadcast -a " + action + " --es simulate_data '" + extra + "'")
+ except Exception as e:
+ ad.log.error("Exception error to send CAT: %s", e)
+ return False
+
+ return True
+
+
+def load_scone_cat_data_from_file(ad, simulate_file_path, sub_id=None):
+ """ Load radio simulate data
+ ad: android device controller
+ simulate_file_path: JSON file of simulate data
+ sub_id: RIL sub id, should be 0 or 1
+ """
+ ad.log.info("load_radio_simulate_data_from_file from %s" % simulate_file_path)
+ radio_simulate_data = {}
+
+ #Check RIL sub id
+ if sub_id is None or sub_id > 1:
+ ad.log.error("The value of RIL sub_id should be 0 or 1")
+ raise ValueError
+
+ with open(simulate_file_path, 'r') as f:
+ try:
+ radio_simulate_data = json.load(f)
+ except Exception as e:
+ self.log.error("Exception error to load %s: %s", f, e)
+ return False
+
+ for item in radio_simulate_data:
+ result = load_scone_cat_simulate_data(ad, item, sub_id)
+ if result == False:
+ ad.log.error("Load CAT command fail")
+ return False
+ time.sleep(0.1)
+
+ return True
+
+
+def toggle_connectivity_monitor_setting(ad, state=True):
+ monitor_setting = ad.adb.getprop("persist.radio.enable_tel_mon")
+ ad.log.info("radio.enable_tel_mon setting is %s", monitor_setting)
+ current_state = True if monitor_setting == "user_enabled" else False
+ if current_state == state:
+ return True
+ elif state is None:
+ state = not current_state
+ expected_monitor_setting = "user_enabled" if state else "disabled"
+ cmd = "setprop persist.radio.enable_tel_mon %s" % expected_monitor_setting
+ ad.log.info("Toggle connectivity monitor by %s", cmd)
+ ad.adb.shell(
+ "am start -n com.android.settings/.DevelopmentSettings",
+ ignore_status=True)
+ ad.adb.shell(cmd)
+ monitor_setting = ad.adb.getprop("persist.radio.enable_tel_mon")
+ ad.log.info("radio.enable_tel_mon setting is %s", monitor_setting)
+ return monitor_setting == expected_monitor_setting
+
+def get_call_forwarding_by_adb(log, ad, call_forwarding_type="unconditional"):
+ """ Get call forwarding status by adb shell command
+ 'dumpsys telephony.registry'.
+
+ Args:
+ log: log object
+ ad: android object
+ call_forwarding_type:
+ - "unconditional"
+ - "busy" (todo)
+ - "not_answered" (todo)
+ - "not_reachable" (todo)
+ Returns:
+ - "true": if call forwarding unconditional is enabled.
+ - "false": if call forwarding unconditional is disabled.
+ - "unknown": if the type is other than 'unconditional'.
+ - False: any case other than above 3 cases.
+ """
+ if call_forwarding_type != "unconditional":
+ return "unknown"
+
+ slot_index_of_default_voice_subid = get_slot_index_from_subid(log, ad,
+ get_incoming_voice_sub_id(ad))
+ output = ad.adb.shell("dumpsys telephony.registry | grep mCallForwarding")
+ if "mCallForwarding" in output:
+ result_list = re.findall(r"mCallForwarding=(true|false)", output)
+ if result_list:
+ result = result_list[slot_index_of_default_voice_subid]
+ ad.log.info("mCallForwarding is %s", result)
+
+ if re.search("false", result, re.I):
+ return "false"
+ elif re.search("true", result, re.I):
+ return "true"
+ else:
+ return False
+ else:
+ return False
+ else:
+ ad.log.error("'mCallForwarding' cannot be found in dumpsys.")
+ return False
+
+def erase_call_forwarding_by_mmi(
+ log,
+ ad,
+ retry=2,
+ call_forwarding_type="unconditional"):
+ """ Erase setting of call forwarding (erase the number and disable call
+ forwarding) by MMI code.
+
+ Args:
+ log: log object
+ ad: android object
+ retry: times of retry if the erasure failed.
+ call_forwarding_type:
+ - "unconditional"
+ - "busy"
+ - "not_answered"
+ - "not_reachable"
+ Returns:
+ True by successful erasure. Otherwise False.
+ """
+ res = get_call_forwarding_by_adb(log, ad,
+ call_forwarding_type=call_forwarding_type)
+ if res == "false":
+ return True
+
+ user_config_profile = get_user_config_profile(ad)
+ is_airplane_mode = user_config_profile["Airplane Mode"]
+ is_wfc_enabled = user_config_profile["WFC Enabled"]
+ wfc_mode = user_config_profile["WFC Mode"]
+ is_wifi_on = user_config_profile["WiFi State"]
+
+ if is_airplane_mode:
+ if not toggle_airplane_mode(log, ad, False):
+ ad.log.error("Failed to disable airplane mode.")
+ return False
+
+ operator_name = get_operator_name(log, ad)
+
+ code_dict = {
+ "Verizon": {
+ "unconditional": "73",
+ "busy": "73",
+ "not_answered": "73",
+ "not_reachable": "73",
+ "mmi": "*%s"
+ },
+ "Sprint": {
+ "unconditional": "720",
+ "busy": "740",
+ "not_answered": "730",
+ "not_reachable": "720",
+ "mmi": "*%s"
+ },
+ 'Generic': {
+ "unconditional": "21",
+ "busy": "67",
+ "not_answered": "61",
+ "not_reachable": "62",
+ "mmi": "##%s#"
+ }
+ }
+
+ if operator_name in code_dict:
+ code = code_dict[operator_name][call_forwarding_type]
+ mmi = code_dict[operator_name]["mmi"]
+ else:
+ code = code_dict['Generic'][call_forwarding_type]
+ mmi = code_dict['Generic']["mmi"]
+
+ result = False
+ while retry >= 0:
+ res = get_call_forwarding_by_adb(
+ log, ad, call_forwarding_type=call_forwarding_type)
+ if res == "false":
+ ad.log.info("Call forwarding is already disabled.")
+ result = True
+ break
+
+ ad.log.info("Erasing and deactivating call forwarding %s..." %
+ call_forwarding_type)
+
+ ad.droid.telecomDialNumber(mmi % code)
+
+ time.sleep(3)
+ ad.send_keycode("ENTER")
+ time.sleep(15)
+
+ # To dismiss the pop-out dialog
+ ad.send_keycode("BACK")
+ time.sleep(5)
+ ad.send_keycode("BACK")
+
+ res = get_call_forwarding_by_adb(
+ log, ad, call_forwarding_type=call_forwarding_type)
+ if res == "false" or res == "unknown":
+ result = True
+ break
+ else:
+ ad.log.error("Failed to erase and deactivate call forwarding by "
+ "MMI code ##%s#." % code)
+ retry = retry - 1
+ time.sleep(30)
+
+ if is_airplane_mode:
+ if not toggle_airplane_mode(log, ad, True):
+ ad.log.error("Failed to enable airplane mode again.")
+ else:
+ if is_wifi_on:
+ ad.droid.wifiToggleState(True)
+ if is_wfc_enabled:
+ if not wait_for_wfc_enabled(
+ log, ad,max_time=MAX_WAIT_TIME_WFC_ENABLED):
+ ad.log.error("WFC is not enabled")
+
+ return result
+
+def set_call_forwarding_by_mmi(
+ log,
+ ad,
+ ad_forwarded,
+ call_forwarding_type="unconditional",
+ retry=2):
+ """ Set up the forwarded number and enable call forwarding by MMI code.
+
+ Args:
+ log: log object
+ ad: android object of the device forwarding the call (primary device)
+ ad_forwarded: android object of the device receiving forwarded call.
+ retry: times of retry if the erasure failed.
+ call_forwarding_type:
+ - "unconditional"
+ - "busy"
+ - "not_answered"
+ - "not_reachable"
+ Returns:
+ True by successful erasure. Otherwise False.
+ """
+
+ res = get_call_forwarding_by_adb(log, ad,
+ call_forwarding_type=call_forwarding_type)
+ if res == "true":
+ return True
+
+ if ad.droid.connectivityCheckAirplaneMode():
+ ad.log.warning("%s is now in airplane mode.", ad.serial)
+ return False
+
+ operator_name = get_operator_name(log, ad)
+
+ code_dict = {
+ "Verizon": {
+ "unconditional": "72",
+ "busy": "71",
+ "not_answered": "71",
+ "not_reachable": "72",
+ "mmi": "*%s%s"
+ },
+ "Sprint": {
+ "unconditional": "72",
+ "busy": "74",
+ "not_answered": "73",
+ "not_reachable": "72",
+ "mmi": "*%s%s"
+ },
+ 'Generic': {
+ "unconditional": "21",
+ "busy": "67",
+ "not_answered": "61",
+ "not_reachable": "62",
+ "mmi": "*%s*%s#",
+ "mmi_for_plus_sign": "*%s*"
+ }
+ }
+
+ if operator_name in code_dict:
+ code = code_dict[operator_name][call_forwarding_type]
+ mmi = code_dict[operator_name]["mmi"]
+ else:
+ code = code_dict['Generic'][call_forwarding_type]
+ mmi = code_dict['Generic']["mmi"]
+ mmi_for_plus_sign = code_dict['Generic']["mmi_for_plus_sign"]
+
+ while retry >= 0:
+ if not erase_call_forwarding_by_mmi(
+ log, ad, call_forwarding_type=call_forwarding_type):
+ retry = retry - 1
+ continue
+
+ forwarded_number = ad_forwarded.telephony['subscription'][
+ ad_forwarded.droid.subscriptionGetDefaultVoiceSubId()][
+ 'phone_num']
+ ad.log.info("Registering and activating call forwarding %s to %s..." %
+ (call_forwarding_type, forwarded_number))
+
+ (forwarded_number_no_prefix, _) = _phone_number_remove_prefix(
+ forwarded_number)
+
+ _found_plus_sign = 0
+ if re.search("^\+", forwarded_number):
+ _found_plus_sign = 1
+ forwarded_number.replace("+", "")
+
+ if operator_name in code_dict:
+ ad.droid.telecomDialNumber(mmi % (code, forwarded_number_no_prefix))
+ else:
+ if _found_plus_sign == 0:
+ ad.droid.telecomDialNumber(mmi % (code, forwarded_number))
+ else:
+ ad.droid.telecomDialNumber(mmi_for_plus_sign % code)
+ ad.send_keycode("PLUS")
+ dial_phone_number(ad, forwarded_number + "#")
+
+ time.sleep(3)
+ ad.send_keycode("ENTER")
+ time.sleep(15)
+
+ # To dismiss the pop-out dialog
+ ad.send_keycode("BACK")
+ time.sleep(5)
+ ad.send_keycode("BACK")
+
+ result = get_call_forwarding_by_adb(
+ log, ad, call_forwarding_type=call_forwarding_type)
+ if result == "false":
+ retry = retry - 1
+ elif result == "true":
+ return True
+ elif result == "unknown":
+ return True
+ else:
+ retry = retry - 1
+
+ if retry >= 0:
+ ad.log.warning("Failed to register or activate call forwarding %s "
+ "to %s. Retry after 15 seconds." % (call_forwarding_type,
+ forwarded_number))
+ time.sleep(15)
+
+ ad.log.error("Failed to register or activate call forwarding %s to %s." %
+ (call_forwarding_type, forwarded_number))
+ return False
+
+def get_call_waiting_status(log, ad):
+ """ (Todo) Get call waiting status (activated or deactivated) when there is
+ any proper method available.
+ """
+ return True
+
+def set_call_waiting(log, ad, enable=1, retry=1):
+ """ Activate/deactivate call waiting by dialing MMI code.
+
+ Args:
+ log: log object.
+ ad: android object.
+ enable: 1 for activation and 0 fir deactivation
+ retry: times of retry if activation/deactivation fails
+
+ Returns:
+ True by successful activation/deactivation; otherwise False.
+ """
+ operator_name = get_operator_name(log, ad)
+
+ if operator_name in ["Verizon", "Sprint"]:
+ return True
+
+ while retry >= 0:
+ if enable:
+ ad.log.info("Activating call waiting...")
+ ad.droid.telecomDialNumber("*43#")
+ else:
+ ad.log.info("Deactivating call waiting...")
+ ad.droid.telecomDialNumber("#43#")
+
+ time.sleep(3)
+ ad.send_keycode("ENTER")
+ time.sleep(15)
+
+ ad.send_keycode("BACK")
+ time.sleep(5)
+ ad.send_keycode("BACK")
+
+ if get_call_waiting_status(log, ad):
+ return True
+ else:
+ retry = retry + 1
+
+ return False
+
+def get_rx_tx_power_levels(log, ad):
+ """ Obtains Rx and Tx power levels from the MDS application.
+
+ The method requires the MDS app to be installed in the DUT.
+
+ Args:
+ log: logger object
+ ad: an android device
+
+ Return:
+ A tuple where the first element is an array array with the RSRP value
+ in Rx chain, and the second element is the transmitted power in dBm.
+ Values for invalid Rx / Tx chains are set to None.
+ """
+ cmd = ('am instrument -w -e request "80 00 e8 03 00 08 00 00 00" -e '
+ 'response wait "com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemCommandInstrumentation"')
+ output = ad.adb.shell(cmd)
+
+ if 'result=SUCCESS' not in output:
+ raise RuntimeError('Could not obtain Tx/Rx power levels from MDS. Is '
+ 'the MDS app installed?')
+
+ response = re.search(r"(?<=response=).+", output)
+
+ if not response:
+ raise RuntimeError('Invalid response from the MDS app:\n' + output)
+
+ # Obtain a list of bytes in hex format from the response string
+ response_hex = response.group(0).split(' ')
+
+ def get_bool(pos):
+ """ Obtain a boolean variable from the byte array. """
+ return response_hex[pos] == '01'
+
+ def get_int32(pos):
+ """ Obtain an int from the byte array. Bytes are printed in
+ little endian format."""
+ return struct.unpack(
+ '<i', bytearray.fromhex(''.join(response_hex[pos:pos + 4])))[0]
+
+ rx_power = []
+ RX_CHAINS = 4
+
+ for i in range(RX_CHAINS):
+ # Calculate starting position for the Rx chain data structure
+ start = 12 + i * 22
+
+ # The first byte in the data structure indicates if the rx chain is
+ # valid.
+ if get_bool(start):
+ rx_power.append(get_int32(start + 2) / 10)
+ else:
+ rx_power.append(None)
+
+ # Calculate the position for the tx chain data structure
+ tx_pos = 12 + RX_CHAINS * 22
+
+ tx_valid = get_bool(tx_pos)
+ if tx_valid:
+ tx_power = get_int32(tx_pos + 2) / -10
+ else:
+ tx_power = None
+
+ return rx_power, tx_power
+
+def sms_in_collision_send_receive_verify(
+ log,
+ ad_rx,
+ ad_rx2,
+ ad_tx,
+ ad_tx2,
+ array_message,
+ array_message2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+ """Send 2 SMS', receive both SMS', and verify content and sender's number of
+ each SMS.
+
+ Send 2 SMS'. One from ad_tx to ad_rx and the other from ad_tx2 to ad_rx2.
+ When ad_rx is identical to ad_rx2, the scenario of SMS' in collision can
+ be tested.
+ Verify both SMS' are sent, delivered and received.
+ Verify received content and sender's number of each SMS is correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object..
+ ad_rx: Receiver's Android Device Object.
+ ad_tx2: 2nd sender's Android Device Object..
+ ad_rx2: 2nd receiver's Android Device Object.
+ array_message: the array of message to send/receive from ad_tx to ad_rx
+ array_message2: the array of message to send/receive from ad_tx2 to
+ ad_rx2
+ max_wait_time: Max time to wait for reception of SMS
+ """
+
+ rx_sub_id = get_outgoing_message_sub_id(ad_rx)
+ rx2_sub_id = get_outgoing_message_sub_id(ad_rx2)
+
+ _, tx_sub_id, _ = get_subid_on_same_network_of_host_ad(
+ [ad_rx, ad_tx, ad_tx2],
+ host_sub_id=rx_sub_id)
+ set_subid_for_message(ad_tx, tx_sub_id)
+
+ _, _, tx2_sub_id = get_subid_on_same_network_of_host_ad(
+ [ad_rx2, ad_tx, ad_tx2],
+ host_sub_id=rx2_sub_id)
+ set_subid_for_message(ad_tx2, tx2_sub_id)
+
+ if not sms_in_collision_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_tx2,
+ ad_rx,
+ ad_rx2,
+ tx_sub_id,
+ tx2_sub_id,
+ rx_sub_id,
+ rx_sub_id,
+ array_message,
+ array_message2,
+ max_wait_time):
+ log_messaging_screen_shot(
+ ad_rx, test_name="sms rx subid: %s" % rx_sub_id)
+ log_messaging_screen_shot(
+ ad_rx2, test_name="sms rx2 subid: %s" % rx2_sub_id)
+ log_messaging_screen_shot(
+ ad_tx, test_name="sms tx subid: %s" % tx_sub_id)
+ log_messaging_screen_shot(
+ ad_tx2, test_name="sms tx subid: %s" % tx2_sub_id)
+ return False
+ return True
+
+def sms_in_collision_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_tx2,
+ ad_rx,
+ ad_rx2,
+ subid_tx,
+ subid_tx2,
+ subid_rx,
+ subid_rx2,
+ array_message,
+ array_message2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+ """Send 2 SMS', receive both SMS', and verify content and sender's number of
+ each SMS.
+
+ Send 2 SMS'. One from ad_tx to ad_rx and the other from ad_tx2 to ad_rx2.
+ When ad_rx is identical to ad_rx2, the scenario of SMS' in collision can
+ be tested.
+ Verify both SMS' are sent, delivered and received.
+ Verify received content and sender's number of each SMS is correct.
+
+ Args:
+ log: Log object.
+ ad_tx: Sender's Android Device Object..
+ ad_rx: Receiver's Android Device Object.
+ ad_tx2: 2nd sender's Android Device Object..
+ ad_rx2: 2nd receiver's Android Device Object.
+ subid_tx: Sub ID of ad_tx as default Sub ID for outgoing SMS
+ subid_tx2: Sub ID of ad_tx2 as default Sub ID for outgoing SMS
+ subid_rx: Sub ID of ad_rx as default Sub ID for incoming SMS
+ subid_rx2: Sub ID of ad_rx2 as default Sub ID for incoming SMS
+ array_message: the array of message to send/receive from ad_tx to ad_rx
+ array_message2: the array of message to send/receive from ad_tx2 to
+ ad_rx2
+ max_wait_time: Max time to wait for reception of SMS
+ """
+
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_tx2 = ad_tx2.telephony['subscription'][subid_tx2]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+ phonenumber_rx2 = ad_rx2.telephony['subscription'][subid_rx2]['phone_num']
+
+ for ad in (ad_tx, ad_tx2, ad_rx, ad_rx2):
+ ad.send_keycode("BACK")
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ ad.messaging_ed.clear_all_events()
+ ad.messaging_droid.logI(
+ "Start sms_send_receive_verify_for_subscription test")
+ except Exception:
+ ad.log.info("Create new sl4a session for messaging")
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
+ for text, text2 in zip(array_message, array_message2):
+ length = len(text)
+ length2 = len(text2)
+ ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx, phonenumber_rx, length, text)
+ ad_tx2.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx2, phonenumber_rx2, length2, text2)
+
+ try:
+ ad_rx.messaging_ed.clear_events(EventSmsReceived)
+ ad_rx2.messaging_ed.clear_events(EventSmsReceived)
+ ad_tx.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx.messaging_ed.clear_events(EventSmsSentFailure)
+ ad_tx2.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx2.messaging_ed.clear_events(EventSmsSentFailure)
+ ad_rx.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ if ad_rx2 != ad_rx:
+ ad_rx2.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ time.sleep(1)
+ ad_tx.messaging_droid.logI("Sending SMS of length %s" % length)
+ ad_tx2.messaging_droid.logI("Sending SMS of length %s" % length2)
+ ad_rx.messaging_droid.logI(
+ "Expecting SMS of length %s from %s" % (length, ad_tx.serial))
+ ad_rx2.messaging_droid.logI(
+ "Expecting SMS of length %s from %s" % (length2, ad_tx2.serial))
+
+ tasks = [
+ (ad_tx.messaging_droid.smsSendTextMessage,
+ (phonenumber_rx, text, True)),
+ (ad_tx2.messaging_droid.smsSendTextMessage,
+ (phonenumber_rx2, text2, True))]
+ multithread_func(log, tasks)
+ try:
+ tasks = [
+ (ad_tx.messaging_ed.pop_events, ("(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure), max_wait_time)),
+ (ad_tx2.messaging_ed.pop_events, ("(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure), max_wait_time))
+ ]
+ results = run_multithread_func(log, tasks)
+ res = True
+ _ad = ad_tx
+ for ad, events in [(ad_tx, results[0]),(ad_tx2, results[1])]:
+ _ad = ad
+ for event in events:
+ ad.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or \
+ event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and event["data"].get("Reason"):
+ ad.log.error("%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ res = False
+ elif event["name"] == EventSmsSentSuccess or \
+ event["name"] == EventSmsDeliverSuccess:
+ break
+ if not res:
+ return False
+ except Empty:
+ _ad.log.error("No %s or %s event for SMS of length %s.",
+ EventSmsSentSuccess, EventSmsSentFailure,
+ length)
+ return False
+ if ad_rx == ad_rx2:
+ if not wait_for_matching_mt_sms_in_collision(
+ log,
+ ad_rx,
+ phonenumber_tx,
+ phonenumber_tx2,
+ text,
+ text2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+
+ ad_rx.log.error(
+ "No matching received SMS of length %s from %s.",
+ length,
+ ad_rx.serial)
+ return False
+ else:
+ if not wait_for_matching_mt_sms_in_collision_with_mo_sms(
+ log,
+ ad_rx,
+ ad_rx2,
+ phonenumber_tx,
+ phonenumber_tx2,
+ text,
+ text2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+ return False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingSmsMessage()
+ ad_rx2.messaging_droid.smsStopTrackingIncomingSmsMessage()
+ return True
+
+
+def sms_rx_power_off_multiple_send_receive_verify(
+ log,
+ ad_rx,
+ ad_tx,
+ ad_tx2,
+ array_message_length,
+ array_message2_length,
+ num_array_message,
+ num_array_message2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+
+ rx_sub_id = get_outgoing_message_sub_id(ad_rx)
+
+ _, tx_sub_id, _ = get_subid_on_same_network_of_host_ad(
+ [ad_rx, ad_tx, ad_tx2],
+ host_sub_id=rx_sub_id)
+ set_subid_for_message(ad_tx, tx_sub_id)
+
+ _, _, tx2_sub_id = get_subid_on_same_network_of_host_ad(
+ [ad_rx, ad_tx, ad_tx2],
+ host_sub_id=rx_sub_id)
+ set_subid_for_message(ad_tx2, tx2_sub_id)
+
+ if not sms_rx_power_off_multiple_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_tx2,
+ ad_rx,
+ tx_sub_id,
+ tx2_sub_id,
+ rx_sub_id,
+ rx_sub_id,
+ array_message_length,
+ array_message2_length,
+ num_array_message,
+ num_array_message2):
+ log_messaging_screen_shot(
+ ad_rx, test_name="sms rx subid: %s" % rx_sub_id)
+ log_messaging_screen_shot(
+ ad_tx, test_name="sms tx subid: %s" % tx_sub_id)
+ log_messaging_screen_shot(
+ ad_tx2, test_name="sms tx subid: %s" % tx2_sub_id)
+ return False
+ return True
+
+
+def sms_rx_power_off_multiple_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_tx2,
+ ad_rx,
+ subid_tx,
+ subid_tx2,
+ subid_rx,
+ subid_rx2,
+ array_message_length,
+ array_message2_length,
+ num_array_message,
+ num_array_message2,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_tx2 = ad_tx2.telephony['subscription'][subid_tx2]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+ phonenumber_rx2 = ad_rx.telephony['subscription'][subid_rx2]['phone_num']
+
+ if not toggle_airplane_mode(log, ad_rx, True):
+ ad_rx.log.error("Failed to enable Airplane Mode")
+ return False
+ ad_rx.stop_services()
+ ad_rx.log.info("Rebooting......")
+ ad_rx.adb.reboot()
+
+ message_dict = {phonenumber_tx: [], phonenumber_tx2: []}
+ for index in range(max(num_array_message, num_array_message2)):
+ array_message = [rand_ascii_str(array_message_length)]
+ array_message2 = [rand_ascii_str(array_message2_length)]
+ for text, text2 in zip(array_message, array_message2):
+ message_dict[phonenumber_tx].append(text)
+ message_dict[phonenumber_tx2].append(text2)
+ length = len(text)
+ length2 = len(text2)
+
+ ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx, phonenumber_rx, length, text)
+ ad_tx2.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx2, phonenumber_rx2, length2, text2)
+
+ try:
+ for ad in (ad_tx, ad_tx2):
+ ad.send_keycode("BACK")
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = \
+ ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ ad.messaging_ed.clear_all_events()
+ ad.messaging_droid.logI(
+ "Start sms_send_receive_verify_for_subscription"
+ " test")
+ except Exception:
+ ad.log.info("Create new sl4a session for messaging")
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
+ ad_tx.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx.messaging_ed.clear_events(EventSmsSentFailure)
+ ad_tx2.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx2.messaging_ed.clear_events(EventSmsSentFailure)
+
+ if index < num_array_message and index < num_array_message2:
+ ad_tx.messaging_droid.logI(
+ "Sending SMS of length %s" % length)
+ ad_tx2.messaging_droid.logI(
+ "Sending SMS of length %s" % length2)
+ tasks = [
+ (ad_tx.messaging_droid.smsSendTextMessage,
+ (phonenumber_rx, text, True)),
+ (ad_tx2.messaging_droid.smsSendTextMessage,
+ (phonenumber_rx2, text2, True))]
+ multithread_func(log, tasks)
+ else:
+ if index < num_array_message:
+ ad_tx.messaging_droid.logI(
+ "Sending SMS of length %s" % length)
+ ad_tx.messaging_droid.smsSendTextMessage(
+ phonenumber_rx, text, True)
+ if index < num_array_message2:
+ ad_tx2.messaging_droid.logI(
+ "Sending SMS of length %s" % length2)
+ ad_tx2.messaging_droid.smsSendTextMessage(
+ phonenumber_rx2, text2, True)
+
+ try:
+ if index < num_array_message and index < num_array_message2:
+ tasks = [
+ (ad_tx.messaging_ed.pop_events, ("(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure),
+ max_wait_time)),
+ (ad_tx2.messaging_ed.pop_events, ("(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure),
+ max_wait_time))
+ ]
+ results = run_multithread_func(log, tasks)
+ res = True
+ _ad = ad_tx
+ for ad, events in [
+ (ad_tx, results[0]), (ad_tx2, results[1])]:
+ _ad = ad
+ for event in events:
+ ad.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or \
+ event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and \
+ event["data"].get("Reason"):
+ ad.log.error("%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ res = False
+ elif event["name"] == EventSmsSentSuccess or \
+ event["name"] == EventSmsDeliverSuccess:
+ break
+ if not res:
+ return False
+ else:
+ if index < num_array_message:
+ result = ad_tx.messaging_ed.pop_events(
+ "(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure),
+ max_wait_time)
+ res = True
+ _ad = ad_tx
+ for ad, events in [(ad_tx, result)]:
+ _ad = ad
+ for event in events:
+ ad.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or \
+ event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and \
+ event["data"].get("Reason"):
+ ad.log.error(
+ "%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ res = False
+ elif event["name"] == EventSmsSentSuccess \
+ or event["name"] == EventSmsDeliverSuccess:
+ break
+ if not res:
+ return False
+ if index < num_array_message2:
+ result = ad_tx2.messaging_ed.pop_events(
+ "(%s|%s|%s|%s)" % (
+ EventSmsSentSuccess,
+ EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure),
+ max_wait_time)
+ res = True
+ _ad = ad_tx2
+ for ad, events in [(ad_tx2, result)]:
+ _ad = ad
+ for event in events:
+ ad.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or \
+ event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and \
+ event["data"].get("Reason"):
+ ad.log.error(
+ "%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ res = False
+ elif event["name"] == EventSmsSentSuccess \
+ or event["name"] == EventSmsDeliverSuccess:
+ break
+ if not res:
+ return False
+
+
+ except Empty:
+ _ad.log.error("No %s or %s event for SMS of length %s.",
+ EventSmsSentSuccess, EventSmsSentFailure,
+ length)
+ return False
+
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+
+ ad_rx.wait_for_boot_completion()
+ ad_rx.root_adb()
+ ad_rx.start_services(skip_setup_wizard=False)
+
+ output = ad_rx.adb.logcat("-t 1")
+ match = re.search(r"\d+-\d+\s\d+:\d+:\d+.\d+", output)
+ if match:
+ ad_rx.test_log_begin_time = match.group(0)
+
+ ad_rx.messaging_droid, ad_rx.messaging_ed = ad_rx.get_droid()
+ ad_rx.messaging_ed.start()
+ ad_rx.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ time.sleep(1) #sleep 100ms after starting event tracking
+
+ if not toggle_airplane_mode(log, ad_rx, False):
+ ad_rx.log.error("Failed to disable Airplane Mode")
+ return False
+
+ res = True
+ try:
+ if not wait_for_matching_multiple_sms(log,
+ ad_rx,
+ phonenumber_tx,
+ phonenumber_tx2,
+ messages=message_dict,
+ max_wait_time=max_wait_time):
+ res = False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingSmsMessage()
+
+ return res
+
+def wait_for_matching_mt_sms_in_collision(log,
+ ad_rx,
+ phonenumber_tx,
+ phonenumber_tx2,
+ text,
+ text2,
+ allow_multi_part_long_sms=True,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+
+ if not allow_multi_part_long_sms:
+ try:
+ ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived,
+ is_sms_in_collision_match,
+ max_wait_time,
+ phonenumber_tx,
+ phonenumber_tx2,
+ text,
+ text2)
+ ad_rx.log.info("Got event %s", EventSmsReceived)
+ return True
+ except Empty:
+ ad_rx.log.error("No matched SMS received event.")
+ return False
+ else:
+ try:
+ received_sms = ''
+ received_sms2 = ''
+ remaining_text = text
+ remaining_text2 = text2
+ while (remaining_text != '' or remaining_text2 != ''):
+ event = ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived,
+ is_sms_in_collision_partial_match,
+ max_wait_time,
+ phonenumber_tx,
+ phonenumber_tx2,
+ remaining_text,
+ remaining_text2)
+ event_text = event['data']['Text'].split(")")[-1].strip()
+ event_text_length = len(event_text)
+
+ if event_text in remaining_text:
+ ad_rx.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length,
+ phonenumber_tx)
+ remaining_text = remaining_text[event_text_length:]
+ received_sms += event_text
+ elif event_text in remaining_text2:
+ ad_rx.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length,
+ phonenumber_tx2)
+ remaining_text2 = remaining_text2[event_text_length:]
+ received_sms2 += event_text
+
+ ad_rx.log.info("Received SMS of length %s", len(received_sms))
+ ad_rx.log.info("Received SMS of length %s", len(received_sms2))
+ return True
+ except Empty:
+ ad_rx.log.error(
+ "Missing SMS received event.")
+ if received_sms != '':
+ ad_rx.log.error(
+ "Only received partial matched SMS of length %s from %s",
+ len(received_sms), phonenumber_tx)
+ if received_sms2 != '':
+ ad_rx.log.error(
+ "Only received partial matched SMS of length %s from %s",
+ len(received_sms2), phonenumber_tx2)
+ return False
+
+def wait_for_matching_mt_sms_in_collision_with_mo_sms(log,
+ ad_rx,
+ ad_rx2,
+ phonenumber_tx,
+ phonenumber_tx2,
+ text,
+ text2,
+ allow_multi_part_long_sms=True,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION):
+
+ if not allow_multi_part_long_sms:
+ result = True
+ try:
+ ad_rx.messaging_ed.wait_for_call_offhook_event(
+ EventSmsReceived,
+ is_sms_match,
+ max_wait_time,
+ phonenumber_tx,
+ text)
+ ad_rx.log.info("Got event %s", EventSmsReceived)
+ except Empty:
+ ad_rx.log.error("No matched SMS received event.")
+ result = False
+
+ try:
+ ad_rx2.messaging_ed.wait_for_call_offhook_event(
+ EventSmsReceived,
+ is_sms_match,
+ max_wait_time,
+ phonenumber_tx2,
+ text2)
+ ad_rx2.log.info("Got event %s", EventSmsReceived)
+ except Empty:
+ ad_rx2.log.error("No matched SMS received event.")
+ result = False
+
+ return result
+ else:
+ result = True
+ try:
+ received_sms = ''
+ remaining_text = text
+ while remaining_text != '':
+ event = ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived, is_sms_partial_match, max_wait_time,
+ phonenumber_tx, remaining_text)
+ event_text = event['data']['Text'].split(")")[-1].strip()
+ event_text_length = len(event_text)
+
+ if event_text in remaining_text:
+ ad_rx.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length,
+ phonenumber_tx)
+ remaining_text = remaining_text[event_text_length:]
+ received_sms += event_text
+
+ ad_rx.log.info("Received SMS of length %s", len(received_sms))
+ except Empty:
+ ad_rx.log.error(
+ "Missing SMS received event.")
+ if received_sms != '':
+ ad_rx.log.error(
+ "Only received partial matched SMS of length %s from %s",
+ len(received_sms), phonenumber_tx)
+ result = False
+
+ try:
+ received_sms2 = ''
+ remaining_text2 = text2
+ while remaining_text2 != '':
+ event2 = ad_rx2.messaging_ed.wait_for_event(
+ EventSmsReceived, is_sms_partial_match, max_wait_time,
+ phonenumber_tx2, remaining_text2)
+ event_text2 = event2['data']['Text'].split(")")[-1].strip()
+ event_text_length2 = len(event_text2)
+
+ if event_text2 in remaining_text2:
+ ad_rx2.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length2,
+ phonenumber_tx2)
+ remaining_text2 = remaining_text2[event_text_length2:]
+ received_sms2 += event_text2
+
+ ad_rx2.log.info("Received SMS of length %s", len(received_sms2))
+ except Empty:
+ ad_rx2.log.error(
+ "Missing SMS received event.")
+ if received_sms2 != '':
+ ad_rx2.log.error(
+ "Only received partial matched SMS of length %s from %s",
+ len(received_sms2), phonenumber_tx2)
+ result = False
+
+ return result
+
+def wait_for_matching_multiple_sms(log,
+ ad_rx,
+ phonenumber_tx,
+ phonenumber_tx2,
+ messages={},
+ allow_multi_part_long_sms=True,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+
+ if not allow_multi_part_long_sms:
+ try:
+ ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived,
+ is_sms_match_among_multiple_sms,
+ max_wait_time,
+ phonenumber_tx,
+ phonenumber_tx2,
+ messages[phonenumber_tx],
+ messages[phonenumber_tx2])
+ ad_rx.log.info("Got event %s", EventSmsReceived)
+ return True
+ except Empty:
+ ad_rx.log.error("No matched SMS received event.")
+ return False
+ else:
+ all_msgs = []
+ for tx, msgs in messages.items():
+ for msg in msgs:
+ all_msgs.append([tx, msg, msg, ''])
+
+ all_msgs_copy = all_msgs.copy()
+
+ try:
+ while (all_msgs != []):
+ event = ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived,
+ is_sms_partial_match_among_multiple_sms,
+ max_wait_time,
+ phonenumber_tx,
+ phonenumber_tx2,
+ messages[phonenumber_tx],
+ messages[phonenumber_tx2])
+ event_text = event['data']['Text'].split(")")[-1].strip()
+ event_text_length = len(event_text)
+
+ for msg in all_msgs_copy:
+ if event_text in msg[2]:
+ ad_rx.log.info("Got event %s of text length %s from %s",
+ EventSmsReceived, event_text_length,
+ msg[0])
+ msg[2] = msg[2][event_text_length:]
+ msg[3] += event_text
+
+ if msg[2] == "":
+ all_msgs.remove(msg)
+
+ ad_rx.log.info("Received all SMS' sent when power-off.")
+ except Empty:
+ ad_rx.log.error(
+ "Missing SMS received event.")
+
+ for msg in all_msgs_copy:
+ if msg[3] != '':
+ ad_rx.log.error(
+ "Only received partial matched SMS of length %s from %s",
+ len(msg[3]), msg[0])
+ return False
+
+ return True
+
+def is_sms_in_collision_match(
+ event, phonenumber_tx, phonenumber_tx2, text, text2):
+ event_text = event['data']['Text'].strip()
+ if event_text.startswith("("):
+ event_text = event_text.split(")")[-1]
+
+ for phonenumber, txt in [[phonenumber_tx, text], [phonenumber_tx2, text2]]:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber) and txt.startswith(event_text):
+ return True
+ return False
+
+def is_sms_in_collision_partial_match(
+ event, phonenumber_tx, phonenumber_tx2, text, text2):
+ for phonenumber, txt in [[phonenumber_tx, text], [phonenumber_tx2, text2]]:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber) and \
+ event['data']['Text'].strip() == txt:
+ return True
+ return False
+
+def is_sms_match_among_multiple_sms(
+ event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]):
+ for txt in texts:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber_tx) and \
+ event['data']['Text'].strip() == txt:
+ return True
+
+ for txt in texts2:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber_tx2) and \
+ event['data']['Text'].strip() == txt:
+ return True
+
+ return False
+
+def is_sms_partial_match_among_multiple_sms(
+ event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]):
+ event_text = event['data']['Text'].strip()
+ if event_text.startswith("("):
+ event_text = event_text.split(")")[-1]
+
+ for txt in texts:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber_tx) and \
+ txt.startswith(event_text):
+ return True
+
+ for txt in texts2:
+ if check_phone_number_match(
+ event['data']['Sender'], phonenumber_tx2) and \
+ txt.startswith(event_text):
+ return True
+
+ return False
+
+def set_time_sync_from_network(ad, action):
+ if (action == 'enable'):
+ ad.log.info('Enabling sync time from network.')
+ ad.adb.shell('settings put global auto_time 1')
+
+ elif (action == 'disable'):
+ ad.log.info('Disabling sync time from network.')
+ ad.adb.shell('settings put global auto_time 0')
+
+ time.sleep(WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK)
+
+def datetime_handle(ad, action, set_datetime_value='', get_year=False):
+ get_value = ''
+ if (action == 'get'):
+ if (get_year):
+ datetime_string = ad.adb.shell('date')
+ datetime_list = datetime_string.split()
+ try:
+ get_value = datetime_list[5]
+ except Exception as e:
+ self.log.error("Fail to get year from datetime: %s. " \
+ "Exception error: %s", datetime_list
+ , str(e))
+ raise signals.TestSkip("Fail to get year from datetime" \
+ ", the format is changed. Skip the test.")
+ else:
+ get_value = ad.adb.shell('date')
+
+ elif (action == 'set'):
+ ad.adb.shell('date %s' % set_datetime_value)
+ time.sleep(WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK)
+ ad.adb.shell('am broadcast -a android.intent.action.TIME_SET')
+
+ return get_value
+
+def wait_for_sending_sms(ad_tx, max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ try:
+ events = ad_tx.messaging_ed.pop_events(
+ "(%s|%s|%s|%s)" %
+ (EventSmsSentSuccess, EventSmsSentFailure,
+ EventSmsDeliverSuccess,
+ EventSmsDeliverFailure), max_wait_time)
+ for event in events:
+ ad_tx.log.info("Got event %s", event["name"])
+ if event["name"] == EventSmsSentFailure or \
+ event["name"] == EventSmsDeliverFailure:
+ if event.get("data") and event["data"].get("Reason"):
+ ad_tx.log.error("%s with reason: %s",
+ event["name"],
+ event["data"]["Reason"])
+ return False
+ elif event["name"] == EventSmsSentSuccess or \
+ event["name"] == EventSmsDeliverSuccess:
+ return True
+ except Empty:
+ ad_tx.log.error("No %s or %s event for SMS.",
+ EventSmsSentSuccess, EventSmsSentFailure)
+ return False
+
+def wait_for_call_end(
+ log,
+ ad_caller,
+ ad_callee,
+ ad_hangup,
+ verify_caller_func,
+ verify_callee_func,
+ check_interval=5,
+ tel_result_wrapper=TelResultWrapper(CallResult('SUCCESS')),
+ wait_time_in_call=WAIT_TIME_IN_CALL):
+ elapsed_time = 0
+ while (elapsed_time < wait_time_in_call):
+ check_interval = min(check_interval, wait_time_in_call - elapsed_time)
+ time.sleep(check_interval)
+ elapsed_time += check_interval
+ time_message = "at <%s>/<%s> second." % (elapsed_time, wait_time_in_call)
+ for ad, call_func in [(ad_caller, verify_caller_func),
+ (ad_callee, verify_callee_func)]:
+ if not call_func(log, ad):
+ ad.log.error(
+ "NOT in correct %s state at %s, voice in RAT %s",
+ call_func.__name__,
+ time_message,
+ ad.droid.telephonyGetCurrentVoiceNetworkType())
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED')
+ else:
+ ad.log.info("In correct %s state at %s",
+ call_func.__name__, time_message)
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state at %s", time_message)
+ tel_result_wrapper.result_value = CallResult(
+ 'AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED')
+ if not tel_result_wrapper:
+ return tel_result_wrapper
+
+ if ad_hangup:
+ if not hangup_call(log, ad_hangup):
+ ad_hangup.log.info("Failed to hang up the call")
+ tel_result_wrapper.result_value = CallResult('CALL_HANGUP_FAIL')
+
+ if not tel_result_wrapper:
+ for ad in (ad_caller, ad_callee):
+ last_call_drop_reason(ad, begin_time)
+ try:
+ if ad.droid.telecomIsInCall():
+ ad.log.info("In call. End now.")
+ ad.droid.telecomEndCall()
+ except Exception as e:
+ log.error(str(e))
+ if ad_hangup or not tel_result_wrapper:
+ for ad in (ad_caller, ad_callee):
+ if not wait_for_call_id_clearing(ad, getattr(ad, "caller_ids", [])):
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_ID_CLEANUP_FAIL')
+
+ return tel_result_wrapper
+
+def voice_call_in_collision_with_mt_sms_msim(
+ log,
+ ad_primary,
+ ad_sms,
+ ad_voice,
+ sms_subid_ad_primary,
+ sms_subid_ad_sms,
+ voice_subid_ad_primary,
+ voice_subid_ad_voice,
+ array_message,
+ ad_hangup=None,
+ verify_caller_func=None,
+ verify_callee_func=None,
+ call_direction="mo",
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ dialing_number_length=None,
+ video_state=None):
+
+ ad_tx = ad_sms
+ ad_rx = ad_primary
+ subid_tx = sms_subid_ad_sms
+ subid_rx = sms_subid_ad_primary
+
+ if call_direction == "mo":
+ ad_caller = ad_primary
+ ad_callee = ad_voice
+ subid_caller = voice_subid_ad_primary
+ subid_callee = voice_subid_ad_voice
+ elif call_direction == "mt":
+ ad_callee = ad_primary
+ ad_caller = ad_voice
+ subid_callee = voice_subid_ad_primary
+ subid_caller = voice_subid_ad_voice
+
+
+ phonenumber_tx = ad_tx.telephony['subscription'][subid_tx]['phone_num']
+ phonenumber_rx = ad_rx.telephony['subscription'][subid_rx]['phone_num']
+
+ tel_result_wrapper = TelResultWrapper(CallResult('SUCCESS'))
+
+ for ad in (ad_tx, ad_rx):
+ ad.send_keycode("BACK")
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ ad.messaging_ed.clear_all_events()
+ except Exception:
+ ad.log.info("Create new sl4a session for messaging")
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
+ begin_time = get_current_epoch_time()
+ if not verify_caller_func:
+ verify_caller_func = is_phone_in_call
+ if not verify_callee_func:
+ verify_callee_func = is_phone_in_call
+
+ caller_number = ad_caller.telephony['subscription'][subid_caller][
+ 'phone_num']
+ callee_number = ad_callee.telephony['subscription'][subid_callee][
+ 'phone_num']
+ if dialing_number_length:
+ skip_test = False
+ trunc_position = 0 - int(dialing_number_length)
+ try:
+ caller_area_code = caller_number[:trunc_position]
+ callee_area_code = callee_number[:trunc_position]
+ callee_dial_number = callee_number[trunc_position:]
+ except:
+ skip_test = True
+ if caller_area_code != callee_area_code:
+ skip_test = True
+ if skip_test:
+ msg = "Cannot make call from %s to %s by %s digits" % (
+ caller_number, callee_number, dialing_number_length)
+ ad_caller.log.info(msg)
+ raise signals.TestSkip(msg)
+ else:
+ callee_number = callee_dial_number
+
+ msg = "Call from %s to %s" % (caller_number, callee_number)
+ if video_state:
+ msg = "Video %s" % msg
+ video = True
+ else:
+ video = False
+ if ad_hangup:
+ msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+ ad_caller.log.info(msg)
+
+ for ad in (ad_caller, ad_callee):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ if call_ids:
+ ad.log.info("Pre-exist CallId %s before making call", call_ids)
+
+ ad_caller.ed.clear_events(EventCallStateChanged)
+ begin_time = get_device_epoch_time(ad)
+ ad_caller.droid.telephonyStartTrackingCallStateForSubscription(subid_caller)
+
+ for text in array_message:
+ length = len(text)
+ ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+ phonenumber_tx, phonenumber_rx, length, text)
+ try:
+ ad_rx.messaging_ed.clear_events(EventSmsReceived)
+ ad_tx.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_tx.messaging_ed.clear_events(EventSmsSentFailure)
+ ad_rx.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ time.sleep(1) #sleep 100ms after starting event tracking
+ ad_tx.messaging_droid.logI("Sending SMS of length %s" % length)
+ ad_rx.messaging_droid.logI("Expecting SMS of length %s" % length)
+ ad_caller.log.info("Make a phone call to %s", callee_number)
+
+ tasks = [
+ (ad_tx.messaging_droid.smsSendTextMessage,
+ (phonenumber_rx, text, True)),
+ (ad_caller.droid.telecomCallNumber,
+ (callee_number, video))]
+
+ run_multithread_func(log, tasks)
+
+ try:
+ # Verify OFFHOOK state
+ if not wait_for_call_offhook_for_subscription(
+ log,
+ ad_caller,
+ subid_caller,
+ event_tracking_started=True):
+ ad_caller.log.info(
+ "sub_id %s not in call offhook state", subid_caller)
+ last_call_drop_reason(ad_caller, begin_time=begin_time)
+
+ ad_caller.log.error("Initiate call failed.")
+ tel_result_wrapper.result_value = CallResult(
+ 'INITIATE_FAILED')
+ return tel_result_wrapper
+ else:
+ ad_caller.log.info("Caller initate call successfully")
+ finally:
+ ad_caller.droid.telephonyStopTrackingCallStateChangeForSubscription(
+ subid_caller)
+ if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+ ad_caller.droid.telecomShowInCallScreen()
+ elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+ ad_caller.droid.showHomeScreen()
+
+ if not wait_and_answer_call_for_subscription(
+ log,
+ ad_callee,
+ subid_callee,
+ incoming_number=caller_number,
+ caller=ad_caller,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state):
+ ad_callee.log.error("Answer call fail.")
+ tel_result_wrapper.result_value = CallResult(
+ 'NO_RING_EVENT_OR_ANSWER_FAILED')
+ return tel_result_wrapper
+ else:
+ ad_callee.log.info("Callee answered the call successfully")
+
+ for ad, call_func in zip([ad_caller, ad_callee],
+ [verify_caller_func, verify_callee_func]):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_ids = set(call_ids) - set(ad.call_ids)
+ if not new_call_ids:
+ ad.log.error(
+ "No new call ids are found after call establishment")
+ ad.log.error("telecomCallGetCallIds returns %s",
+ ad.droid.telecomCallGetCallIds())
+ tel_result_wrapper.result_value = CallResult(
+ 'NO_CALL_ID_FOUND')
+ for new_call_id in new_call_ids:
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT')
+ else:
+ ad.log.info(
+ "callProperties = %s",
+ ad.droid.telecomCallGetProperties(new_call_id))
+
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state")
+ tel_result_wrapper.result_value = CallResult(
+ 'AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT')
+
+ if call_func(log, ad):
+ ad.log.info("Call is in %s state", call_func.__name__)
+ else:
+ ad.log.error("Call is not in %s state, voice in RAT %s",
+ call_func.__name__,
+ ad.droid.telephonyGetCurrentVoiceNetworkType())
+ tel_result_wrapper.result_value = CallResult(
+ 'CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT')
+ if not tel_result_wrapper:
+ return tel_result_wrapper
+
+ if not wait_for_sending_sms(
+ ad_tx,
+ max_wait_time=MAX_WAIT_TIME_SMS_SENT_SUCCESS_IN_COLLISION):
+ return False
+
+ tasks = [
+ (wait_for_matching_sms,
+ (log, ad_rx, phonenumber_tx, text,
+ MAX_WAIT_TIME_SMS_RECEIVE_IN_COLLISION, True)),
+ (wait_for_call_end,
+ (log, ad_caller, ad_callee, ad_hangup, verify_caller_func,
+ verify_callee_func, 5, tel_result_wrapper,
+ WAIT_TIME_IN_CALL))]
+
+ results = run_multithread_func(log, tasks)
+
+ if not results[0]:
+ ad_rx.log.error("No matching received SMS of length %s.",
+ length)
+ return False
+
+ tel_result_wrapper = results[1]
+
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ finally:
+ ad_rx.messaging_droid.smsStopTrackingIncomingSmsMessage()
+
+ return tel_result_wrapper
+
+def change_voice_subid_temporarily(ad, sub_id, state_check_func):
+ result = False
+ voice_sub_id_changed = False
+ current_sub_id = get_incoming_voice_sub_id(ad)
+ if current_sub_id != sub_id:
+ set_incoming_voice_sub_id(ad, sub_id)
+ voice_sub_id_changed = True
+
+ if state_check_func():
+ result = True
+
+ if voice_sub_id_changed:
+ set_incoming_voice_sub_id(ad, current_sub_id)
+
+ return result
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py
new file mode 100644
index 0000000..85eecd3
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py
@@ -0,0 +1,949 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+from queue import Empty
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_UMTS
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_RX_PAUSED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_TX_PAUSED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_STATE_INVALID
+from acts_contrib.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import EventCallStateChanged
+from acts_contrib.test_utils.tel.tel_defines import EventTelecomVideoCallSessionModifyRequestReceived
+from acts_contrib.test_utils.tel.tel_defines import EventTelecomVideoCallSessionModifyResponseReceived
+from acts_contrib.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED
+from acts_contrib.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED
+from acts_contrib.test_utils.tel.tel_defines import CallStateContainer
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import is_event_match
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ringing_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_telecom_ringing
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_video_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import is_wfc_enabled
+from acts_contrib.test_utils.tel.tel_voice_utils import is_call_hd
+
+
+def phone_setup_video(log, ad, wfc_mode=WFC_MODE_DISABLED):
+ """Setup phone default sub_id to make video call
+
+ Args:
+ log: log object.
+ ad: android device object
+ wfc_mode: WFC mode to set to.
+ Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
+
+ Returns:
+ True if ad (default sub_id) is setup correctly and idle for video call.
+ """
+ return phone_setup_video_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad),
+ wfc_mode)
+
+
+def phone_setup_video_for_subscription(log,
+ ad,
+ sub_id,
+ wfc_mode=WFC_MODE_DISABLED):
+ """Setup phone sub_id to make video call
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: ad's sub id.
+ wfc_mode: WFC mode to set to.
+ Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+ WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
+
+ Returns:
+ True if ad (sub_id) is setup correctly and idle for video call.
+ """
+
+ toggle_airplane_mode(log, ad, False)
+ if not set_wfc_mode(log, ad, wfc_mode):
+ log.error("{} WFC mode failed to be set to {}.".format(
+ ad.serial, wfc_mode))
+ return False
+ toggle_volte(log, ad, True)
+
+ if not ensure_network_generation(
+ log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+ log.error("{} voice not in LTE mode.".format(ad.serial))
+ return False
+
+ return phone_idle_video_for_subscription(log, ad, sub_id)
+
+
+def phone_idle_video(log, ad):
+ """Return if phone (default sub_id) is idle for video call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if ad is idle for video call.
+ """
+ return phone_idle_video_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_video_for_subscription(log, ad, sub_id):
+ """Return if phone (sub_id) is idle for video call.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: ad's sub id
+
+ Returns:
+ True if ad (sub_id) is idle for video call.
+ """
+
+ if not wait_for_network_generation(log, ad, GEN_4G):
+ log.error("{} voice not in LTE mode.".format(ad.serial))
+ return False
+
+ if not wait_for_video_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED):
+ log.error(
+ "{} failed to <report video calling enabled> within {}s.".format(
+ ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
+ return False
+ return True
+
+
+def is_phone_in_call_video(log, ad):
+ """Return if ad is in a video call (in expected video state).
+
+ Args:
+ log: log object.
+ ad: android device object
+ video_state: Expected Video call state.
+ This is optional, if it's None,
+ then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
+ return True.
+
+ Returns:
+ True if ad (for sub_id) is in a video call (in expected video state).
+ """
+ return is_phone_in_call_video_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_video_for_subscription(log, ad, sub_id, video_state=None):
+ """Return if ad (for sub_id) is in a video call (in expected video state).
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: device sub_id
+ video_state: Expected Video call state.
+ This is optional, if it's None,
+ then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
+ return True.
+
+ Returns:
+ True if ad is in a video call (in expected video state).
+ """
+
+ if video_state is None:
+ log.info("Verify if {}(subid {}) in video call.".format(
+ ad.serial, sub_id))
+ if not ad.droid.telecomIsInCall():
+ log.error("{} not in call.".format(ad.serial))
+ return False
+ call_list = ad.droid.telecomCallGetCallIds()
+ for call in call_list:
+ state = ad.droid.telecomCallVideoGetState(call)
+ if video_state is None:
+ if {
+ VT_STATE_AUDIO_ONLY: False,
+ VT_STATE_TX_ENABLED: True,
+ VT_STATE_TX_PAUSED: True,
+ VT_STATE_RX_ENABLED: True,
+ VT_STATE_RX_PAUSED: True,
+ VT_STATE_BIDIRECTIONAL: True,
+ VT_STATE_BIDIRECTIONAL_PAUSED: True,
+ VT_STATE_STATE_INVALID: False
+ }[state]:
+ return True
+ else:
+ if state == video_state:
+ return True
+ log.info("Non-Video-State: {}".format(state))
+ log.error("Phone not in video call. Call list: {}".format(call_list))
+ return False
+
+
+def is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
+ video_state=None):
+ """Return if ad (for sub_id) is in a viwifi call (in expected video state).
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: device sub_id
+ video_state: Expected Video call state.
+ This is optional, if it's None,
+ then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
+ return True.
+
+ Returns:
+ True if ad is in a video call (in expected video state).
+ """
+
+ if video_state is None:
+ log.info("Verify if {}(subid {}) in video call.".format(
+ ad.serial, sub_id))
+ if not ad.droid.telecomIsInCall():
+ log.error("{} not in call.".format(ad.serial))
+ return False
+ nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
+ if nw_type != RAT_IWLAN:
+ ad.log.error("Data rat on: %s. Expected: iwlan", nw_type)
+ return False
+ if not is_wfc_enabled(log, ad):
+ ad.log.error("WiFi Calling feature bit is False.")
+ return False
+ call_list = ad.droid.telecomCallGetCallIds()
+ for call in call_list:
+ state = ad.droid.telecomCallVideoGetState(call)
+ if video_state is None:
+ if {
+ VT_STATE_AUDIO_ONLY: False,
+ VT_STATE_TX_ENABLED: True,
+ VT_STATE_TX_PAUSED: True,
+ VT_STATE_RX_ENABLED: True,
+ VT_STATE_RX_PAUSED: True,
+ VT_STATE_BIDIRECTIONAL: True,
+ VT_STATE_BIDIRECTIONAL_PAUSED: True,
+ VT_STATE_STATE_INVALID: False
+ }[state]:
+ return True
+ else:
+ if state == video_state:
+ return True
+ ad.log.info("Non-Video-State: %s", state)
+ ad.log.error("Phone not in video call. Call list: %s", call_list)
+ return False
+
+
+def is_phone_in_call_video_bidirectional(log, ad):
+ """Return if phone in bi-directional video call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in bi-directional video call.
+ """
+ return is_phone_in_call_video_bidirectional_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_video_bidirectional_for_subscription(log, ad, sub_id):
+ """Return if phone in bi-directional video call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in bi-directional video call.
+ """
+ log.info("Verify if {}(subid {}) in bi-directional video call.".format(
+ ad.serial, sub_id))
+ return is_phone_in_call_video_for_subscription(log, ad, sub_id,
+ VT_STATE_BIDIRECTIONAL)
+
+
+def is_phone_in_call_viwifi_bidirectional(log, ad):
+ """Return if phone in bi-directional viwifi call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in bi-directional viwifi call.
+ """
+ return is_phone_in_call_viwifi_bidirectional_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_viwifi_bidirectional_for_subscription(log, ad, sub_id):
+ """Return if phone in bi-directional viwifi call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in bi-directional viwifi call.
+ """
+ ad.log.info("Verify if subid %s in bi-directional video call.", sub_id)
+ return is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
+ VT_STATE_BIDIRECTIONAL)
+
+
+def is_phone_in_call_video_tx_enabled(log, ad):
+ """Return if phone in tx_enabled video call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in tx_enabled video call.
+ """
+ return is_phone_in_call_video_tx_enabled_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_video_tx_enabled_for_subscription(log, ad, sub_id):
+ """Return if phone in tx_enabled video call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in tx_enabled video call.
+ """
+ log.info("Verify if {}(subid {}) in tx_enabled video call.".format(
+ ad.serial, sub_id))
+ return is_phone_in_call_video_for_subscription(log, ad, sub_id,
+ VT_STATE_TX_ENABLED)
+
+
+def is_phone_in_call_video_rx_enabled(log, ad):
+ """Return if phone in rx_enabled video call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in rx_enabled video call.
+ """
+ return is_phone_in_call_video_rx_enabled_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_video_rx_enabled_for_subscription(log, ad, sub_id):
+ """Return if phone in rx_enabled video call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in rx_enabled video call.
+ """
+ log.info("Verify if {}(subid {}) in rx_enabled video call.".format(
+ ad.serial, sub_id))
+ return is_phone_in_call_video_for_subscription(log, ad, sub_id,
+ VT_STATE_RX_ENABLED)
+
+
+def is_phone_in_call_voice_hd(log, ad):
+ """Return if phone in hd voice call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in hd voice call.
+ """
+ return is_phone_in_call_voice_hd_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_voice_hd_for_subscription(log, ad, sub_id):
+ """Return if phone in hd voice call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in hd voice call.
+ """
+ log.info("Verify if {}(subid {}) in hd voice call.".format(
+ ad.serial, sub_id))
+ if not ad.droid.telecomIsInCall():
+ log.error("{} not in call.".format(ad.serial))
+ return False
+ for call in ad.droid.telecomCallGetCallIds():
+ state = ad.droid.telecomCallVideoGetState(call)
+ if (state == VT_STATE_AUDIO_ONLY and is_call_hd(log, ad, call)):
+ return True
+ log.info("Non-HDAudio-State: {}, property: {}".format(
+ state, ad.droid.telecomCallGetProperties(call)))
+ return False
+
+
+def initiate_video_call(log, ad_caller, callee_number):
+ """Make phone call from caller to callee.
+
+ Args:
+ log: logging handle
+ ad_caller: Caller android device object.
+ callee_number: Callee phone number.
+
+ Returns:
+ result: if phone call is placed successfully.
+ """
+ return initiate_call(log, ad_caller, callee_number, video=True)
+
+
+def wait_and_answer_video_call(log,
+ ad,
+ incoming_number=None,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """Wait for an incoming call on default voice subscription and
+ accepts the call.
+
+ Args:
+ ad: android device object.
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ return wait_and_answer_video_call_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad), incoming_number, video_state,
+ incall_ui_display)
+
+
+def wait_and_answer_video_call_for_subscription(
+ log,
+ ad,
+ sub_id,
+ incoming_number=None,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """Wait for an incoming call on specified subscription and
+ accepts the call.
+
+ Args:
+ ad: android device object.
+ sub_id: subscription ID
+ incoming_number: Expected incoming number.
+ Optional. Default is None
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True: if incoming call is received and answered successfully.
+ False: for errors
+ """
+ return wait_and_answer_call_for_subscription(
+ log,
+ ad,
+ sub_id,
+ incoming_number=None,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+ video_state=video_state)
+
+
+def video_call_setup_teardown(log,
+ ad_caller,
+ ad_callee,
+ ad_hangup=None,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ verify_caller_func=None,
+ verify_callee_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on default subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ video_state: video state for VT call.
+ Optional. Default value is VT_STATE_BIDIRECTIONAL
+ verify_caller_func: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ wait_time_in_call: wait time during call.
+ Optional. Default is WAIT_TIME_IN_CALL.
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ return video_call_setup_teardown_for_subscription(
+ log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
+ get_incoming_voice_sub_id(ad_callee), ad_hangup, video_state,
+ verify_caller_func, verify_callee_func, wait_time_in_call,
+ incall_ui_display)
+
+
+def video_call_setup_teardown_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ ad_hangup=None,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ verify_caller_func=None,
+ verify_callee_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on specified subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ subid_caller: Caller subscription ID
+ subid_callee: Callee subscription ID
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ video_state: video state for VT call.
+ Optional. Default value is VT_STATE_BIDIRECTIONAL
+ verify_caller_func: func_ptr to verify caller in correct mode
+ Optional. Default is None
+ verify_callee_func: func_ptr to verify callee in correct mode
+ Optional. Default is None
+ wait_time_in_call: wait time during call.
+ Optional. Default is WAIT_TIME_IN_CALL.
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ return call_setup_teardown_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ ad_hangup=ad_hangup,
+ verify_caller_func=verify_caller_func,
+ verify_callee_func=verify_callee_func,
+ wait_time_in_call=wait_time_in_call,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state)
+
+
+def video_call_setup(log,
+ ad_caller,
+ ad_callee,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on default subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ return video_call_setup_for_subscription(
+ log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
+ get_incoming_voice_sub_id(ad_callee), video_state, incall_ui_display)
+
+
+def video_call_setup_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ video_state=VT_STATE_BIDIRECTIONAL,
+ incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+ """ Call process, including make a phone call from caller,
+ accept from callee, and hang up. The call is on specified subscription
+
+ In call process, call from <droid_caller> to <droid_callee>,
+ accept the call, (optional)then hang up from <droid_hangup>.
+
+ Args:
+ ad_caller: Caller Android Device Object.
+ ad_callee: Callee Android Device Object.
+ subid_caller: Caller subscription ID
+ subid_callee: Callee subscription ID
+ ad_hangup: Android Device Object end the phone call.
+ Optional. Default value is None, and phone call will continue.
+ incall_ui_display: after answer the call, bring in-call UI to foreground or
+ background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+ if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+ if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+ else, do nothing.
+
+ Returns:
+ True if call process without any error.
+ False if error happened.
+
+ """
+ return call_setup_teardown_for_subscription(
+ log,
+ ad_caller,
+ ad_callee,
+ subid_caller,
+ subid_callee,
+ ad_hangup=None,
+ incall_ui_display=incall_ui_display,
+ video_state=video_state)
+
+
+def video_call_modify_video(log,
+ ad_requester,
+ call_id_requester,
+ ad_responder,
+ call_id_responder,
+ video_state_request,
+ video_quality_request=VT_VIDEO_QUALITY_DEFAULT,
+ video_state_response=None,
+ video_quality_response=None,
+ verify_func_between_request_and_response=None):
+ """Modifies an ongoing call to change the video_call state
+
+ Args:
+ log: logger object
+ ad_requester: android_device object of the requester
+ call_id_requester: the call_id of the call placing the modify request
+ ad_requester: android_device object of the responder
+ call_id_requester: the call_id of the call receiving the modify request
+ video_state_request: the requested video state
+ video_quality_request: the requested video quality, defaults to
+ QUALITY_DEFAULT
+ video_state_response: the responded video state or, or (default)
+ match the request if None
+ video_quality_response: the responded video quality, or (default)
+ match the request if None
+
+ Returns:
+ A call_id corresponding to the first call in the state, or None
+ """
+
+ if not video_state_response:
+ video_state_response = video_state_request
+ if not video_quality_response:
+ video_quality_response = video_quality_request
+
+ cur_video_state = ad_requester.droid.telecomCallVideoGetState(
+ call_id_requester)
+
+ log.info("State change request from {} to {} requested".format(
+ cur_video_state, video_state_request))
+
+ if cur_video_state == video_state_request:
+ return True
+
+ ad_responder.ed.clear_events(
+ EventTelecomVideoCallSessionModifyRequestReceived)
+
+ ad_responder.droid.telecomCallVideoStartListeningForEvent(
+ call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
+
+ ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
+ call_id_requester, video_state_request, video_quality_request)
+
+ try:
+ request_event = ad_responder.ed.pop_event(
+ EventTelecomVideoCallSessionModifyRequestReceived,
+ MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+ log.info(request_event)
+ except Empty:
+ log.error("Failed to receive SessionModifyRequest!")
+ return False
+ finally:
+ ad_responder.droid.telecomCallVideoStopListeningForEvent(
+ call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
+
+ if (verify_func_between_request_and_response
+ and not verify_func_between_request_and_response()):
+ log.error("verify_func_between_request_and_response failed.")
+ return False
+
+ # TODO: b/26291165 Replace with reducing the volume as we want
+ # to test route switching
+ ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+ ad_requester.droid.telecomCallVideoStartListeningForEvent(
+ call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+
+ ad_responder.droid.telecomCallVideoSendSessionModifyResponse(
+ call_id_responder, video_state_response, video_quality_response)
+
+ try:
+ response_event = ad_requester.ed.pop_event(
+ EventTelecomVideoCallSessionModifyResponseReceived,
+ MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+ log.info(response_event)
+ except Empty:
+ log.error("Failed to receive SessionModifyResponse!")
+ return False
+ finally:
+ ad_requester.droid.telecomCallVideoStopListeningForEvent(
+ call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+
+ # TODO: b/26291165 Replace with reducing the volume as we want
+ # to test route switching
+ ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+ return True
+
+
+def is_call_id_in_video_state(log, ad, call_id, video_state):
+ """Return is the call_id is in expected video_state
+
+ Args:
+ log: logger object
+ ad: android_device object
+ call_id: call id
+ video_state: valid VIDEO_STATE
+
+ Returns:
+ True is call_id in expected video_state; False if not.
+ """
+ return video_state == ad.droid.telecomCallVideoGetState(call_id)
+
+
+def get_call_id_in_video_state(log, ad, video_state):
+ """Gets the first call reporting a given video_state
+ from among the active calls
+
+ Args:
+ log: logger object
+ ad: android_device object
+ video_state: valid VIDEO_STATE
+
+ Returns:
+ A call_id corresponding to the first call in the state, or None
+ """
+
+ if not ad.droid.telecomIsInCall():
+ log.error("{} not in call.".format(ad.serial))
+ return None
+ for call in ad.droid.telecomCallGetCallIds():
+ if is_call_id_in_video_state(log, ad, call, video_state):
+ return call
+ return None
+
+
+def video_call_downgrade(log,
+ ad_requester,
+ call_id_requester,
+ ad_responder,
+ call_id_responder,
+ video_state_request=None,
+ video_quality_request=VT_VIDEO_QUALITY_DEFAULT):
+ """Downgrade Video call to video_state_request.
+ Send telecomCallVideoSendSessionModifyRequest from ad_requester.
+ Get video call state from ad_requester and ad_responder.
+ Verify video calls states are correct and downgrade succeed.
+
+ Args:
+ log: logger object
+ ad_requester: android_device object of the requester
+ call_id_requester: the call_id of the call placing the modify request
+ ad_requester: android_device object of the responder
+ call_id_requester: the call_id of the call receiving the modify request
+ video_state_request: the requested downgrade video state
+ This parameter is optional. If this parameter is None:
+ if call_id_requester current is bi-directional, will downgrade to RX_ENABLED
+ if call_id_requester current is RX_ENABLED, will downgrade to AUDIO_ONLY
+ video_quality_request: the requested video quality, defaults to
+ QUALITY_DEFAULT
+ Returns:
+ True if downgrade succeed.
+ """
+ if (call_id_requester is None) or (call_id_responder is None):
+ log.error("call_id_requester: {}, call_id_responder: {}".format(
+ call_id_requester, call_id_responder))
+ return False
+ current_video_state_requester = ad_requester.droid.telecomCallVideoGetState(
+ call_id_requester)
+ if video_state_request is None:
+ if (current_video_state_requester == VT_STATE_BIDIRECTIONAL or
+ current_video_state_requester == VT_STATE_BIDIRECTIONAL_PAUSED
+ ):
+ video_state_request = VT_STATE_RX_ENABLED
+ elif (current_video_state_requester == VT_STATE_TX_ENABLED
+ or current_video_state_requester == VT_STATE_TX_PAUSED):
+ video_state_request = VT_STATE_AUDIO_ONLY
+ else:
+ log.error("Can Not Downgrade. ad: {}, current state {}".format(
+ ad_requester.serial, current_video_state_requester))
+ return False
+ expected_video_state_responder = {
+ VT_STATE_AUDIO_ONLY: VT_STATE_AUDIO_ONLY,
+ VT_STATE_RX_ENABLED: VT_STATE_TX_ENABLED
+ }[video_state_request]
+
+ ad_requester.droid.telecomCallVideoStartListeningForEvent(
+ call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+
+ ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
+ call_id_requester, video_state_request, video_quality_request)
+
+ try:
+ response_event = ad_requester.ed.pop_event(
+ EventTelecomVideoCallSessionModifyResponseReceived,
+ MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+ log.info(response_event)
+ except Empty:
+ log.error("Failed to receive SessionModifyResponse!")
+ return False
+ finally:
+ ad_requester.droid.telecomCallVideoStopListeningForEvent(
+ call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ # TODO: b/26291165 Replace with reducing the volume as we want
+ # to test route switching
+ ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+ ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+ time.sleep(WAIT_TIME_IN_CALL)
+ if video_state_request != ad_requester.droid.telecomCallVideoGetState(
+ call_id_requester):
+ log.error("requester not in correct state. expected:{}, current:{}"
+ .format(video_state_request,
+ ad_requester.droid.telecomCallVideoGetState(
+ call_id_requester)))
+ return False
+ if (expected_video_state_responder !=
+ ad_responder.droid.telecomCallVideoGetState(call_id_responder)):
+ log.error(
+ "responder not in correct state. expected:{}, current:{}".format(
+ expected_video_state_responder,
+ ad_responder.droid.telecomCallVideoGetState(
+ call_id_responder)))
+ return False
+
+ return True
+
+
+def verify_video_call_in_expected_state(log, ad, call_id, call_video_state,
+ call_state):
+ """Return True if video call is in expected video state and call state.
+
+ Args:
+ log: logger object
+ ad: android_device object
+ call_id: ad's call id
+ call_video_state: video state to validate.
+ call_state: call state to validate.
+
+ Returns:
+ True if video call is in expected video state and call state.
+ """
+ if not is_call_id_in_video_state(log, ad, call_id, call_video_state):
+ log.error("Call is not in expected {} state. Current state {}".format(
+ call_video_state, ad.droid.telecomCallVideoGetState(call_id)))
+ return False
+ if ad.droid.telecomCallGetCallState(call_id) != call_state:
+ log.error("Call is not in expected {} state. Current state {}".format(
+ call_state, ad.droid.telecomCallGetCallState(call_id)))
+ return False
+ return True
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py
new file mode 100644
index 0000000..7868d86
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py
@@ -0,0 +1,1970 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+from acts import signals
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_HIGH_DEF_AUDIO
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import GEN_5G
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_UMTS
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_reject_leave_message
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown_for_call_forwarding
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown_for_call_waiting
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ ensure_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ ensure_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_gen_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import is_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ reset_preferred_network_type_to_allowable_range
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_test_utils import TelResultWrapper
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ wait_for_data_attach_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_enhanced_4g_lte_setting
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ wait_for_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_not_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ wait_for_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ wait_for_not_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_volte_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import \
+ wait_for_voice_attach_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+
+CallResult = TelephonyVoiceTestResult.CallResult.Value
+
+
+def two_phone_call_leave_voice_mail(
+ log,
+ caller,
+ caller_idle_func,
+ caller_in_call_check_func,
+ callee,
+ callee_idle_func,
+ wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
+ """Call from caller to callee, reject on callee, caller leave a voice mail.
+
+ 1. Caller call Callee.
+ 2. Callee reject incoming call.
+ 3. Caller leave a voice mail.
+ 4. Verify callee received the voice mail notification.
+
+ Args:
+ caller: caller android device object.
+ caller_idle_func: function to check caller's idle state.
+ caller_in_call_check_func: function to check caller's in-call state.
+ callee: callee android device object.
+ callee_idle_func: function to check callee's idle state.
+ wait_time_in_call: time to wait when leaving a voice mail.
+ This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
+
+ Returns:
+ True: if voice message is received on callee successfully.
+ False: for errors
+ """
+
+ ads = [caller, callee]
+
+ # Make sure phones are idle.
+ ensure_phones_idle(log, ads)
+ if caller_idle_func and not caller_idle_func(log, caller):
+ caller.log.error("Caller Failed to Reselect")
+ return False
+ if callee_idle_func and not callee_idle_func(log, callee):
+ callee.log.error("Callee Failed to Reselect")
+ return False
+
+ # TODO: b/26337871 Need to use proper API to check phone registered.
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ # Make call and leave a message.
+ if not call_reject_leave_message(
+ log, caller, callee, caller_in_call_check_func, wait_time_in_call):
+ log.error("make a call and leave a message failed.")
+ return False
+ return True
+
+
+def two_phone_call_short_seq(log,
+ phone_a,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_b_idle_func,
+ phone_b_in_call_check_func,
+ call_sequence_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL):
+ """Call process short sequence.
+ 1. Ensure phone idle and in idle_func check return True.
+ 2. Call from PhoneA to PhoneB, accept on PhoneB.
+ 3. Check phone state, hangup on PhoneA.
+ 4. Ensure phone idle and in idle_func check return True.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB.
+ 6. Check phone state, hangup on PhoneB.
+
+ Args:
+ phone_a: PhoneA's android device object.
+ phone_a_idle_func: function to check PhoneA's idle state.
+ phone_a_in_call_check_func: function to check PhoneA's in-call state.
+ phone_b: PhoneB's android device object.
+ phone_b_idle_func: function to check PhoneB's idle state.
+ phone_b_in_call_check_func: function to check PhoneB's in-call state.
+ call_sequence_func: default parameter, not implemented.
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+
+ Returns:
+ TelResultWrapper which will evaluate as False if error.
+ """
+ ads = [phone_a, phone_b]
+
+ call_params = [
+ (ads[0], ads[1], ads[0], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ (ads[0], ads[1], ads[1], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ ]
+
+ tel_result = TelResultWrapper(CallResult('SUCCESS'))
+ for param in call_params:
+ # Make sure phones are idle.
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+ if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+ phone_b.log.error("Phone B Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+
+ # TODO: b/26337871 Need to use proper API to check phone registered.
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ # Make call.
+ log.info("---> Call test: %s to %s <---", param[0].serial,
+ param[1].serial)
+ tel_result = call_setup_teardown(
+ log, *param, wait_time_in_call=wait_time_in_call)
+ if not tel_result:
+ log.error("Call Iteration Failed")
+ break
+
+ return tel_result
+
+def two_phone_call_msim_short_seq(log,
+ phone_a,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_b_idle_func,
+ phone_b_in_call_check_func,
+ call_sequence_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL):
+ """Call process short sequence.
+ 1. Ensure phone idle and in idle_func check return True.
+ 2. Call from PhoneA to PhoneB, accept on PhoneB.
+ 3. Check phone state, hangup on PhoneA.
+ 4. Ensure phone idle and in idle_func check return True.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB.
+ 6. Check phone state, hangup on PhoneB.
+ Args:
+ phone_a: PhoneA's android device object.
+ phone_a_idle_func: function to check PhoneA's idle state.
+ phone_a_in_call_check_func: function to check PhoneA's in-call state.
+ phone_b: PhoneB's android device object.
+ phone_b_idle_func: function to check PhoneB's idle state.
+ phone_b_in_call_check_func: function to check PhoneB's in-call state.
+ call_sequence_func: default parameter, not implemented.
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+ Returns:
+ True: if call sequence succeed.
+ False: for errors
+ """
+ ads = [phone_a, phone_b]
+ call_params = [
+ (ads[0], ads[1], ads[0], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ (ads[0], ads[1], ads[1], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ ]
+ for param in call_params:
+ # Make sure phones are idle.
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return False
+ if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+ phone_b.log.error("Phone B Failed to Reselect")
+ return False
+ # TODO: b/26337871 Need to use proper API to check phone registered.
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+ # Make call.
+ log.info("--> Call test: %s to %s <--", phone_a.serial, phone_b.serial)
+ slots = 2
+ for slot in range(slots):
+ set_subid_for_outgoing_call(
+ ads[0], get_subid_from_slot_index(log,ads[0],slot))
+ set_subid_for_outgoing_call(
+ ads[1], get_subid_from_slot_index(log,ads[1],slot))
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+ if not call_setup_teardown(log, *param,slot_id_callee = slot,
+ wait_time_in_call=wait_time_in_call):
+ log.error("Call Iteration Failed")
+ return False
+ if not call_setup_teardown(log, *param,slot_id_callee = 1-slot,
+ wait_time_in_call=wait_time_in_call):
+ log.error("Call Iteration Failed")
+ return False
+ return True
+
+def two_phone_call_long_seq(log,
+ phone_a,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_b_idle_func,
+ phone_b_in_call_check_func,
+ call_sequence_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL):
+ """Call process long sequence.
+ 1. Ensure phone idle and in idle_func check return True.
+ 2. Call from PhoneA to PhoneB, accept on PhoneB.
+ 3. Check phone state, hangup on PhoneA.
+ 4. Ensure phone idle and in idle_func check return True.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB.
+ 6. Check phone state, hangup on PhoneB.
+ 7. Ensure phone idle and in idle_func check return True.
+ 8. Call from PhoneB to PhoneA, accept on PhoneA.
+ 9. Check phone state, hangup on PhoneA.
+ 10. Ensure phone idle and in idle_func check return True.
+ 11. Call from PhoneB to PhoneA, accept on PhoneA.
+ 12. Check phone state, hangup on PhoneB.
+
+ Args:
+ phone_a: PhoneA's android device object.
+ phone_a_idle_func: function to check PhoneA's idle state.
+ phone_a_in_call_check_func: function to check PhoneA's in-call state.
+ phone_b: PhoneB's android device object.
+ phone_b_idle_func: function to check PhoneB's idle state.
+ phone_b_in_call_check_func: function to check PhoneB's in-call state.
+ call_sequence_func: default parameter, not implemented.
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+
+ Returns:
+ TelResultWrapper which will evaluate as False if error.
+
+ """
+ ads = [phone_a, phone_b]
+
+ call_params = [
+ (ads[0], ads[1], ads[0], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ (ads[0], ads[1], ads[1], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ (ads[1], ads[0], ads[0], phone_b_in_call_check_func,
+ phone_a_in_call_check_func),
+ (ads[1], ads[0], ads[1], phone_b_in_call_check_func,
+ phone_a_in_call_check_func),
+ ]
+
+ tel_result = TelResultWrapper(CallResult('SUCCESS'))
+ for param in call_params:
+ # Make sure phones are idle.
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+ if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+ phone_b.log.error("Phone B Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+
+ # TODO: b/26337871 Need to use proper API to check phone registered.
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ # Make call.
+ log.info("---> Call test: %s to %s <---", param[0].serial,
+ param[1].serial)
+ tel_result = call_setup_teardown(
+ log, *param, wait_time_in_call=wait_time_in_call)
+ if not tel_result:
+ log.error("Call Iteration Failed")
+ break
+
+ return tel_result
+
+def two_phone_call_msim_for_slot(log,
+ phone_a,
+ phone_a_slot,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_b_slot,
+ phone_b_idle_func,
+ phone_b_in_call_check_func,
+ call_sequence_func=None,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ retry=2):
+ """Call process between 2 phones with specific slot.
+ 1. Ensure phone idle and in idle_func check return True.
+ 2. Call from PhoneA to PhoneB, accept on PhoneB.
+ 3. Check phone state, hangup on PhoneA.
+ 4. Ensure phone idle and in idle_func check return True.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB.
+ 6. Check phone state, hangup on PhoneB.
+
+ Args:
+ phone_a: PhoneA's android device object.
+ phone_a_slot: 0 or 1 (pSIM or eSIM)
+ phone_a_idle_func: function to check PhoneA's idle state.
+ phone_a_in_call_check_func: function to check PhoneA's in-call state.
+ phone_b: PhoneB's android device object.
+ phone_b_slot: 0 or 1 (pSIM or eSIM)
+ phone_b_idle_func: function to check PhoneB's idle state.
+ phone_b_in_call_check_func: function to check PhoneB's in-call state.
+ call_sequence_func: default parameter, not implemented.
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+ retry: times of retry if call_setup_teardown failed.
+
+ Returns:
+ True: if call sequence succeed.
+ False: for errors
+ """
+ ads = [phone_a, phone_b]
+
+ call_params = [
+ (ads[0], ads[1], ads[0], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ (ads[0], ads[1], ads[1], phone_a_in_call_check_func,
+ phone_b_in_call_check_func),
+ ]
+
+ tel_result = TelResultWrapper(CallResult('SUCCESS'))
+ for param in call_params:
+ # Make sure phones are idle.
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+ if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+ phone_b.log.error("Phone B Failed to Reselect")
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+
+ # TODO: b/26337871 Need to use proper API to check phone registered.
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ # Make call.
+ log.info("--> Call test: %s slot %s to %s slot %s <--", phone_a.serial,
+ phone_a_slot, phone_b.serial, phone_b_slot)
+
+ mo_default_voice_subid = get_subid_from_slot_index(log,ads[0],
+ phone_a_slot)
+ if mo_default_voice_subid == INVALID_SUB_ID:
+ log.warning("Sub ID of MO (%s) slot %s is invalid.", phone_a.serial,
+ phone_a_slot)
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+ set_subid_for_outgoing_call(
+ ads[0], mo_default_voice_subid)
+
+ mt_default_voice_subid = get_subid_from_slot_index(log,ads[1],
+ phone_b_slot)
+ if mt_default_voice_subid == INVALID_SUB_ID:
+ log.warning("Sub ID of MT (%s) slot %s is invalid.", phone_b.serial,
+ phone_b_slot)
+ return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
+
+ tel_result = call_setup_teardown(
+ log,
+ *param,
+ slot_id_callee=phone_b_slot,
+ wait_time_in_call=wait_time_in_call)
+
+ while not tel_result:
+ if retry <= 0:
+ log.error("Call Iteration failed.")
+ break
+ else:
+ log.info("RERUN call_setup_teardown.")
+ tel_result = call_setup_teardown(
+ log,
+ *param,
+ slot_id_callee=phone_b_slot,
+ wait_time_in_call=wait_time_in_call)
+
+ retry = retry - 1
+
+ return tel_result
+
+def three_phone_call_forwarding_short_seq(log,
+ phone_a,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_c,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ call_forwarding_type="unconditional",
+ retry=2):
+ """Short sequence of call process with call forwarding.
+ Test steps:
+ 1. Ensure all phones are initially in idle state.
+ 2. Enable call forwarding on Phone A.
+ 3. Make a call from Phone B to Phone A, The call should be forwarded to
+ PhoneC. Accept the call on Phone C.
+ 4. Ensure the call is connected and in correct phone state.
+ 5. Hang up the call on Phone B.
+ 6. Ensure all phones are in idle state.
+ 7. Disable call forwarding on Phone A.
+ 7. Make a call from Phone B to Phone A, The call should NOT be forwarded
+ to PhoneC. Accept the call on Phone A.
+ 8. Ensure the call is connected and in correct phone state.
+ 9. Hang up the call on Phone B.
+
+ Args:
+ phone_a: android object of Phone A
+ phone_a_idle_func: function to check idle state on Phone A
+ phone_a_in_call_check_func: function to check in-call state on Phone A
+ phone_b: android object of Phone B
+ phone_c: android object of Phone C
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+ call_forwarding_type:
+ - "unconditional"
+ - "busy"
+ - "not_answered"
+ - "not_reachable"
+ retry: times of retry
+
+ Returns:
+ True: if call sequence succeed.
+ False: for errors
+ """
+ ads = [phone_a, phone_b, phone_c]
+
+ call_params = [
+ (ads[1], ads[0], ads[2], ads[1], phone_a_in_call_check_func, False)
+ ]
+
+ if call_forwarding_type != "unconditional":
+ call_params.append((
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[1],
+ phone_a_in_call_check_func,
+ True))
+
+ for param in call_params:
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return False
+
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ log.info(
+ "---> Call forwarding %s (caller: %s, callee: %s, callee forwarded:"
+ " %s) <---",
+ call_forwarding_type,
+ param[0].serial,
+ param[1].serial,
+ param[2].serial)
+ while not call_setup_teardown_for_call_forwarding(
+ log,
+ *param,
+ wait_time_in_call=wait_time_in_call,
+ call_forwarding_type=call_forwarding_type) and retry >= 0:
+
+ if retry <= 0:
+ log.error("Call forwarding %s failed." % call_forwarding_type)
+ return False
+ else:
+ log.info(
+ "RERUN the test case: 'Call forwarding %s'" %
+ call_forwarding_type)
+
+ retry = retry - 1
+
+ return True
+
+def three_phone_call_waiting_short_seq(log,
+ phone_a,
+ phone_a_idle_func,
+ phone_a_in_call_check_func,
+ phone_b,
+ phone_c,
+ wait_time_in_call=WAIT_TIME_IN_CALL,
+ call_waiting=True,
+ scenario=None,
+ retry=2):
+ """Short sequence of call process with call waiting.
+ Test steps:
+ 1. Ensure all phones are initially in idle state.
+ 2. Enable call waiting on Phone A.
+ 3. Make the 1st call from Phone B to Phone A. Accept the call on Phone B.
+ 4. Ensure the call is connected and in correct phone state.
+ 5. Make the 2nd call from Phone C to Phone A. The call should be able to
+ income correctly. Whether or not the 2nd call should be answered by
+ Phone A depends on the scenario listed in the next step.
+ 6. Following 8 scenarios will be tested:
+ - 1st call ended first by Phone B during 2nd call incoming. 2nd call
+ ended by Phone C
+ - 1st call ended first by Phone B during 2nd call incoming. 2nd call
+ ended by Phone A
+ - 1st call ended first by Phone A during 2nd call incoming. 2nd call
+ ended by Phone C
+ - 1st call ended first by Phone A during 2nd call incoming. 2nd call
+ ended by Phone A
+ - 1st call ended by Phone B. 2nd call ended by Phone C
+ - 1st call ended by Phone B. 2nd call ended by Phone A
+ - 1st call ended by Phone A. 2nd call ended by Phone C
+ - 1st call ended by Phone A. 2nd call ended by Phone A
+ 7. Ensure all phones are in idle state.
+
+ Args:
+ phone_a: android object of Phone A
+ phone_a_idle_func: function to check idle state on Phone A
+ phone_a_in_call_check_func: function to check in-call state on Phone A
+ phone_b: android object of Phone B
+ phone_c: android object of Phone C
+ wait_time_in_call: time to wait in call.
+ This is optional, default is WAIT_TIME_IN_CALL
+ call_waiting: True for call waiting enabled and False for disabled
+ scenario: 1-8 for scenarios listed above
+ retry: times of retry
+
+ Returns:
+ True: if call sequence succeed.
+ False: for errors
+ """
+ ads = [phone_a, phone_b, phone_c]
+
+ sub_test_cases = [
+ {
+ "description": "1st call ended first by caller1 during 2nd call"
+ " incoming. 2nd call ended by caller2",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[1],
+ ads[2],
+ phone_a_in_call_check_func,
+ True)},
+ {
+ "description": "1st call ended first by caller1 during 2nd call"
+ " incoming. 2nd call ended by callee",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[1],
+ ads[0],
+ phone_a_in_call_check_func,
+ True)},
+ {
+ "description": "1st call ended first by callee during 2nd call"
+ " incoming. 2nd call ended by caller2",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[0],
+ ads[2],
+ phone_a_in_call_check_func,
+ True)},
+ {
+ "description": "1st call ended first by callee during 2nd call"
+ " incoming. 2nd call ended by callee",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[0],
+ ads[0],
+ phone_a_in_call_check_func,
+ True)},
+ {
+ "description": "1st call ended by caller1. 2nd call ended by"
+ " caller2",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[1],
+ ads[2],
+ phone_a_in_call_check_func,
+ False)},
+ {
+ "description": "1st call ended by caller1. 2nd call ended by callee",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[1],
+ ads[0],
+ phone_a_in_call_check_func,
+ False)},
+ {
+ "description": "1st call ended by callee. 2nd call ended by caller2",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[0],
+ ads[2],
+ phone_a_in_call_check_func,
+ False)},
+ {
+ "description": "1st call ended by callee. 2nd call ended by callee",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[0],
+ ads[0],
+ phone_a_in_call_check_func,
+ False)}
+ ]
+
+ if call_waiting:
+ if not scenario:
+ test_cases = sub_test_cases
+ else:
+ test_cases = [sub_test_cases[scenario-1]]
+ else:
+ test_cases = [
+ {
+ "description": "Call waiting deactivated",
+ "params": (
+ ads[1],
+ ads[0],
+ ads[2],
+ ads[0],
+ ads[0],
+ phone_a_in_call_check_func,
+ False)}
+ ]
+
+ results = []
+
+ for test_case in test_cases:
+ ensure_phones_idle(log, ads)
+ if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+ phone_a.log.error("Phone A Failed to Reselect")
+ return False
+
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ log.info(
+ "---> %s (caller1: %s, caller2: %s, callee: %s) <---",
+ test_case["description"],
+ test_case["params"][1].serial,
+ test_case["params"][2].serial,
+ test_case["params"][0].serial)
+
+ while not call_setup_teardown_for_call_waiting(
+ log,
+ *test_case["params"],
+ wait_time_in_call=wait_time_in_call,
+ call_waiting=call_waiting) and retry >= 0:
+
+ if retry <= 0:
+ log.error("Call waiting sub-case: '%s' failed." % test_case[
+ "description"])
+ results.append(False)
+ else:
+ log.info("RERUN the sub-case: '%s'" % test_case["description"])
+
+ retry = retry - 1
+
+ for result in results:
+ if not result:
+ return False
+
+ return True
+
+def phone_setup_iwlan(log,
+ ad,
+ is_airplane_mode,
+ wfc_mode,
+ wifi_ssid=None,
+ wifi_pwd=None):
+ """Phone setup function for epdg call test.
+ Set WFC mode according to wfc_mode.
+ Set airplane mode according to is_airplane_mode.
+ Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+ Wait for phone to be in iwlan data network type.
+ Wait for phone to report wfc enabled flag to be true.
+ Args:
+ log: Log object.
+ ad: Android device object.
+ is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+ wfc_mode: WFC mode to set to.
+ wifi_ssid: WiFi network SSID. This is optional.
+ If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+ wifi_pwd: WiFi network password. This is optional.
+ Returns:
+ True if success. False if fail.
+ """
+ return phone_setup_iwlan_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad),
+ is_airplane_mode, wfc_mode,
+ wifi_ssid, wifi_pwd)
+
+
+def phone_setup_iwlan_for_subscription(log,
+ ad,
+ sub_id,
+ is_airplane_mode,
+ wfc_mode,
+ wifi_ssid=None,
+ wifi_pwd=None):
+ """Phone setup function for epdg call test for subscription id.
+ Set WFC mode according to wfc_mode.
+ Set airplane mode according to is_airplane_mode.
+ Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+ Wait for phone to be in iwlan data network type.
+ Wait for phone to report wfc enabled flag to be true.
+ Args:
+ log: Log object.
+ ad: Android device object.
+ sub_id: subscription id.
+ is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+ wfc_mode: WFC mode to set to.
+ wifi_ssid: WiFi network SSID. This is optional.
+ If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+ wifi_pwd: WiFi network password. This is optional.
+ Returns:
+ True if success. False if fail.
+ """
+ if not get_capability_for_subscription(ad, CAPABILITY_WFC, sub_id):
+ ad.log.error("WFC is not supported, abort test.")
+ raise signals.TestSkip("WFC is not supported, abort test.")
+ toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False)
+ # check if WFC supported phones
+ if wfc_mode != WFC_MODE_DISABLED and not ad.droid.imsIsWfcEnabledByPlatform(
+ ):
+ ad.log.error("WFC is not enabled on this device by checking "
+ "ImsManager.isWfcEnabledByPlatform")
+ return False
+ if wifi_ssid is not None:
+ if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd, apm=is_airplane_mode):
+ ad.log.error("Fail to bring up WiFi connection on %s.", wifi_ssid)
+ return False
+ else:
+ ad.log.info("WiFi network SSID not specified, available user "
+ "parameters are: wifi_network_ssid, wifi_network_ssid_2g, "
+ "wifi_network_ssid_5g")
+ if not set_wfc_mode(log, ad, wfc_mode):
+ ad.log.error("Unable to set WFC mode to %s.", wfc_mode)
+ return False
+ if not wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
+ ad.log.error("WFC is not enabled")
+ return False
+ return True
+
+
+def phone_setup_iwlan_cellular_preferred(log,
+ ad,
+ wifi_ssid=None,
+ wifi_pwd=None):
+ """Phone setup function for iwlan Non-APM CELLULAR_PREFERRED test.
+ Set WFC mode according to CELLULAR_PREFERRED.
+ Set airplane mode according to False.
+ Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+ Make sure phone don't report iwlan data network type.
+ Make sure phone don't report wfc enabled flag to be true.
+
+ Args:
+ log: Log object.
+ ad: Android device object.
+ wifi_ssid: WiFi network SSID. This is optional.
+ If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+ wifi_pwd: WiFi network password. This is optional.
+
+ Returns:
+ True if success. False if fail.
+ """
+ toggle_airplane_mode(log, ad, False, strict_checking=False)
+ try:
+ toggle_volte(log, ad, True)
+ if not wait_for_network_generation(
+ log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+ if not ensure_network_generation(
+ log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+ ad.log.error("Fail to ensure data in 4G")
+ return False
+ except Exception as e:
+ ad.log.error(e)
+ ad.droid.telephonyToggleDataConnection(True)
+ if wifi_ssid is not None:
+ if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd):
+ ad.log.error("Connect to WiFi failed.")
+ return False
+ if not set_wfc_mode(log, ad, WFC_MODE_CELLULAR_PREFERRED):
+ ad.log.error("Set WFC mode failed.")
+ return False
+ if not wait_for_not_network_rat(
+ log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
+ ad.log.error("Data rat in iwlan mode.")
+ return False
+ elif not wait_for_wfc_disabled(log, ad, MAX_WAIT_TIME_WFC_ENABLED):
+ ad.log.error("Should report wifi calling disabled within %s.",
+ MAX_WAIT_TIME_WFC_ENABLED)
+ return False
+ return True
+
+
+def phone_setup_data_for_subscription(log, ad, sub_id, network_generation):
+ """Setup Phone <sub_id> Data to <network_generation>
+
+ Args:
+ log: log object
+ ad: android device object
+ sub_id: subscription id
+ network_generation: network generation, e.g. GEN_2G, GEN_3G, GEN_4G, GEN_5G
+
+ Returns:
+ True if success, False if fail.
+ """
+ toggle_airplane_mode(log, ad, False, strict_checking=False)
+ set_wifi_to_default(log, ad)
+ if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+ ad.log.error("Disable WFC failed.")
+ return False
+ if not ensure_network_generation_for_subscription(
+ log,
+ ad,
+ sub_id,
+ network_generation,
+ voice_or_data=NETWORK_SERVICE_DATA):
+ get_telephony_signal_strength(ad)
+ return False
+ return True
+
+
+def phone_setup_5g(log, ad):
+ """Setup Phone default data sub_id data to 5G.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_5g_for_subscription(log, ad,
+ get_default_data_sub_id(ad))
+
+
+def phone_setup_5g_for_subscription(log, ad, sub_id):
+ """Setup Phone <sub_id> Data to 5G.
+
+ Args:
+ log: log object
+ ad: android device object
+ sub_id: subscription id
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_data_for_subscription(log, ad, sub_id, GEN_5G)
+
+
+def phone_setup_4g(log, ad):
+ """Setup Phone default data sub_id data to 4G.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_4g_for_subscription(log, ad,
+ get_default_data_sub_id(ad))
+
+
+def phone_setup_4g_for_subscription(log, ad, sub_id):
+ """Setup Phone <sub_id> Data to 4G.
+
+ Args:
+ log: log object
+ ad: android device object
+ sub_id: subscription id
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_data_for_subscription(log, ad, sub_id, GEN_4G)
+
+
+def phone_setup_3g(log, ad):
+ """Setup Phone default data sub_id data to 3G.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_3g_for_subscription(log, ad,
+ get_default_data_sub_id(ad))
+
+
+def phone_setup_3g_for_subscription(log, ad, sub_id):
+ """Setup Phone <sub_id> Data to 3G.
+
+ Args:
+ log: log object
+ ad: android device object
+ sub_id: subscription id
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_data_for_subscription(log, ad, sub_id, GEN_3G)
+
+
+def phone_setup_2g(log, ad):
+ """Setup Phone default data sub_id data to 2G.
+
+ Args:
+ log: log object
+ ad: android device object
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_2g_for_subscription(log, ad,
+ get_default_data_sub_id(ad))
+
+
+def phone_setup_2g_for_subscription(log, ad, sub_id):
+ """Setup Phone <sub_id> Data to 3G.
+
+ Args:
+ log: log object
+ ad: android device object
+ sub_id: subscription id
+
+ Returns:
+ True if success, False if fail.
+ """
+ return phone_setup_data_for_subscription(log, ad, sub_id, GEN_2G)
+
+
+def phone_setup_csfb(log, ad):
+ """Setup phone for CSFB call test.
+
+ Setup Phone to be in 4G mode.
+ Disabled VoLTE.
+
+ Args:
+ log: log object
+ ad: Android device object.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ return phone_setup_csfb_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_csfb_for_subscription(log, ad, sub_id):
+ """Setup phone for CSFB call test for subscription id.
+
+ Setup Phone to be in 4G mode.
+ Disabled VoLTE.
+
+ Args:
+ log: log object
+ ad: Android device object.
+ sub_id: subscription id.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ capabilities = ad.telephony["subscription"][sub_id].get("capabilities", [])
+ if capabilities:
+ if "hide_enhanced_4g_lte" in capabilities:
+ show_enhanced_4g_lte_mode = getattr(ad, "show_enhanced_4g_lte_mode", False)
+ if show_enhanced_4g_lte_mode in ["false", "False", False]:
+ ad.log.warning("'VoLTE' option is hidden. Test will be skipped.")
+ raise signals.TestSkip("'VoLTE' option is hidden. Test will be skipped.")
+ if not phone_setup_4g_for_subscription(log, ad, sub_id):
+ ad.log.error("Failed to set to 4G data.")
+ return False
+
+ toggle_volte_for_subscription(log, ad, sub_id, False)
+
+ if not ensure_network_generation_for_subscription(
+ log, ad, sub_id, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+ return False
+
+ if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ return False
+
+ return phone_idle_csfb_for_subscription(log, ad, sub_id)
+
+
+def phone_setup_volte(log, ad):
+ """Setup VoLTE enable.
+
+ Args:
+ log: log object
+ ad: android device object.
+
+ Returns:
+ True: if VoLTE is enabled successfully.
+ False: for errors
+ """
+ if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
+ get_outgoing_voice_sub_id(ad)):
+ ad.log.error("VoLTE is not supported, abort test.")
+ raise signals.TestSkip("VoLTE is not supported, abort test.")
+ return phone_setup_volte_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_volte_for_subscription(log, ad, sub_id):
+ """Setup VoLTE enable for subscription id.
+ Args:
+ log: log object
+ ad: android device object.
+ sub_id: subscription id.
+ Returns:
+ True: if VoLTE is enabled successfully.
+ False: for errors
+ """
+ if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
+ get_outgoing_voice_sub_id(ad)):
+ ad.log.error("VoLTE is not supported, abort test.")
+ raise signals.TestSkip("VoLTE is not supported, abort test.")
+ if not phone_setup_4g_for_subscription(log, ad, sub_id):
+ ad.log.error("Failed to set to 4G data.")
+ return False
+ if not wait_for_enhanced_4g_lte_setting(log, ad, sub_id):
+ ad.log.error("Enhanced 4G LTE setting is not available")
+ return False
+ toggle_volte_for_subscription(log, ad, sub_id, True)
+ return phone_idle_volte_for_subscription(log, ad, sub_id)
+
+
+def phone_setup_voice_3g(log, ad):
+ """Setup phone voice to 3G.
+
+ Args:
+ log: log object
+ ad: Android device object.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ return phone_setup_voice_3g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_voice_3g_for_subscription(log, ad, sub_id):
+ """Setup phone voice to 3G for subscription id.
+
+ Args:
+ log: log object
+ ad: Android device object.
+ sub_id: subscription id.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ if not phone_setup_3g_for_subscription(log, ad, sub_id):
+ ad.log.error("Failed to set to 3G data.")
+ return False
+ if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ return False
+ return phone_idle_3g_for_subscription(log, ad, sub_id)
+
+
+def phone_setup_voice_2g(log, ad):
+ """Setup phone voice to 2G.
+
+ Args:
+ log: log object
+ ad: Android device object.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ return phone_setup_voice_2g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_voice_2g_for_subscription(log, ad, sub_id):
+ """Setup phone voice to 2G for subscription id.
+
+ Args:
+ log: log object
+ ad: Android device object.
+ sub_id: subscription id.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ if not phone_setup_2g_for_subscription(log, ad, sub_id):
+ ad.log.error("Failed to set to 2G data.")
+ return False
+ if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ return False
+ return phone_idle_2g_for_subscription(log, ad, sub_id)
+
+
+def phone_setup_voice_general(log, ad):
+ """Setup phone for voice general call test.
+
+ Make sure phone attached to voice.
+ Make necessary delay.
+
+ Args:
+ ad: Android device object.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ return phone_setup_voice_general_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_voice_general_for_slot(log,ad,slot_id):
+ return phone_setup_voice_general_for_subscription(
+ log, ad, get_subid_from_slot_index(log,ad,slot_id))
+
+
+def phone_setup_voice_general_for_subscription(log, ad, sub_id):
+ """Setup phone for voice general call test for subscription id.
+
+ Make sure phone attached to voice.
+ Make necessary delay.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ toggle_airplane_mode(log, ad, False, strict_checking=False)
+ if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ # if phone can not attach voice, try phone_setup_voice_3g
+ return phone_setup_voice_3g_for_subscription(log, ad, sub_id)
+ return True
+
+
+def phone_setup_data_general(log, ad):
+ """Setup phone for data general test.
+
+ Make sure phone attached to data.
+ Make necessary delay.
+
+ Args:
+ ad: Android device object.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ return phone_setup_data_general_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultDataSubId())
+
+
+def phone_setup_data_general_for_subscription(log, ad, sub_id):
+ """Setup phone for data general test for subscription id.
+
+ Make sure phone attached to data.
+ Make necessary delay.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+
+ Returns:
+ True if setup successfully.
+ False for errors.
+ """
+ toggle_airplane_mode(log, ad, False, strict_checking=False)
+ if not wait_for_data_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION):
+ # if phone can not attach data, try reset network preference settings
+ reset_preferred_network_type_to_allowable_range(log, ad)
+
+ return wait_for_data_attach_for_subscription(log, ad, sub_id,
+ MAX_WAIT_TIME_NW_SELECTION)
+
+
+def phone_setup_rat_for_subscription(log, ad, sub_id, network_preference,
+ rat_family):
+ toggle_airplane_mode(log, ad, False, strict_checking=False)
+ set_wifi_to_default(log, ad)
+ if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+ ad.log.error("Disable WFC failed.")
+ return False
+ return ensure_network_rat_for_subscription(log, ad, sub_id,
+ network_preference, rat_family)
+
+
+def phone_setup_lte_gsm_wcdma(log, ad):
+ return phone_setup_lte_gsm_wcdma_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_lte_gsm_wcdma_for_subscription(log, ad, sub_id):
+ return phone_setup_rat_for_subscription(
+ log, ad, sub_id, NETWORK_MODE_LTE_GSM_WCDMA, RAT_FAMILY_LTE)
+
+
+def phone_setup_gsm_umts(log, ad):
+ return phone_setup_gsm_umts_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_gsm_umts_for_subscription(log, ad, sub_id):
+ return phone_setup_rat_for_subscription(
+ log, ad, sub_id, NETWORK_MODE_GSM_UMTS, RAT_FAMILY_WCDMA)
+
+
+def phone_setup_gsm_only(log, ad):
+ return phone_setup_gsm_only_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_gsm_only_for_subscription(log, ad, sub_id):
+ return phone_setup_rat_for_subscription(
+ log, ad, sub_id, NETWORK_MODE_GSM_ONLY, RAT_FAMILY_GSM)
+
+
+def phone_setup_lte_cdma_evdo(log, ad):
+ return phone_setup_lte_cdma_evdo_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_lte_cdma_evdo_for_subscription(log, ad, sub_id):
+ return phone_setup_rat_for_subscription(
+ log, ad, sub_id, NETWORK_MODE_LTE_CDMA_EVDO, RAT_FAMILY_LTE)
+
+
+def phone_setup_cdma(log, ad):
+ return phone_setup_cdma_for_subscription(
+ log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_cdma_for_subscription(log, ad, sub_id):
+ return phone_setup_rat_for_subscription(log, ad, sub_id, NETWORK_MODE_CDMA,
+ RAT_FAMILY_CDMA2000)
+
+
+def phone_idle_volte(log, ad):
+ """Return if phone is idle for VoLTE call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_volte_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_volte_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for VoLTE call test for subscription id.
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not wait_for_network_rat_for_subscription(
+ log, ad, sub_id, RAT_FAMILY_LTE,
+ voice_or_data=NETWORK_SERVICE_VOICE):
+ ad.log.error("Voice rat not in LTE mode.")
+ return False
+ if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED, sub_id):
+ ad.log.error(
+ "Failed to <report volte enabled true> within %s seconds.",
+ MAX_WAIT_TIME_VOLTE_ENABLED)
+ return False
+ return True
+
+
+def phone_idle_iwlan(log, ad):
+ """Return if phone is idle for WiFi calling call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_iwlan_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_iwlan_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for WiFi calling call test for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not wait_for_wfc_enabled(log, ad, MAX_WAIT_TIME_WFC_ENABLED):
+ ad.log.error("Failed to <report wfc enabled true> within %s seconds.",
+ MAX_WAIT_TIME_WFC_ENABLED)
+ return False
+ return True
+
+
+def phone_idle_not_iwlan(log, ad):
+ """Return if phone is idle for non WiFi calling call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_not_iwlan_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_not_iwlan_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for non WiFi calling call test for sub id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not wait_for_not_network_rat_for_subscription(
+ log, ad, sub_id, RAT_FAMILY_WLAN,
+ voice_or_data=NETWORK_SERVICE_DATA):
+ log.error("{} data rat in iwlan mode.".format(ad.serial))
+ return False
+ return True
+
+
+def phone_idle_csfb(log, ad):
+ """Return if phone is idle for CSFB call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_csfb_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_csfb_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for CSFB call test for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not wait_for_network_rat_for_subscription(
+ log, ad, sub_id, RAT_FAMILY_LTE,
+ voice_or_data=NETWORK_SERVICE_DATA):
+ ad.log.error("Data rat not in lte mode.")
+ return False
+ return True
+
+
+def phone_idle_3g(log, ad):
+ """Return if phone is idle for 3G call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_3g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_3g_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for 3G call test for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ return wait_for_network_generation_for_subscription(
+ log, ad, sub_id, GEN_3G, voice_or_data=NETWORK_SERVICE_VOICE)
+
+
+def phone_idle_2g(log, ad):
+ """Return if phone is idle for 2G call test.
+
+ Args:
+ ad: Android device object.
+ """
+ return phone_idle_2g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_2g_for_subscription(log, ad, sub_id):
+ """Return if phone is idle for 2G call test for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ return wait_for_network_generation_for_subscription(
+ log, ad, sub_id, GEN_2G, voice_or_data=NETWORK_SERVICE_VOICE)
+
+
+def get_current_voice_rat(log, ad):
+ """Return current Voice RAT
+
+ Args:
+ ad: Android device object.
+ """
+ return get_current_voice_rat_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def get_current_voice_rat_for_subscription(log, ad, sub_id):
+ """Return current Voice RAT for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ return get_network_rat_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+
+
+def is_phone_in_call_volte(log, ad):
+ """Return if phone is in VoLTE call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_volte_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_volte_for_subscription(log, ad, sub_id):
+ """Return if phone is in VoLTE call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_type != RAT_LTE:
+ ad.log.error("Voice rat on: %s. Expected: LTE", nw_type)
+ return False
+ return True
+
+
+def is_phone_in_call_csfb(log, ad):
+ """Return if phone is in CSFB call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_csfb_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_csfb_for_subscription(log, ad, sub_id):
+ """Return if phone is in CSFB call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_type == RAT_LTE:
+ ad.log.error("Voice rat on: %s. Expected: not LTE", nw_type)
+ return False
+ return True
+
+
+def is_phone_in_call_3g(log, ad):
+ """Return if phone is in 3G call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_3g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_3g_for_subscription(log, ad, sub_id):
+ """Return if phone is in 3G call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_gen != GEN_3G:
+ ad.log.error("Voice rat on: %s. Expected: 3g", nw_gen)
+ return False
+ return True
+
+
+def is_phone_in_call_2g(log, ad):
+ """Return if phone is in 2G call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_2g_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_2g_for_subscription(log, ad, sub_id):
+ """Return if phone is in 2G call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_gen != GEN_2G:
+ ad.log.error("Voice rat on: %s. Expected: 2g", nw_gen)
+ return False
+ return True
+
+
+def is_phone_in_call_1x(log, ad):
+ """Return if phone is in 1x call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_1x_for_subscription(log, ad,
+ get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_1x_for_subscription(log, ad, sub_id):
+ """Return if phone is in 1x call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_type != RAT_1XRTT:
+ ad.log.error("Voice rat on: %s. Expected: 1xrtt", nw_type)
+ return False
+ return True
+
+
+def is_phone_in_call_wcdma(log, ad):
+ """Return if phone is in WCDMA call.
+
+ Args:
+ ad: Android device object.
+ """
+ return is_phone_in_call_wcdma_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_wcdma_for_subscription(log, ad, sub_id):
+ """Return if phone is in WCDMA call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ # Currently checking 'umts'.
+ # Changes may needed in the future.
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+ NETWORK_SERVICE_VOICE)
+ if nw_type != RAT_UMTS:
+ ad.log.error("%s voice rat on: %s. Expected: umts", nw_type)
+ return False
+ return True
+
+
+def is_phone_in_call_iwlan(log, ad, call_id=None):
+ """Return if phone is in WiFi call.
+
+ Args:
+ ad: Android device object.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ if not ad.droid.telephonyIsImsRegistered():
+ ad.log.info("IMS is not registered.")
+ return False
+ if not ad.droid.telephonyIsWifiCallingAvailable():
+ ad.log.info("IsWifiCallingAvailable is False")
+ return False
+ if not call_id:
+ call_ids = ad.droid.telecomCallGetCallIds()
+ if call_ids:
+ call_id = call_ids[-1]
+ if not call_id:
+ ad.log.error("Failed to get call id")
+ return False
+ else:
+ call_prop = ad.droid.telecomCallGetProperties(call_id)
+ if "WIFI" not in call_prop:
+ ad.log.info("callProperties = %s, expecting WIFI", call_prop)
+ return False
+ nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
+ if nw_type != RAT_IWLAN:
+ ad.log.warning("Data rat on: %s. Expected: iwlan", nw_type)
+ return True
+
+
+def is_phone_in_call_not_iwlan(log, ad):
+ """Return if phone is in WiFi call for subscription id.
+
+ Args:
+ ad: Android device object.
+ sub_id: subscription id.
+ """
+ if not ad.droid.telecomIsInCall():
+ ad.log.error("Not in call.")
+ return False
+ nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
+ if nw_type == RAT_IWLAN:
+ ad.log.error("Data rat on: %s. Expected: not iwlan", nw_type)
+ return False
+ if is_wfc_enabled(log, ad):
+ ad.log.error("WiFi Calling feature bit is True.")
+ return False
+ return True
+
+
+def swap_calls(log,
+ ads,
+ call_hold_id,
+ call_active_id,
+ num_swaps=1,
+ check_call_status=True):
+ """PhoneA in call with B and C. Swap active/holding call on PhoneA.
+
+ Swap call and check status on PhoneA.
+ (This step may have multiple times according to 'num_swaps'.)
+ Check if all 3 phones are 'in-call'.
+
+ Args:
+ ads: list of ad object, at least three need to pass in.
+ Swap operation will happen on ads[0].
+ ads[1] and ads[2] are call participants.
+ call_hold_id: id for the holding call in ads[0].
+ call_hold_id should be 'STATE_HOLDING' when calling this function.
+ call_active_id: id for the active call in ads[0].
+ call_active_id should be 'STATE_ACTIVE' when calling this function.
+ num_swaps: how many swap/check operations will be done before return.
+ check_call_status: This is optional. Default value is True.
+ If this value is True, then call status (active/hold) will be
+ be checked after each swap operation.
+
+ Returns:
+ If no error happened, return True, otherwise, return False.
+ """
+ if check_call_status:
+ # Check status before swap.
+ if ads[0].droid.telecomCallGetCallState(
+ call_active_id) != CALL_STATE_ACTIVE:
+ ads[0].log.error(
+ "Call_id:%s, state:%s, expected: STATE_ACTIVE", call_active_id,
+ ads[0].droid.telecomCallGetCallState(call_active_id))
+ return False
+ if ads[0].droid.telecomCallGetCallState(
+ call_hold_id) != CALL_STATE_HOLDING:
+ ads[0].log.error(
+ "Call_id:%s, state:%s, expected: STATE_HOLDING", call_hold_id,
+ ads[0].droid.telecomCallGetCallState(call_hold_id))
+ return False
+
+ i = 1
+ while (i <= num_swaps):
+ ads[0].log.info("swap_test %s: swap and check call status.", i)
+ ads[0].droid.telecomCallHold(call_active_id)
+ time.sleep(WAIT_TIME_IN_CALL)
+ # Swap object reference
+ call_active_id, call_hold_id = call_hold_id, call_active_id
+ if check_call_status:
+ # Check status
+ if ads[0].droid.telecomCallGetCallState(
+ call_active_id) != CALL_STATE_ACTIVE:
+ ads[0].log.error(
+ "Call_id:%s, state:%s, expected: STATE_ACTIVE",
+ call_active_id,
+ ads[0].droid.telecomCallGetCallState(call_active_id))
+ return False
+ if ads[0].droid.telecomCallGetCallState(
+ call_hold_id) != CALL_STATE_HOLDING:
+ ads[0].log.error(
+ "Call_id:%s, state:%s, expected: STATE_HOLDING",
+ call_hold_id,
+ ads[0].droid.telecomCallGetCallState(call_hold_id))
+ return False
+ # TODO: b/26296375 add voice check.
+
+ i += 1
+
+ #In the end, check all three phones are 'in-call'.
+ if not verify_incall_state(log, [ads[0], ads[1], ads[2]], True):
+ return False
+
+ return True
+
+
+def get_audio_route(log, ad):
+ """Gets the audio route for the active call
+
+ Args:
+ log: logger object
+ ad: android_device object
+
+ Returns:
+ Audio route string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
+ "WIRED_OR_EARPIECE"]
+ """
+
+ audio_state = ad.droid.telecomCallGetAudioState()
+ return audio_state["AudioRoute"]
+
+
+def set_audio_route(log, ad, route):
+ """Sets the audio route for the active call
+
+ Args:
+ log: logger object
+ ad: android_device object
+ route: string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
+ "WIRED_OR_EARPIECE"]
+
+ Returns:
+ If no error happened, return True, otherwise, return False.
+ """
+ ad.droid.telecomCallSetAudioRoute(route)
+ return True
+
+
+def is_property_in_call_properties(log, ad, call_id, expected_property):
+ """Return if the call_id has the expected property
+
+ Args:
+ log: logger object
+ ad: android_device object
+ call_id: call id.
+ expected_property: expected property.
+
+ Returns:
+ True if call_id has expected_property. False if not.
+ """
+ properties = ad.droid.telecomCallGetProperties(call_id)
+ return (expected_property in properties)
+
+
+def is_call_hd(log, ad, call_id):
+ """Return if the call_id is HD call.
+
+ Args:
+ log: logger object
+ ad: android_device object
+ call_id: call id.
+
+ Returns:
+ True if call_id is HD call. False if not.
+ """
+ return is_property_in_call_properties(log, ad, call_id,
+ CALL_PROPERTY_HIGH_DEF_AUDIO)
+
+
+def get_cep_conference_call_id(ad):
+ """Get CEP conference call id if there is an ongoing CEP conference call.
+
+ Args:
+ ad: android device object.
+
+ Returns:
+ call id for CEP conference call if there is an ongoing CEP conference call.
+ None otherwise.
+ """
+ for call in ad.droid.telecomCallGetCallIds():
+ if len(ad.droid.telecomCallGetCallChildren(call)) != 0:
+ return call
+ return None
+
+def phone_setup_on_rat(
+ log,
+ ad,
+ rat='volte',
+ sub_id=None,
+ is_airplane_mode=False,
+ wfc_mode=None,
+ wifi_ssid=None,
+ wifi_pwd=None,
+ only_return_fn=None,
+ sub_id_type='voice'):
+
+ if sub_id is None:
+ if sub_id_type == 'sms':
+ sub_id = get_outgoing_message_sub_id(ad)
+ else:
+ sub_id = get_outgoing_voice_sub_id(ad)
+
+ if rat.lower() == 'volte':
+ if only_return_fn:
+ return phone_setup_volte_for_subscription
+ else:
+ return phone_setup_volte_for_subscription(log, ad, sub_id)
+
+ elif rat.lower() == 'csfb':
+ if only_return_fn:
+ return phone_setup_csfb_for_subscription
+ else:
+ return phone_setup_csfb_for_subscription(log, ad, sub_id)
+
+ elif rat.lower() == '3g':
+ if only_return_fn:
+ return phone_setup_voice_3g_for_subscription
+ else:
+ return phone_setup_voice_3g_for_subscription(log, ad, sub_id)
+
+ elif rat.lower() == 'wfc':
+ if only_return_fn:
+ return phone_setup_iwlan_for_subscription
+ else:
+ return phone_setup_iwlan_for_subscription(
+ log,
+ ad,
+ sub_id,
+ is_airplane_mode,
+ wfc_mode,
+ wifi_ssid,
+ wifi_pwd)
+ else:
+ if only_return_fn:
+ return phone_setup_voice_general_for_subscription
+ else:
+ return phone_setup_voice_general_for_subscription(log, ad, sub_id)
+
+def is_phone_in_call_on_rat(log, ad, rat='volte', only_return_fn=None):
+ if rat.lower() == 'volte':
+ if only_return_fn:
+ return is_phone_in_call_volte
+ else:
+ return is_phone_in_call_volte(log, ad)
+
+ elif rat.lower() == 'csfb':
+ if only_return_fn:
+ return is_phone_in_call_csfb
+ else:
+ return is_phone_in_call_csfb(log, ad)
+
+ elif rat.lower() == '3g':
+ if only_return_fn:
+ return is_phone_in_call_3g
+ else:
+ return is_phone_in_call_3g(log, ad)
+
+ elif rat.lower() == 'wfc':
+ if only_return_fn:
+ return is_phone_in_call_iwlan
+ else:
+ return is_phone_in_call_iwlan(log, ad)
+ else:
+ return None
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/twilio_client.py b/acts_tests/acts_contrib/test_utils/tel/twilio_client.py
new file mode 100644
index 0000000..fe13287
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/twilio_client.py
@@ -0,0 +1,75 @@
+#! /usr/bin/env python3
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""The twilio client that initiates the call."""
+
+# TODO(danielvernon):Generalize client to use any service including phone.
+
+from twilio.rest import Client
+import yaml
+
+ACCOUNT_SID_KEY = 'account_sid'
+AUTH_TOKEN_KEY = 'auth_token'
+PHONE_NUMBER_KEY = 'phone_number'
+MUSIC_URL = 'http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient'
+URLS_KEY = 'urls'
+
+class TwilioClient:
+ """A class that wraps the Twilio Client class and can make calls.
+
+ Attributes:
+ __account_sid: The account id
+ __auth_token: The authentication token
+ __phone_number: The phoone number
+ urls: urls that will be played during call
+ """
+
+ def __init__(self, cfg_path):
+ self.__account_sid = None
+ self.__auth_token = None
+ self.__phone_number = None
+ self.urls = None
+
+ self.load_config(cfg_path)
+ self.client = Client(self.__account_sid, self.__auth_token)
+ self.call_handle = self.client.api.account.calls
+
+ def load_config(self, cfg_path):
+ """Loads the config for twilio.
+
+ Args:
+ cfg_path: A string, which is the path to the config file.
+ """
+ with open(cfg_path) as cfg_file:
+ cfg = yaml.load(cfg_file)
+ self.__account_sid = cfg[ACCOUNT_SID_KEY]
+ self.__auth_token = cfg[AUTH_TOKEN_KEY]
+ self.__phone_number = cfg[PHONE_NUMBER_KEY]
+ self.urls = cfg[URLS_KEY]
+
+ def call(self, to):
+ """Makes request to Twilio API to call number and play music.
+
+ Must be registered with Twilio account in order to use this client.
+ Arguments:
+ to: the number to call (str). example -- '+12345678910'
+
+ Returns:
+ call.sid: the sid of the call request.
+ """
+
+ call = self.call_handle.create(to=to, from_=self.__phone_number,
+ url=MUSIC_URL, status_callback=MUSIC_URL)
+ return call.sid
diff --git a/acts_tests/acts_contrib/test_utils/users/__init__.py b/acts_tests/acts_contrib/test_utils/users/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/users/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/users/users.py b/acts_tests/acts_contrib/test_utils/users/users.py
new file mode 100644
index 0000000..72f1892
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/users/users.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Defines utilities that can be used to create android user account
+
+import re
+import time
+import logging as log
+
+
+
+def get_all_users(android_device):
+ all_users = {}
+ out = android_device.adb.shell("pm list users")
+
+ for user in re.findall("UserInfo{(.*\d*\w):", out):
+ all = user.split(":")
+ all_users[all[1]] = all_users.get(all[1], all[0])
+ return all_users
+
+
+def create_new_user(android_device, user_name):
+ out = android_device.adb.shell("pm create-user {}".format(user_name))
+ return re.search("Success(.* (.*\d))", out).group(2)
+
+
+def switch_user(android_device, user_id):
+ prev_user = get_current_user(android_device)
+ android_device.adb.shell("am switch-user {}".format(user_id))
+ if not _wait_for_user_to_take_place(android_device, prev_user):
+ log.error("Failed to successfully switch user {}".format(user_id))
+ return False
+ return True
+
+
+def remove_user(android_device, user_id):
+ return "Success" in android_device.adb.shell("pm remove-user {}".format(user_id))
+
+
+def get_current_user(android_device):
+ out = android_device.adb.shell("dumpsys activity")
+ result = re.search("mCurrentUserId:(\d+)", out)
+ return result.group(1)
+
+
+def _wait_for_user_to_take_place(android_device, user_id, timeout=10):
+ start_time = time.time()
+ while (start_time + timeout) > time.time():
+ time.sleep(1)
+ if user_id != get_current_user(android_device):
+ return True
+ return False
diff --git a/acts_tests/acts_contrib/test_utils/wifi/OWNERS b/acts_tests/acts_contrib/test_utils/wifi/OWNERS
new file mode 100644
index 0000000..edb3e3e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/OWNERS
@@ -0,0 +1,6 @@
+bkleung@google.com
+dysu@google.com
+etancohen@google.com
+gmoturu@google.com
+rpius@google.com
+satk@google.com
diff --git a/acts_tests/acts_contrib/test_utils/wifi/RttPostFlightTest.py b/acts_tests/acts_contrib/test_utils/wifi/RttPostFlightTest.py
new file mode 100644
index 0000000..b058a07
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/RttPostFlightTest.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import acts_contrib.test_utils.wifi.rpm_controller_utils as rutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts import asserts
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+SSID = "DO_NOT_CONNECT"
+TIMEOUT = 60
+WAIT_TIME = 10
+
+class RttPostFlightTest(WifiBaseTest):
+ """Turns off 802.11mc AP after RTT tests."""
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ required_params = ["rpm_ip", "rpm_port"]
+ self.unpack_userparams(req_param_names=required_params)
+ self.rpm_telnet = rutils.create_telnet_session(self.rpm_ip)
+
+ ### Tests ###
+
+ def test_turn_off_80211mc_ap(self):
+ self.rpm_telnet.turn_off(self.rpm_port)
+ curr_time = time.time()
+ while time.time() < curr_time + TIMEOUT:
+ time.sleep(WAIT_TIME)
+ if not wutils.start_wifi_connection_scan_and_check_for_network(
+ self.dut, SSID):
+ return True
+ self.log.error("Failed to turn off AP")
+ return False
diff --git a/acts_tests/acts_contrib/test_utils/wifi/RttPreFlightTest.py b/acts_tests/acts_contrib/test_utils/wifi/RttPreFlightTest.py
new file mode 100644
index 0000000..db87976
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/RttPreFlightTest.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import acts_contrib.test_utils.wifi.rpm_controller_utils as rutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts import asserts
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+SSID = "DO_NOT_CONNECT"
+TIMEOUT = 60
+WAIT_TIME = 10
+
+class RttPreFlightTest(WifiBaseTest):
+ """Turns on/off 802.11mc AP before and after RTT tests."""
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ required_params = ["rpm_ip", "rpm_port"]
+ self.unpack_userparams(req_param_names=required_params)
+ self.rpm_telnet = rutils.create_telnet_session(self.rpm_ip)
+
+ ### Tests ###
+
+ def test_turn_on_80211mc_ap(self):
+ self.rpm_telnet.turn_on(self.rpm_port)
+ curr_time = time.time()
+ while time.time() < curr_time + TIMEOUT:
+ time.sleep(WAIT_TIME)
+ if wutils.start_wifi_connection_scan_and_check_for_network(
+ self.dut, SSID):
+ return True
+ self.log.error("Failed to turn on AP")
+ return False
diff --git a/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py
new file mode 100644
index 0000000..e191599
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py
@@ -0,0 +1,801 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+ Base Class for Defining Common WiFi Test Functionality
+"""
+
+import copy
+import itertools
+import time
+
+import acts.controllers.access_point as ap
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts.signals import TestSignal
+from acts.controllers import android_device
+from acts.controllers.access_point import AccessPoint
+from acts.controllers.ap_lib import hostapd_ap_preset
+from acts.controllers.ap_lib import hostapd_bss_settings
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib import hostapd_security
+
+AP_1 = 0
+AP_2 = 1
+MAX_AP_COUNT = 2
+
+
+class WifiBaseTest(BaseTestClass):
+ def setup_class(self):
+ if hasattr(self, 'attenuators') and self.attenuators:
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0)
+
+ def get_psk_network(
+ self,
+ mirror_ap,
+ reference_networks,
+ hidden=False,
+ same_ssid=False,
+ security_mode=hostapd_constants.WPA2_STRING,
+ ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+ ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+ passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
+ passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G):
+ """Generates SSID and passphrase for a WPA2 network using random
+ generator.
+
+ Args:
+ mirror_ap: Boolean, determines if both APs use the same hostapd
+ config or different configs.
+ reference_networks: List of PSK networks.
+ same_ssid: Boolean, determines if both bands on AP use the same
+ SSID.
+ ssid_length_2gecond AP Int, number of characters to use for 2G SSID.
+ ssid_length_5g: Int, number of characters to use for 5G SSID.
+ passphrase_length_2g: Int, length of password for 2G network.
+ passphrase_length_5g: Int, length of password for 5G network.
+
+ Returns: A dict of 2G and 5G network lists for hostapd configuration.
+
+ """
+ network_dict_2g = {}
+ network_dict_5g = {}
+ ref_5g_security = security_mode
+ ref_2g_security = security_mode
+
+ if same_ssid:
+ ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
+ ref_5g_ssid = ref_2g_ssid
+
+ ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g)
+ ref_5g_passphrase = ref_2g_passphrase
+
+ else:
+ ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
+ ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g)
+
+ ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
+ ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g)
+
+ network_dict_2g = {
+ "SSID": ref_2g_ssid,
+ "security": ref_2g_security,
+ "password": ref_2g_passphrase,
+ "hiddenSSID": hidden
+ }
+
+ network_dict_5g = {
+ "SSID": ref_5g_ssid,
+ "security": ref_5g_security,
+ "password": ref_5g_passphrase,
+ "hiddenSSID": hidden
+ }
+
+ ap = 0
+ for ap in range(MAX_AP_COUNT):
+ reference_networks.append({
+ "2g": copy.copy(network_dict_2g),
+ "5g": copy.copy(network_dict_5g)
+ })
+ if not mirror_ap:
+ break
+ return {"2g": network_dict_2g, "5g": network_dict_5g}
+
+ def get_open_network(self,
+ mirror_ap,
+ open_network,
+ hidden=False,
+ same_ssid=False,
+ ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+ ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+ security_mode='none'):
+ """Generates SSIDs for a open network using a random generator.
+
+ Args:
+ mirror_ap: Boolean, determines if both APs use the same hostapd
+ config or different configs.
+ open_network: List of open networks.
+ same_ssid: Boolean, determines if both bands on AP use the same
+ SSID.
+ ssid_length_2g: Int, number of characters to use for 2G SSID.
+ ssid_length_5g: Int, number of characters to use for 5G SSID.
+ security_mode: 'none' for open and 'OWE' for WPA3 OWE.
+
+ Returns: A dict of 2G and 5G network lists for hostapd configuration.
+
+ """
+ network_dict_2g = {}
+ network_dict_5g = {}
+
+ if same_ssid:
+ open_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
+ open_5g_ssid = open_2g_ssid
+
+ else:
+ open_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
+ open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
+
+ network_dict_2g = {
+ "SSID": open_2g_ssid,
+ "security": security_mode,
+ "hiddenSSID": hidden
+ }
+
+ network_dict_5g = {
+ "SSID": open_5g_ssid,
+ "security": security_mode,
+ "hiddenSSID": hidden
+ }
+
+ ap = 0
+ for ap in range(MAX_AP_COUNT):
+ open_network.append({
+ "2g": copy.copy(network_dict_2g),
+ "5g": copy.copy(network_dict_5g)
+ })
+ if not mirror_ap:
+ break
+ return {"2g": network_dict_2g, "5g": network_dict_5g}
+
+ def get_wep_network(
+ self,
+ mirror_ap,
+ networks,
+ hidden=False,
+ same_ssid=False,
+ ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+ ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+ passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
+ passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G):
+ """Generates SSID and passphrase for a WEP network using random
+ generator.
+
+ Args:
+ mirror_ap: Boolean, determines if both APs use the same hostapd
+ config or different configs.
+ networks: List of WEP networks.
+ same_ssid: Boolean, determines if both bands on AP use the same
+ SSID.
+ ssid_length_2gecond AP Int, number of characters to use for 2G SSID.
+ ssid_length_5g: Int, number of characters to use for 5G SSID.
+ passphrase_length_2g: Int, length of password for 2G network.
+ passphrase_length_5g: Int, length of password for 5G network.
+
+ Returns: A dict of 2G and 5G network lists for hostapd configuration.
+
+ """
+ network_dict_2g = {}
+ network_dict_5g = {}
+ ref_5g_security = hostapd_constants.WEP_STRING
+ ref_2g_security = hostapd_constants.WEP_STRING
+
+ if same_ssid:
+ ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
+ ref_5g_ssid = ref_2g_ssid
+
+ ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g)
+ ref_5g_passphrase = ref_2g_passphrase
+
+ else:
+ ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
+ ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g)
+
+ ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
+ ref_5g_passphrase = utils.rand_hex_str(passphrase_length_5g)
+
+ network_dict_2g = {
+ "SSID": ref_2g_ssid,
+ "security": ref_2g_security,
+ "wepKeys": [ref_2g_passphrase] * 4,
+ "hiddenSSID": hidden
+ }
+
+ network_dict_5g = {
+ "SSID": ref_5g_ssid,
+ "security": ref_5g_security,
+ "wepKeys": [ref_2g_passphrase] * 4,
+ "hiddenSSID": hidden
+ }
+
+ ap = 0
+ for ap in range(MAX_AP_COUNT):
+ networks.append({
+ "2g": copy.copy(network_dict_2g),
+ "5g": copy.copy(network_dict_5g)
+ })
+ if not mirror_ap:
+ break
+ return {"2g": network_dict_2g, "5g": network_dict_5g}
+
+ def update_bssid(self, ap_instance, ap, network, band):
+ """Get bssid and update network dictionary.
+
+ Args:
+ ap_instance: Accesspoint index that was configured.
+ ap: Accesspoint object corresponding to ap_instance.
+ network: Network dictionary.
+ band: Wifi networks' band.
+
+ """
+ bssid = ap.get_bssid_from_ssid(network["SSID"], band)
+
+ if network["security"] == hostapd_constants.WPA2_STRING:
+ # TODO:(bamahadev) Change all occurances of reference_networks
+ # to wpa_networks.
+ self.reference_networks[ap_instance][band]["bssid"] = bssid
+ if network["security"] == hostapd_constants.WPA_STRING:
+ self.wpa_networks[ap_instance][band]["bssid"] = bssid
+ if network["security"] == hostapd_constants.WEP_STRING:
+ self.wep_networks[ap_instance][band]["bssid"] = bssid
+ if network["security"] == hostapd_constants.ENT_STRING:
+ if "bssid" not in self.ent_networks[ap_instance][band]:
+ self.ent_networks[ap_instance][band]["bssid"] = bssid
+ else:
+ self.ent_networks_pwd[ap_instance][band]["bssid"] = bssid
+ if network["security"] == 'none':
+ self.open_network[ap_instance][band]["bssid"] = bssid
+
+ def populate_bssid(self, ap_instance, ap, networks_5g, networks_2g):
+ """Get bssid for a given SSID and add it to the network dictionary.
+
+ Args:
+ ap_instance: Accesspoint index that was configured.
+ ap: Accesspoint object corresponding to ap_instance.
+ networks_5g: List of 5g networks configured on the APs.
+ networks_2g: List of 2g networks configured on the APs.
+
+ """
+
+ if not (networks_5g or networks_2g):
+ return
+
+ for network in networks_5g:
+ if 'channel' in network:
+ continue
+ self.update_bssid(ap_instance, ap, network,
+ hostapd_constants.BAND_5G)
+
+ for network in networks_2g:
+ if 'channel' in network:
+ continue
+ self.update_bssid(ap_instance, ap, network,
+ hostapd_constants.BAND_2G)
+
+ def configure_openwrt_ap_and_start(
+ self,
+ channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+ channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
+ ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+ passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
+ ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+ passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
+ mirror_ap=False,
+ hidden=False,
+ same_ssid=False,
+ open_network=False,
+ wpa_network=False,
+ wep_network=False,
+ ent_network=False,
+ ent_network_pwd=False,
+ owe_network=False,
+ sae_network=False,
+ radius_conf_2g=None,
+ radius_conf_5g=None,
+ radius_conf_pwd=None,
+ ap_count=1):
+ """Create, configure and start OpenWrt AP.
+
+ Args:
+ channel_5g: 5G channel to configure.
+ channel_2g: 2G channel to configure.
+ ssid_length_2g: Int, number of characters to use for 2G SSID.
+ passphrase_length_2g: Int, length of password for 2G network.
+ ssid_length_5g: Int, number of characters to use for 5G SSID.
+ passphrase_length_5g: Int, length of password for 5G network.
+ same_ssid: Boolean, determines if both bands on AP use the same SSID.
+ open_network: Boolean, to check if open network should be configured.
+ wpa_network: Boolean, to check if wpa network should be configured.
+ wep_network: Boolean, to check if wep network should be configured.
+ ent_network: Boolean, to check if ent network should be configured.
+ ent_network_pwd: Boolean, to check if ent pwd network should be configured.
+ owe_network: Boolean, to check if owe network should be configured.
+ sae_network: Boolean, to check if sae network should be configured.
+ radius_conf_2g: dictionary with enterprise radius server details.
+ radius_conf_5g: dictionary with enterprise radius server details.
+ radius_conf_pwd: dictionary with enterprise radiuse server details.
+ ap_count: APs to configure.
+ """
+ if mirror_ap and ap_count == 1:
+ raise ValueError("ap_count cannot be 1 if mirror_ap is True.")
+
+ self.reference_networks = []
+ self.wpa_networks = []
+ self.wep_networks = []
+ self.ent_networks = []
+ self.ent_networks_pwd = []
+ self.open_network = []
+ self.owe_networks = []
+ self.sae_networks = []
+ self.bssid_map = []
+ for i in range(ap_count):
+ network_list = []
+ if wpa_network:
+ wpa_dict = self.get_psk_network(mirror_ap,
+ self.reference_networks,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g,
+ passphrase_length_2g,
+ passphrase_length_5g)
+ wpa_dict[hostapd_constants.BAND_2G]["security"] = "psk2"
+ wpa_dict[hostapd_constants.BAND_5G]["security"] = "psk2"
+ self.wpa_networks.append(wpa_dict)
+ network_list.append(wpa_dict)
+ if wep_network:
+ wep_dict = self.get_wep_network(mirror_ap,
+ self.wep_networks,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g)
+ network_list.append(wep_dict)
+ if ent_network:
+ ent_dict = self.get_open_network(mirror_ap,
+ self.ent_networks,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g)
+ ent_dict["2g"]["security"] = "wpa2"
+ ent_dict["2g"].update(radius_conf_2g)
+ ent_dict["5g"]["security"] = "wpa2"
+ ent_dict["5g"].update(radius_conf_5g)
+ network_list.append(ent_dict)
+ if ent_network_pwd:
+ ent_pwd_dict = self.get_open_network(mirror_ap,
+ self.ent_networks_pwd,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g)
+ ent_pwd_dict["2g"]["security"] = "wpa2"
+ ent_pwd_dict["2g"].update(radius_conf_pwd)
+ ent_pwd_dict["5g"]["security"] = "wpa2"
+ ent_pwd_dict["5g"].update(radius_conf_pwd)
+ network_list.append(ent_pwd_dict)
+ if open_network:
+ open_dict = self.get_open_network(mirror_ap,
+ self.open_network,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g)
+ network_list.append(open_dict)
+ if owe_network:
+ owe_dict = self.get_open_network(mirror_ap,
+ self.owe_networks,
+ hidden,
+ same_ssid,
+ ssid_length_2g,
+ ssid_length_5g,
+ "OWE")
+ owe_dict[hostapd_constants.BAND_2G]["security"] = "owe"
+ owe_dict[hostapd_constants.BAND_5G]["security"] = "owe"
+ network_list.append(owe_dict)
+ if sae_network:
+ sae_dict = self.get_psk_network(mirror_ap,
+ self.sae_networks,
+ hidden,
+ same_ssid,
+ hostapd_constants.WPA3_KEY_MGMT,
+ ssid_length_2g,
+ ssid_length_5g,
+ passphrase_length_2g,
+ passphrase_length_5g)
+ sae_dict[hostapd_constants.BAND_2G]["security"] = "sae"
+ sae_dict[hostapd_constants.BAND_5G]["security"] = "sae"
+ network_list.append(sae_dict)
+ self.access_points[i].configure_ap(network_list,
+ channel_2g,
+ channel_5g)
+ self.access_points[i].start_ap()
+ self.bssid_map.append(
+ self.access_points[i].get_bssids_for_wifi_networks())
+ if mirror_ap:
+ self.access_points[i+1].configure_ap(network_list,
+ channel_2g,
+ channel_5g)
+ self.access_points[i+1].start_ap()
+ self.bssid_map.append(
+ self.access_points[i+1].get_bssids_for_wifi_networks())
+ break
+
+ def legacy_configure_ap_and_start(
+ self,
+ channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+ channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
+ max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G,
+ max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G,
+ ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+ ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
+ ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+ ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
+ hidden=False,
+ same_ssid=False,
+ mirror_ap=True,
+ wpa_network=False,
+ wep_network=False,
+ ent_network=False,
+ radius_conf_2g=None,
+ radius_conf_5g=None,
+ ent_network_pwd=False,
+ radius_conf_pwd=None,
+ ap_count=1):
+
+ config_count = 1
+ count = 0
+
+ # For example, the NetworkSelector tests use 2 APs and require that
+ # both APs are not mirrored.
+ if not mirror_ap and ap_count == 1:
+ raise ValueError("ap_count cannot be 1 if mirror_ap is False.")
+
+ if not mirror_ap:
+ config_count = ap_count
+
+ self.user_params["reference_networks"] = []
+ self.user_params["open_network"] = []
+ if wpa_network:
+ self.user_params["wpa_networks"] = []
+ if wep_network:
+ self.user_params["wep_networks"] = []
+ if ent_network:
+ self.user_params["ent_networks"] = []
+ if ent_network_pwd:
+ self.user_params["ent_networks_pwd"] = []
+
+ # kill hostapd & dhcpd if the cleanup was not successful
+ for i in range(len(self.access_points)):
+ self.log.debug("Check ap state and cleanup")
+ self._cleanup_hostapd_and_dhcpd(i)
+
+ for count in range(config_count):
+
+ network_list_2g = []
+ network_list_5g = []
+
+ orig_network_list_2g = []
+ orig_network_list_5g = []
+
+ network_list_2g.append({"channel": channel_2g})
+ network_list_5g.append({"channel": channel_5g})
+
+ networks_dict = self.get_psk_network(
+ mirror_ap,
+ self.user_params["reference_networks"],
+ hidden=hidden,
+ same_ssid=same_ssid)
+ self.reference_networks = self.user_params["reference_networks"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ # When same_ssid is set, only configure one set of WPA networks.
+ # We cannot have more than one set because duplicate interface names
+ # are not allowed.
+ # TODO(bmahadev): Provide option to select the type of network,
+ # instead of defaulting to WPA.
+ if not same_ssid:
+ networks_dict = self.get_open_network(
+ mirror_ap,
+ self.user_params["open_network"],
+ hidden=hidden,
+ same_ssid=same_ssid)
+ self.open_network = self.user_params["open_network"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ if wpa_network:
+ networks_dict = self.get_psk_network(
+ mirror_ap,
+ self.user_params["wpa_networks"],
+ hidden=hidden,
+ same_ssid=same_ssid,
+ security_mode=hostapd_constants.WPA_STRING)
+ self.wpa_networks = self.user_params["wpa_networks"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ if wep_network:
+ networks_dict = self.get_wep_network(
+ mirror_ap,
+ self.user_params["wep_networks"],
+ hidden=hidden,
+ same_ssid=same_ssid)
+ self.wep_networks = self.user_params["wep_networks"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ if ent_network:
+ networks_dict = self.get_open_network(
+ mirror_ap,
+ self.user_params["ent_networks"],
+ hidden=hidden,
+ same_ssid=same_ssid)
+ networks_dict["2g"]["security"] = hostapd_constants.ENT_STRING
+ networks_dict["2g"].update(radius_conf_2g)
+ networks_dict["5g"]["security"] = hostapd_constants.ENT_STRING
+ networks_dict["5g"].update(radius_conf_5g)
+ self.ent_networks = self.user_params["ent_networks"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ if ent_network_pwd:
+ networks_dict = self.get_open_network(
+ mirror_ap,
+ self.user_params["ent_networks_pwd"],
+ hidden=hidden,
+ same_ssid=same_ssid)
+ networks_dict["2g"]["security"] = hostapd_constants.ENT_STRING
+ networks_dict["2g"].update(radius_conf_pwd)
+ networks_dict["5g"]["security"] = hostapd_constants.ENT_STRING
+ networks_dict["5g"].update(radius_conf_pwd)
+ self.ent_networks_pwd = self.user_params["ent_networks_pwd"]
+
+ network_list_2g.append(networks_dict["2g"])
+ network_list_5g.append(networks_dict["5g"])
+
+ orig_network_list_5g = copy.copy(network_list_5g)
+ orig_network_list_2g = copy.copy(network_list_2g)
+
+ if len(network_list_5g) > 1:
+ self.config_5g = self._generate_legacy_ap_config(network_list_5g)
+ if len(network_list_2g) > 1:
+ self.config_2g = self._generate_legacy_ap_config(network_list_2g)
+
+ self.access_points[count].start_ap(self.config_2g)
+ self.access_points[count].start_ap(self.config_5g)
+ self.populate_bssid(count, self.access_points[count], orig_network_list_5g,
+ orig_network_list_2g)
+
+ # Repeat configuration on the second router.
+ if mirror_ap and ap_count == 2:
+ self.access_points[AP_2].start_ap(self.config_2g)
+ self.access_points[AP_2].start_ap(self.config_5g)
+ self.populate_bssid(AP_2, self.access_points[AP_2],
+ orig_network_list_5g, orig_network_list_2g)
+
+ def _kill_processes(self, ap, daemon):
+ """ Kill hostapd and dhcpd daemons
+
+ Args:
+ ap: AP to cleanup
+ daemon: process to kill
+
+ Returns: True/False if killing process is successful
+ """
+ self.log.info("Killing %s" % daemon)
+ pids = ap.ssh.run('pidof %s' % daemon, ignore_status=True)
+ if pids.stdout:
+ ap.ssh.run('kill %s' % pids.stdout, ignore_status=True)
+ time.sleep(3)
+ pids = ap.ssh.run('pidof %s' % daemon, ignore_status=True)
+ if pids.stdout:
+ return False
+ return True
+
+ def _cleanup_hostapd_and_dhcpd(self, count):
+ """ Check if AP was cleaned up properly
+
+ Kill hostapd and dhcpd processes if cleanup was not successful in the
+ last run
+
+ Args:
+ count: AP to check
+
+ Returns:
+ New AccessPoint object if AP required cleanup
+
+ Raises:
+ Error: if the AccessPoint timed out to setup
+ """
+ ap = self.access_points[count]
+ phy_ifaces = ap.interfaces.get_physical_interface()
+ kill_hostapd = False
+ for iface in phy_ifaces:
+ if '2g_' in iface or '5g_' in iface or 'xg_' in iface:
+ kill_hostapd = True
+ break
+
+ if not kill_hostapd:
+ return
+
+ self.log.debug("Cleanup AP")
+ if not self._kill_processes(ap, 'hostapd') or \
+ not self._kill_processes(ap, 'dhcpd'):
+ raise("Failed to cleanup AP")
+
+ ap.__init__(self.user_params['AccessPoint'][count])
+
+ def _generate_legacy_ap_config(self, network_list):
+ bss_settings = []
+ wlan_2g = self.access_points[AP_1].wlan_2g
+ wlan_5g = self.access_points[AP_1].wlan_5g
+ ap_settings = network_list.pop(0)
+ # TODO:(bmahadev) This is a bug. We should not have to pop the first
+ # network in the list and treat it as a separate case. Instead,
+ # create_ap_preset() should be able to take NULL ssid and security and
+ # build config based on the bss_Settings alone.
+ hostapd_config_settings = network_list.pop(0)
+ for network in network_list:
+ if "password" in network:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=network["hiddenSSID"],
+ security=hostapd_security.Security(
+ security_mode=network["security"],
+ password=network["password"])))
+ elif "wepKeys" in network:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=network["hiddenSSID"],
+ security=hostapd_security.Security(
+ security_mode=network["security"],
+ password=network["wepKeys"][0])))
+ elif network["security"] == hostapd_constants.ENT_STRING:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=network["hiddenSSID"],
+ security=hostapd_security.Security(
+ security_mode=network["security"],
+ radius_server_ip=network["radius_server_ip"],
+ radius_server_port=network["radius_server_port"],
+ radius_server_secret=network["radius_server_secret"])))
+ else:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=network["hiddenSSID"]))
+ if "password" in hostapd_config_settings:
+ config = hostapd_ap_preset.create_ap_preset(
+ iface_wlan_2g=wlan_2g,
+ iface_wlan_5g=wlan_5g,
+ channel=ap_settings["channel"],
+ ssid=hostapd_config_settings["SSID"],
+ hidden=hostapd_config_settings["hiddenSSID"],
+ security=hostapd_security.Security(
+ security_mode=hostapd_config_settings["security"],
+ password=hostapd_config_settings["password"]),
+ bss_settings=bss_settings)
+ elif "wepKeys" in hostapd_config_settings:
+ config = hostapd_ap_preset.create_ap_preset(
+ iface_wlan_2g=wlan_2g,
+ iface_wlan_5g=wlan_5g,
+ channel=ap_settings["channel"],
+ ssid=hostapd_config_settings["SSID"],
+ hidden=hostapd_config_settings["hiddenSSID"],
+ security=hostapd_security.Security(
+ security_mode=hostapd_config_settings["security"],
+ password=hostapd_config_settings["wepKeys"][0]),
+ bss_settings=bss_settings)
+ else:
+ config = hostapd_ap_preset.create_ap_preset(
+ iface_wlan_2g=wlan_2g,
+ iface_wlan_5g=wlan_5g,
+ channel=ap_settings["channel"],
+ ssid=hostapd_config_settings["SSID"],
+ hidden=hostapd_config_settings["hiddenSSID"],
+ bss_settings=bss_settings)
+ return config
+
+ def configure_packet_capture(
+ self,
+ channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+ channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G):
+ """Configure packet capture for 2G and 5G bands.
+
+ Args:
+ channel_5g: Channel to set the monitor mode to for 5G band.
+ channel_2g: Channel to set the monitor mode to for 2G band.
+ """
+ self.packet_capture = self.packet_capture[0]
+ result = self.packet_capture.configure_monitor_mode(
+ hostapd_constants.BAND_2G, channel_2g)
+ if not result:
+ raise ValueError("Failed to configure channel for 2G band")
+
+ result = self.packet_capture.configure_monitor_mode(
+ hostapd_constants.BAND_5G, channel_5g)
+ if not result:
+ raise ValueError("Failed to configure channel for 5G band.")
+
+ @staticmethod
+ def wifi_test_wrap(fn):
+ def _safe_wrap_test_case(self, *args, **kwargs):
+ test_id = "%s:%s:%s" % (self.__class__.__name__, self.test_name,
+ self.log_begin_time.replace(' ', '-'))
+ self.test_id = test_id
+ self.result_detail = ""
+ tries = int(self.user_params.get("wifi_auto_rerun", 3))
+ for ad in self.android_devices:
+ ad.log_path = self.log_path
+ for i in range(tries + 1):
+ result = True
+ if i > 0:
+ log_string = "[Test Case] RETRY:%s %s" % (i, self.test_name)
+ self.log.info(log_string)
+ self._teardown_test(self.test_name)
+ self._setup_test(self.test_name)
+ try:
+ result = fn(self, *args, **kwargs)
+ except signals.TestFailure as e:
+ self.log.warn("Error msg: %s" % e)
+ if self.result_detail:
+ signal.details = self.result_detail
+ result = False
+ except signals.TestSignal:
+ if self.result_detail:
+ signal.details = self.result_detail
+ raise
+ except Exception as e:
+ self.log.exception(e)
+ asserts.fail(self.result_detail)
+ if result is False:
+ if i < tries:
+ continue
+ else:
+ break
+ if result is not False:
+ asserts.explicit_pass(self.result_detail)
+ else:
+ asserts.fail(self.result_detail)
+
+ return _safe_wrap_test_case
diff --git a/acts_tests/acts_contrib/test_utils/wifi/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py
new file mode 100644
index 0000000..4758129
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+
+
+class AwareBaseTest(BaseTestClass):
+ # message ID counter to make sure all uses are unique
+ msg_id = 0
+
+ # offset (in seconds) to separate the start-up of multiple devices.
+ # De-synchronizes the start-up time so that they don't start and stop scanning
+ # at the same time - which can lead to very long clustering times.
+ device_startup_offset = 2
+
+ def setup_test(self):
+ required_params = ("aware_default_power_mode",
+ "dbs_supported_models",)
+ self.unpack_userparams(required_params)
+
+ for ad in self.android_devices:
+ asserts.skip_if(
+ not ad.droid.doesDeviceSupportWifiAwareFeature(),
+ "Device under test does not support Wi-Fi Aware - skipping test"
+ )
+ aware_avail = ad.droid.wifiIsAwareAvailable()
+ ad.droid.wifiP2pClose()
+ wutils.wifi_toggle_state(ad, True)
+ utils.set_location_service(ad, True)
+ if not aware_avail:
+ self.log.info('Aware not available. Waiting ...')
+ autils.wait_for_event(ad,
+ aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+ ad.aware_capabilities = autils.get_aware_capabilities(ad)
+ self.reset_device_parameters(ad)
+ self.reset_device_statistics(ad)
+ self.set_power_mode_parameters(ad)
+ wutils.set_wifi_country_code(ad, wutils.WifiEnums.CountryCode.US)
+ autils.configure_ndp_allow_any_override(ad, True)
+ # set randomization interval to 0 (disable) to reduce likelihood of
+ # interference in tests
+ autils.configure_mac_random_interval(ad, 0)
+ ad.ed.clear_all_events()
+
+ def teardown_test(self):
+ for ad in self.android_devices:
+ if not ad.droid.doesDeviceSupportWifiAwareFeature():
+ return
+ ad.droid.wifiP2pClose()
+ ad.droid.wifiAwareDestroyAll()
+ self.reset_device_parameters(ad)
+ autils.validate_forbidden_callbacks(ad)
+
+ def reset_device_parameters(self, ad):
+ """Reset device configurations which may have been set by tests. Should be
+ done before tests start (in case previous one was killed without tearing
+ down) and after they end (to leave device in usable state).
+
+ Args:
+ ad: device to be reset
+ """
+ ad.adb.shell("cmd wifiaware reset")
+
+ def reset_device_statistics(self, ad):
+ """Reset device statistics.
+
+ Args:
+ ad: device to be reset
+ """
+ ad.adb.shell("cmd wifiaware native_cb get_cb_count --reset")
+
+ def set_power_mode_parameters(self, ad):
+ """Set the power configuration DW parameters for the device based on any
+ configuration overrides (if provided)"""
+ if self.aware_default_power_mode == "INTERACTIVE":
+ autils.config_settings_high_power(ad)
+ elif self.aware_default_power_mode == "NON_INTERACTIVE":
+ autils.config_settings_low_power(ad)
+ else:
+ asserts.assert_false(
+ "The 'aware_default_power_mode' configuration must be INTERACTIVE or "
+ "NON_INTERACTIVE")
+
+ def get_next_msg_id(self):
+ """Increment the message ID and returns the new value. Guarantees that
+ each call to the method returns a unique value.
+
+ Returns: a new message id value.
+ """
+ self.msg_id = self.msg_id + 1
+ return self.msg_id
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+ ad.cat_adb_log(test_name, begin_time)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/aware/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py
new file mode 100644
index 0000000..d5a0381
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+######################################################
+# Aware power settings values for interactive (high power) and
+# non-interactive (low power) modes
+######################################################
+
+POWER_DW_24_INTERACTIVE = 1
+POWER_DW_5_INTERACTIVE = 1
+POWER_DISC_BEACON_INTERVAL_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE = 0
+
+POWER_DW_24_NON_INTERACTIVE = 4
+POWER_DW_5_NON_INTERACTIVE = 0
+POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_NON_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE = 0
+
+######################################################
+# Broadcast events
+######################################################
+BROADCAST_WIFI_AWARE_AVAILABLE = "WifiAwareAvailable"
+BROADCAST_WIFI_AWARE_NOT_AVAILABLE = "WifiAwareNotAvailable"
+
+######################################################
+# ConfigRequest keys
+######################################################
+
+CONFIG_KEY_5G_BAND = "Support5gBand"
+CONFIG_KEY_MASTER_PREF = "MasterPreference"
+CONFIG_KEY_CLUSTER_LOW = "ClusterLow"
+CONFIG_KEY_CLUSTER_HIGH = "ClusterHigh"
+CONFIG_KEY_ENABLE_IDEN_CB = "EnableIdentityChangeCallback"
+
+######################################################
+# Publish & Subscribe Config keys
+######################################################
+
+DISCOVERY_KEY_SERVICE_NAME = "ServiceName"
+DISCOVERY_KEY_SSI = "ServiceSpecificInfo"
+DISCOVERY_KEY_MATCH_FILTER = "MatchFilter"
+DISCOVERY_KEY_MATCH_FILTER_LIST = "MatchFilterList"
+DISCOVERY_KEY_DISCOVERY_TYPE = "DiscoveryType"
+DISCOVERY_KEY_TTL = "TtlSec"
+DISCOVERY_KEY_TERM_CB_ENABLED = "TerminateNotificationEnabled"
+DISCOVERY_KEY_RANGING_ENABLED = "RangingEnabled"
+DISCOVERY_KEY_MIN_DISTANCE_MM = "MinDistanceMm"
+DISCOVERY_KEY_MAX_DISTANCE_MM = "MaxDistanceMm"
+
+PUBLISH_TYPE_UNSOLICITED = 0
+PUBLISH_TYPE_SOLICITED = 1
+
+SUBSCRIBE_TYPE_PASSIVE = 0
+SUBSCRIBE_TYPE_ACTIVE = 1
+
+######################################################
+# WifiAwareAttachCallback events
+######################################################
+EVENT_CB_ON_ATTACHED = "WifiAwareOnAttached"
+EVENT_CB_ON_ATTACH_FAILED = "WifiAwareOnAttachFailed"
+
+######################################################
+# WifiAwareIdentityChangedListener events
+######################################################
+EVENT_CB_ON_IDENTITY_CHANGED = "WifiAwareOnIdentityChanged"
+
+# WifiAwareAttachCallback & WifiAwareIdentityChangedListener events keys
+EVENT_CB_KEY_REASON = "reason"
+EVENT_CB_KEY_MAC = "mac"
+EVENT_CB_KEY_LATENCY_MS = "latencyMs"
+EVENT_CB_KEY_TIMESTAMP_MS = "timestampMs"
+
+######################################################
+# WifiAwareDiscoverySessionCallback events
+######################################################
+SESSION_CB_ON_PUBLISH_STARTED = "WifiAwareSessionOnPublishStarted"
+SESSION_CB_ON_SUBSCRIBE_STARTED = "WifiAwareSessionOnSubscribeStarted"
+SESSION_CB_ON_SESSION_CONFIG_UPDATED = "WifiAwareSessionOnSessionConfigUpdated"
+SESSION_CB_ON_SESSION_CONFIG_FAILED = "WifiAwareSessionOnSessionConfigFailed"
+SESSION_CB_ON_SESSION_TERMINATED = "WifiAwareSessionOnSessionTerminated"
+SESSION_CB_ON_SERVICE_DISCOVERED = "WifiAwareSessionOnServiceDiscovered"
+SESSION_CB_ON_MESSAGE_SENT = "WifiAwareSessionOnMessageSent"
+SESSION_CB_ON_MESSAGE_SEND_FAILED = "WifiAwareSessionOnMessageSendFailed"
+SESSION_CB_ON_MESSAGE_RECEIVED = "WifiAwareSessionOnMessageReceived"
+
+# WifiAwareDiscoverySessionCallback events keys
+SESSION_CB_KEY_CB_ID = "callbackId"
+SESSION_CB_KEY_SESSION_ID = "discoverySessionId"
+SESSION_CB_KEY_REASON = "reason"
+SESSION_CB_KEY_PEER_ID = "peerId"
+SESSION_CB_KEY_SERVICE_SPECIFIC_INFO = "serviceSpecificInfo"
+SESSION_CB_KEY_MATCH_FILTER = "matchFilter"
+SESSION_CB_KEY_MATCH_FILTER_LIST = "matchFilterList"
+SESSION_CB_KEY_MESSAGE = "message"
+SESSION_CB_KEY_MESSAGE_ID = "messageId"
+SESSION_CB_KEY_MESSAGE_AS_STRING = "messageAsString"
+SESSION_CB_KEY_LATENCY_MS = "latencyMs"
+SESSION_CB_KEY_TIMESTAMP_MS = "timestampMs"
+SESSION_CB_KEY_DISTANCE_MM = "distanceMm"
+
+######################################################
+# WifiAwareRangingListener events (RttManager.RttListener)
+######################################################
+RTT_LISTENER_CB_ON_SUCCESS = "WifiAwareRangingListenerOnSuccess"
+RTT_LISTENER_CB_ON_FAILURE = "WifiAwareRangingListenerOnFailure"
+RTT_LISTENER_CB_ON_ABORT = "WifiAwareRangingListenerOnAborted"
+
+# WifiAwareRangingListener events (RttManager.RttListener) keys
+RTT_LISTENER_CB_KEY_CB_ID = "callbackId"
+RTT_LISTENER_CB_KEY_SESSION_ID = "sessionId"
+RTT_LISTENER_CB_KEY_RESULTS = "Results"
+RTT_LISTENER_CB_KEY_REASON = "reason"
+RTT_LISTENER_CB_KEY_DESCRIPTION = "description"
+
+######################################################
+# Capabilities keys
+######################################################
+
+CAP_MAX_CONCURRENT_AWARE_CLUSTERS = "maxConcurrentAwareClusters"
+CAP_MAX_PUBLISHES = "maxPublishes"
+CAP_MAX_SUBSCRIBES = "maxSubscribes"
+CAP_MAX_SERVICE_NAME_LEN = "maxServiceNameLen"
+CAP_MAX_MATCH_FILTER_LEN = "maxMatchFilterLen"
+CAP_MAX_TOTAL_MATCH_FILTER_LEN = "maxTotalMatchFilterLen"
+CAP_MAX_SERVICE_SPECIFIC_INFO_LEN = "maxServiceSpecificInfoLen"
+CAP_MAX_EXTENDED_SERVICE_SPECIFIC_INFO_LEN = "maxExtendedServiceSpecificInfoLen"
+CAP_MAX_NDI_INTERFACES = "maxNdiInterfaces"
+CAP_MAX_NDP_SESSIONS = "maxNdpSessions"
+CAP_MAX_APP_INFO_LEN = "maxAppInfoLen"
+CAP_MAX_QUEUED_TRANSMIT_MESSAGES = "maxQueuedTransmitMessages"
+CAP_MAX_SUBSCRIBE_INTERFACE_ADDRESSES = "maxSubscribeInterfaceAddresses"
+CAP_SUPPORTED_CIPHER_SUITES = "supportedCipherSuites"
+
+######################################################
+# WifiAwareNetworkCapabilities keys
+######################################################
+
+NET_CAP_IPV6 = "aware_ipv6"
+NET_CAP_PORT = "aware_port"
+NET_CAP_TRANSPORT_PROTOCOL = "aware_transport_protocol"
+
+######################################################
+
+# Aware NDI (NAN data-interface) name prefix
+AWARE_NDI_PREFIX = "aware_data"
+
+# Aware discovery channels
+AWARE_DISCOVERY_CHANNEL_24_BAND = 6
+AWARE_DISCOVERY_CHANNEL_5_BAND = 149
+
+# Aware Data-Path Constants
+DATA_PATH_INITIATOR = 0
+DATA_PATH_RESPONDER = 1
+
+# Maximum send retry
+MAX_TX_RETRIES = 5
+
+# Callback keys (for 'adb shell cmd wifiaware native_cb get_cb_count')
+CB_EV_CLUSTER = "0"
+CB_EV_DISABLED = "1"
+CB_EV_PUBLISH_TERMINATED = "2"
+CB_EV_SUBSCRIBE_TERMINATED = "3"
+CB_EV_MATCH = "4"
+CB_EV_MATCH_EXPIRED = "5"
+CB_EV_FOLLOWUP_RECEIVED = "6"
+CB_EV_TRANSMIT_FOLLOWUP = "7"
+CB_EV_DATA_PATH_REQUEST = "8"
+CB_EV_DATA_PATH_CONFIRM = "9"
+CB_EV_DATA_PATH_TERMINATED = "10"
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py
new file mode 100644
index 0000000..222c6d8
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py
@@ -0,0 +1,1009 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import base64
+import json
+import queue
+import re
+import statistics
+import time
+from acts import asserts
+
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.net import socket_test_utils as sutils
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 10
+
+# semi-arbitrary timeout for network formation events. Based on framework
+# timeout for NDP (NAN data-path) negotiation to be completed.
+EVENT_NDP_TIMEOUT = 20
+
+# number of second to 'reasonably' wait to make sure that devices synchronize
+# with each other - useful for OOB test cases, where the OOB discovery would
+# take some time
+WAIT_FOR_CLUSTER = 5
+
+
+def decorate_event(event_name, id):
+ return '%s_%d' % (event_name, id)
+
+
+def wait_for_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for the specified event or timeout.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait on
+ timeout: Number of seconds to wait
+ Returns:
+ The event (if available)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%s%s: %s', prefix, event_name, event['data'])
+ return event
+ except queue.Empty:
+ ad.log.info('%sTimed out while waiting for %s', prefix, event_name)
+ asserts.fail(event_name)
+
+
+def wait_for_event_with_keys(ad, event_name, timeout=EVENT_TIMEOUT,
+ *keyvalues):
+ """Wait for the specified event contain the key/value pairs or timeout
+
+ Args:
+ ad: The android device
+ event_name: The event to wait on
+ timeout: Number of seconds to wait
+ keyvalues: (kay, value) pairs
+ Returns:
+ The event (if available)
+ """
+
+ def filter_callbacks(event, keyvalues):
+ for keyvalue in keyvalues:
+ key, value = keyvalue
+ if event['data'][key] != value:
+ return False
+ return True
+
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.wait_for_event(event_name, filter_callbacks, timeout,
+ keyvalues)
+ ad.log.info('%s%s: %s', prefix, event_name, event['data'])
+ return event
+ except queue.Empty:
+ ad.log.info('%sTimed out while waiting for %s (%s)', prefix,
+ event_name, keyvalues)
+ asserts.fail(event_name)
+
+
+def fail_on_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for a timeout period and looks for the specified event - fails if it
+ is observed.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait for (and fail on its appearance)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%sReceived unwanted %s: %s', prefix, event_name,
+ event['data'])
+ asserts.fail(event_name, extras=event)
+ except queue.Empty:
+ ad.log.info('%s%s not seen (as expected)', prefix, event_name)
+ return
+
+
+def fail_on_event_with_keys(ad, event_name, timeout=EVENT_TIMEOUT, *keyvalues):
+ """Wait for a timeout period and looks for the specified event which contains
+ the key/value pairs - fails if it is observed.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait on
+ timeout: Number of seconds to wait
+ keyvalues: (kay, value) pairs
+ """
+
+ def filter_callbacks(event, keyvalues):
+ for keyvalue in keyvalues:
+ key, value = keyvalue
+ if event['data'][key] != value:
+ return False
+ return True
+
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.wait_for_event(event_name, filter_callbacks, timeout,
+ keyvalues)
+ ad.log.info('%sReceived unwanted %s: %s', prefix, event_name,
+ event['data'])
+ asserts.fail(event_name, extras=event)
+ except queue.Empty:
+ ad.log.info('%s%s (%s) not seen (as expected)', prefix, event_name,
+ keyvalues)
+ return
+
+
+def verify_no_more_events(ad, timeout=EVENT_TIMEOUT):
+ """Verify that there are no more events in the queue.
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ should_fail = False
+ try:
+ while True:
+ event = ad.ed.pop_events('.*', timeout, freq=0)
+ ad.log.info('%sQueue contains %s', prefix, event)
+ should_fail = True
+ except queue.Empty:
+ if should_fail:
+ asserts.fail('%sEvent queue not empty' % prefix)
+ ad.log.info('%sNo events in the queue (as expected)', prefix)
+ return
+
+
+def encode_list(list_of_objects):
+ """Converts the list of strings or bytearrays to a list of b64 encoded
+ bytearrays.
+
+ A None object is treated as a zero-length bytearray.
+
+ Args:
+ list_of_objects: A list of strings or bytearray objects
+ Returns: A list of the same objects, converted to bytes and b64 encoded.
+ """
+ encoded_list = []
+ for obj in list_of_objects:
+ if obj is None:
+ obj = bytes()
+ if isinstance(obj, str):
+ encoded_list.append(
+ base64.b64encode(bytes(obj, 'utf-8')).decode('utf-8'))
+ else:
+ encoded_list.append(base64.b64encode(obj).decode('utf-8'))
+ return encoded_list
+
+
+def decode_list(list_of_b64_strings):
+ """Converts the list of b64 encoded strings to a list of bytearray.
+
+ Args:
+ list_of_b64_strings: list of strings, each of which is b64 encoded array
+ Returns: a list of bytearrays.
+ """
+ decoded_list = []
+ for str in list_of_b64_strings:
+ decoded_list.append(base64.b64decode(str))
+ return decoded_list
+
+
+def construct_max_match_filter(max_size):
+ """Constructs a maximum size match filter that fits into the 'max_size' bytes.
+
+ Match filters are a set of LVs (Length, Value pairs) where L is 1 byte. The
+ maximum size match filter will contain max_size/2 LVs with all Vs (except
+ possibly the last one) of 1 byte, the last V may be 2 bytes for odd max_size.
+
+ Args:
+ max_size: Maximum size of the match filter.
+ Returns: an array of bytearrays.
+ """
+ mf_list = []
+ num_lvs = max_size // 2
+ for i in range(num_lvs - 1):
+ mf_list.append(bytes([i]))
+ if (max_size % 2 == 0):
+ mf_list.append(bytes([255]))
+ else:
+ mf_list.append(bytes([254, 255]))
+ return mf_list
+
+
+def assert_equal_strings(first, second, msg=None, extras=None):
+ """Assert equality of the string operands - where None is treated as equal to
+ an empty string (''), otherwise fail the test.
+
+ Error message is "first != second" by default. Additional explanation can
+ be supplied in the message.
+
+ Args:
+ first, seconds: The strings that are evaluated for equality.
+ msg: A string that adds additional info about the failure.
+ extras: An optional field for extra information to be included in
+ test result.
+ """
+ if first == None:
+ first = ''
+ if second == None:
+ second = ''
+ asserts.assert_equal(first, second, msg, extras)
+
+
+def get_aware_capabilities(ad):
+ """Get the Wi-Fi Aware capabilities from the specified device. The
+ capabilities are a dictionary keyed by aware_const.CAP_* keys.
+
+ Args:
+ ad: the Android device
+ Returns: the capability dictionary.
+ """
+ return json.loads(ad.adb.shell('cmd wifiaware state_mgr get_capabilities'))
+
+
+def get_wifi_mac_address(ad):
+ """Get the Wi-Fi interface MAC address as a upper-case string of hex digits
+ without any separators (e.g. ':').
+
+ Args:
+ ad: Device on which to run.
+ """
+ return ad.droid.wifiGetConnectionInfo()['mac_address'].upper().replace(
+ ':', '')
+
+
+def validate_forbidden_callbacks(ad, limited_cb=None):
+ """Validate that the specified callbacks have not been called more then permitted.
+
+ In addition to the input configuration also validates that forbidden callbacks
+ have never been called.
+
+ Args:
+ ad: Device on which to run.
+ limited_cb: Dictionary of CB_EV_* ids and maximum permitted calls (0
+ meaning never).
+ """
+ cb_data = json.loads(ad.adb.shell('cmd wifiaware native_cb get_cb_count'))
+
+ if limited_cb is None:
+ limited_cb = {}
+ # add callbacks which should never be called
+ limited_cb[aconsts.CB_EV_MATCH_EXPIRED] = 0
+
+ fail = False
+ for cb_event in limited_cb.keys():
+ if cb_event in cb_data:
+ if cb_data[cb_event] > limited_cb[cb_event]:
+ fail = True
+ ad.log.info(
+ 'Callback %s observed %d times: more then permitted %d times',
+ cb_event, cb_data[cb_event], limited_cb[cb_event])
+
+ asserts.assert_false(fail, 'Forbidden callbacks observed', extras=cb_data)
+
+
+def extract_stats(ad, data, results, key_prefix, log_prefix):
+ """Extract statistics from the data, store in the results dictionary, and
+ output to the info log.
+
+ Args:
+ ad: Android device (for logging)
+ data: A list containing the data to be analyzed.
+ results: A dictionary into which to place the statistics.
+ key_prefix: A string prefix to use for the dict keys storing the
+ extracted stats.
+ log_prefix: A string prefix to use for the info log.
+ include_data: If True includes the raw data in the dictionary,
+ otherwise just the stats.
+ """
+ num_samples = len(data)
+ results['%snum_samples' % key_prefix] = num_samples
+
+ if not data:
+ return
+
+ data_min = min(data)
+ data_max = max(data)
+ data_mean = statistics.mean(data)
+ data_cdf = extract_cdf(data)
+ data_cdf_decile = extract_cdf_decile(data_cdf)
+
+ results['%smin' % key_prefix] = data_min
+ results['%smax' % key_prefix] = data_max
+ results['%smean' % key_prefix] = data_mean
+ results['%scdf' % key_prefix] = data_cdf
+ results['%scdf_decile' % key_prefix] = data_cdf_decile
+ results['%sraw_data' % key_prefix] = data
+
+ if num_samples > 1:
+ data_stdev = statistics.stdev(data)
+ results['%sstdev' % key_prefix] = data_stdev
+ ad.log.info(
+ '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f, cdf_decile=%s',
+ log_prefix, num_samples, data_min, data_max, data_mean, data_stdev,
+ data_cdf_decile)
+ else:
+ ad.log.info(
+ '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, cdf_decile=%s',
+ log_prefix, num_samples, data_min, data_max, data_mean,
+ data_cdf_decile)
+
+
+def extract_cdf_decile(cdf):
+ """Extracts the 10%, 20%, ..., 90% points from the CDF and returns their
+ value (a list of 9 values).
+
+ Since CDF may not (will not) have exact x% value picks the value >= x%.
+
+ Args:
+ cdf: a list of 2 lists, the X and Y of the CDF.
+ """
+ decades = []
+ next_decade = 10
+ for x, y in zip(cdf[0], cdf[1]):
+ while 100 * y >= next_decade:
+ decades.append(x)
+ next_decade = next_decade + 10
+ if next_decade == 100:
+ break
+ return decades
+
+
+def extract_cdf(data):
+ """Calculates the Cumulative Distribution Function (CDF) of the data.
+
+ Args:
+ data: A list containing data (does not have to be sorted).
+
+ Returns: a list of 2 lists: the X and Y axis of the CDF.
+ """
+ x = []
+ cdf = []
+ if not data:
+ return (x, cdf)
+
+ all_values = sorted(data)
+ for val in all_values:
+ if not x:
+ x.append(val)
+ cdf.append(1)
+ else:
+ if x[-1] == val:
+ cdf[-1] += 1
+ else:
+ x.append(val)
+ cdf.append(cdf[-1] + 1)
+
+ scale = 1.0 / len(all_values)
+ for i in range(len(cdf)):
+ cdf[i] = cdf[i] * scale
+
+ return (x, cdf)
+
+
+def get_mac_addr(device, interface):
+ """Get the MAC address of the specified interface. Uses ifconfig and parses
+ its output. Normalizes string to remove ':' and upper case.
+
+ Args:
+ device: Device on which to query the interface MAC address.
+ interface: Name of the interface for which to obtain the MAC address.
+ """
+ out = device.adb.shell("ifconfig %s" % interface)
+ res = re.match(".* HWaddr (\S+).*", out, re.S)
+ asserts.assert_true(
+ res,
+ 'Unable to obtain MAC address for interface %s' % interface,
+ extras=out)
+ return res.group(1).upper().replace(':', '')
+
+
+def get_ipv6_addr(device, interface):
+ """Get the IPv6 address of the specified interface. Uses ifconfig and parses
+ its output. Returns a None if the interface does not have an IPv6 address
+ (indicating it is not UP).
+
+ Args:
+ device: Device on which to query the interface IPv6 address.
+ interface: Name of the interface for which to obtain the IPv6 address.
+ """
+ out = device.adb.shell("ifconfig %s" % interface)
+ res = re.match(".*inet6 addr: (\S+)/.*", out, re.S)
+ if not res:
+ return None
+ return res.group(1)
+
+
+def verify_socket_connect(dut_s, dut_c, ipv6_s, ipv6_c, port):
+ """Verify the socket connection between server (dut_s) and client (dut_c)
+ using the given IPv6 addresses.
+
+ Opens a ServerSocket on the server and tries to connect to it
+ from the client.
+
+ Args:
+ dut_s, dut_c: the server and client devices under test (DUTs)
+ ipv6_s, ipv6_c: the scoped link-local addresses of the server and client.
+ port: the port to use
+ Return: True on success, False otherwise
+ """
+ server_sock = None
+ sock_c = None
+ sock_s = None
+ try:
+ server_sock = sutils.open_server_socket(dut_s, ipv6_s, port)
+ port_to_use = port
+ if port == 0:
+ port_to_use = dut_s.droid.getTcpServerSocketPort(server_sock)
+ sock_c, sock_s = sutils.open_connect_socket(
+ dut_c, dut_s, ipv6_c, ipv6_s, 0, port_to_use, server_sock)
+ except:
+ return False
+ finally:
+ if sock_c is not None:
+ sutils.close_socket(dut_c, sock_c)
+ if sock_s is not None:
+ sutils.close_socket(dut_s, sock_s)
+ if server_sock is not None:
+ sutils.close_server_socket(dut_s, server_sock)
+ return True
+
+
+def run_ping6(dut, target_ip, duration=60):
+ """Run ping test and return the latency result
+
+ Args:
+ dut: the dut which run the ping cmd
+ target_ip: target IP Address for ping
+ duration: the duration time of the ping
+
+ return: dict contains "min/avg/max/mdev" result
+ """
+ cmd = "ping6 -w %d %s" % (duration, target_ip)
+ ping_result = dut.adb.shell(cmd, timeout=duration + 1)
+ res = re.match(".*mdev = (\S+) .*", ping_result, re.S)
+ asserts.assert_true(res, "Cannot reach the IP address %s", target_ip)
+ title = ["min", "avg", "max", "mdev"]
+ result = res.group(1).split("/")
+ latency_result = {}
+ for i in range(len(title)):
+ latency_result[title[i]] = result[i]
+ return latency_result
+
+
+#########################################################
+# Aware primitives
+#########################################################
+
+
+def request_network(dut, ns):
+ """Request a Wi-Fi Aware network.
+
+ Args:
+ dut: Device
+ ns: Network specifier
+ Returns: the request key
+ """
+ network_req = {"TransportType": 5, "NetworkSpecifier": ns}
+ return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
+
+
+def get_network_specifier(dut, id, dev_type, peer_mac, sec):
+ """Create a network specifier for the device based on the security
+ configuration.
+
+ Args:
+ dut: device
+ id: session ID
+ dev_type: device type - Initiator or Responder
+ peer_mac: the discovery MAC address of the peer
+ sec: security configuration
+ """
+ if sec is None:
+ return dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ id, dev_type, peer_mac)
+ if isinstance(sec, str):
+ return dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ id, dev_type, peer_mac, sec)
+ return dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ id, dev_type, peer_mac, None, sec)
+
+
+def configure_power_setting(device, mode, name, value):
+ """Use the command-line API to configure the power setting
+
+ Args:
+ device: Device on which to perform configuration
+ mode: The power mode being set, should be "default", "inactive", or "idle"
+ name: One of the power settings from 'wifiaware set-power'.
+ value: An integer.
+ """
+ device.adb.shell(
+ "cmd wifiaware native_api set-power %s %s %d" % (mode, name, value))
+
+
+def configure_mac_random_interval(device, interval_sec):
+ """Use the command-line API to configure the MAC address randomization
+ interval.
+
+ Args:
+ device: Device on which to perform configuration
+ interval_sec: The MAC randomization interval in seconds. A value of 0
+ disables all randomization.
+ """
+ device.adb.shell("cmd wifiaware native_api set mac_random_interval_sec %d"
+ % interval_sec)
+
+
+def configure_ndp_allow_any_override(device, override_api_check):
+ """Use the command-line API to configure whether an NDP Responder may be
+ configured to accept an NDP request from ANY peer.
+
+ By default the target API level of the requesting app determines whether such
+ configuration is permitted. This allows overriding the API check and allowing
+ it.
+
+ Args:
+ device: Device on which to perform configuration.
+ override_api_check: True to allow a Responder to ANY configuration, False to
+ perform the API level check.
+ """
+ device.adb.shell("cmd wifiaware state_mgr allow_ndp_any %s" %
+ ("true" if override_api_check else "false"))
+
+
+def config_settings_high_power(device):
+ """Configure device's power settings values to high power mode -
+ whether device is in interactive or non-interactive modes"""
+ configure_power_setting(device, "default", "dw_24ghz",
+ aconsts.POWER_DW_24_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_5ghz",
+ aconsts.POWER_DW_5_INTERACTIVE)
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
+
+ configure_power_setting(device, "inactive", "dw_24ghz",
+ aconsts.POWER_DW_24_INTERACTIVE)
+ configure_power_setting(device, "inactive", "dw_5ghz",
+ aconsts.POWER_DW_5_INTERACTIVE)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
+
+
+def config_settings_low_power(device):
+ """Configure device's power settings values to low power mode - whether
+ device is in interactive or non-interactive modes"""
+ configure_power_setting(device, "default", "dw_24ghz",
+ aconsts.POWER_DW_24_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_5ghz",
+ aconsts.POWER_DW_5_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
+
+ configure_power_setting(device, "inactive", "dw_24ghz",
+ aconsts.POWER_DW_24_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "dw_5ghz",
+ aconsts.POWER_DW_5_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
+
+
+def config_power_settings(device,
+ dw_24ghz,
+ dw_5ghz,
+ disc_beacon_interval=None,
+ num_ss_in_disc=None,
+ enable_dw_early_term=None):
+ """Configure device's discovery window (DW) values to the specified values -
+ whether the device is in interactive or non-interactive mode.
+
+ Args:
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ disc_beacon_interval: The discovery beacon interval (in ms). If None then
+ not set.
+ num_ss_in_disc: Number of spatial streams to use for discovery. If None then
+ not set.
+ enable_dw_early_term: If True then enable early termination of the DW. If
+ None then not set.
+ """
+ configure_power_setting(device, "default", "dw_24ghz", dw_24ghz)
+ configure_power_setting(device, "default", "dw_5ghz", dw_5ghz)
+ configure_power_setting(device, "inactive", "dw_24ghz", dw_24ghz)
+ configure_power_setting(device, "inactive", "dw_5ghz", dw_5ghz)
+
+ if disc_beacon_interval is not None:
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ disc_beacon_interval)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ disc_beacon_interval)
+
+ if num_ss_in_disc is not None:
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ num_ss_in_disc)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ num_ss_in_disc)
+
+ if enable_dw_early_term is not None:
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ enable_dw_early_term)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ enable_dw_early_term)
+
+
+def create_discovery_config(service_name,
+ d_type,
+ ssi=None,
+ match_filter=None,
+ match_filter_list=None,
+ ttl=0,
+ term_cb_enable=True):
+ """Create a publish discovery configuration based on input parameters.
+
+ Args:
+ service_name: Service name - required
+ d_type: Discovery type (publish or subscribe constants)
+ ssi: Supplemental information - defaults to None
+ match_filter, match_filter_list: The match_filter, only one mechanism can
+ be used to specify. Defaults to None.
+ ttl: Time-to-live - defaults to 0 (i.e. non-self terminating)
+ term_cb_enable: True (default) to enable callback on termination, False
+ means that no callback is called when session terminates.
+ Returns:
+ publish discovery configuration object.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = d_type
+ if ssi is not None:
+ config[aconsts.DISCOVERY_KEY_SSI] = ssi
+ if match_filter is not None:
+ config[aconsts.DISCOVERY_KEY_MATCH_FILTER] = match_filter
+ if match_filter_list is not None:
+ config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = match_filter_list
+ config[aconsts.DISCOVERY_KEY_TTL] = ttl
+ config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
+ return config
+
+
+def add_ranging_to_pub(p_config, enable_ranging):
+ """Add ranging enabled configuration to a publish configuration (only relevant
+ for publish configuration).
+
+ Args:
+ p_config: The Publish discovery configuration.
+ enable_ranging: True to enable ranging, False to disable.
+ Returns:
+ The modified publish configuration.
+ """
+ p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging
+ return p_config
+
+
+def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm):
+ """Add ranging distance configuration to a subscribe configuration (only
+ relevant to a subscribe configuration).
+
+ Args:
+ s_config: The Subscribe discovery configuration.
+ min_distance_mm, max_distance_mm: The min and max distance specification.
+ Used if not None.
+ Returns:
+ The modified subscribe configuration.
+ """
+ if min_distance_mm is not None:
+ s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm
+ if max_distance_mm is not None:
+ s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm
+ return s_config
+
+
+def attach_with_identity(dut):
+ """Start an Aware session (attach) and wait for confirmation and identity
+ information (mac address).
+
+ Args:
+ dut: Device under test
+ Returns:
+ id: Aware session ID.
+ mac: Discovery MAC address of this device.
+ """
+ id = dut.droid.wifiAwareAttach(True)
+ wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ event = wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ mac = event["data"]["mac"]
+
+ return id, mac
+
+
+def create_discovery_pair(p_dut,
+ s_dut,
+ p_config,
+ s_config,
+ device_startup_offset,
+ msg_id=None):
+ """Creates a discovery session (publish and subscribe), and waits for
+ service discovery - at that point the sessions are connected and ready for
+ further messaging of data-path setup.
+
+ Args:
+ p_dut: Device to use as publisher.
+ s_dut: Device to use as subscriber.
+ p_config: Publish configuration.
+ s_config: Subscribe configuration.
+ device_startup_offset: Number of seconds to offset the enabling of NAN on
+ the two devices.
+ msg_id: Controls whether a message is sent from Subscriber to Publisher
+ (so that publisher has the sub's peer ID). If None then not sent,
+ otherwise should be an int for the message id.
+ Returns: variable size list of:
+ p_id: Publisher attach session id
+ s_id: Subscriber attach session id
+ p_disc_id: Publisher discovery session id
+ s_disc_id: Subscriber discovery session id
+ peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
+ peer_id_on_pub: Peer ID of the Subscriber as seen on the Publisher. Only
+ included if |msg_id| is not None.
+ """
+ p_dut.pretty_name = 'Publisher'
+ s_dut.pretty_name = 'Subscriber'
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach()
+ wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach()
+ wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+ wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+ wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait for service discovery
+ discovery_event = wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ peer_id_on_sub = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Optionally send a message from Subscriber to Publisher
+ if msg_id is not None:
+ ping_msg = 'PING'
+
+ # Subscriber: send message to peer (Publisher)
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id,
+ ping_msg, aconsts.MAX_TX_RETRIES)
+ sub_tx_msg_event = wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ asserts.assert_equal(
+ msg_id,
+ sub_tx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_ID],
+ 'Subscriber -> Publisher message ID corrupted')
+
+ # Publisher: wait for received message
+ pub_rx_msg_event = wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ peer_id_on_pub = pub_rx_msg_event['data'][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+ asserts.assert_equal(
+ ping_msg,
+ pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
+ 'Subscriber -> Publisher message corrupted')
+ return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub, peer_id_on_pub
+
+ return p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub
+
+
+def create_ib_ndp(p_dut, s_dut, p_config, s_config, device_startup_offset):
+ """Create an NDP (using in-band discovery)
+
+ Args:
+ p_dut: Device to use as publisher.
+ s_dut: Device to use as subscriber.
+ p_config: Publish configuration.
+ s_config: Subscribe configuration.
+ device_startup_offset: Number of seconds to offset the enabling of NAN on
+ the two devices.
+ """
+ (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = create_discovery_pair(
+ p_dut, s_dut, p_config, s_config, device_startup_offset, msg_id=9999)
+
+ # Publisher: request network
+ p_req_key = request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, peer_id_on_pub,
+ None))
+
+ # Subscriber: request network
+ s_req_key = request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub,
+ None))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event_nc = wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_nc = wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc["data"],
+ "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc["data"],
+ "Network specifier leak!")
+
+ # note that Pub <-> Sub since IPv6 are of peer's!
+ p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ p_net_event_lp = wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_lp = wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ p_aware_if = p_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ return p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6, s_ipv6
+
+
+def create_oob_ndp_on_sessions(init_dut, resp_dut, init_id, init_mac, resp_id,
+ resp_mac):
+ """Create an NDP on top of existing Aware sessions (using OOB discovery)
+
+ Args:
+ init_dut: Initiator device
+ resp_dut: Responder device
+ init_id: Initiator attach session id
+ init_mac: Initiator discovery MAC address
+ resp_id: Responder attach session id
+ resp_mac: Responder discovery MAC address
+ Returns:
+ init_req_key: Initiator network request
+ resp_req_key: Responder network request
+ init_aware_if: Initiator Aware data interface
+ resp_aware_if: Responder Aware data interface
+ init_ipv6: Initiator IPv6 address
+ resp_ipv6: Responder IPv6 address
+ """
+ # Responder: request network
+ resp_req_key = request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
+
+ # Initiator: request network
+ init_req_key = request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+
+ # Initiator & Responder: wait for network formation
+ init_net_event_nc = wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_nc = wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc["data"],
+ "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc["data"],
+ "Network specifier leak!")
+
+ # note that Init <-> Resp since IPv6 are of peer's!
+ resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ init_net_event_lp = wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_lp = wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ init_aware_if = init_net_event_lp['data'][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ resp_aware_if = resp_net_event_lp['data'][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6)
+
+
+def create_oob_ndp(init_dut, resp_dut):
+ """Create an NDP (using OOB discovery)
+
+ Args:
+ init_dut: Initiator device
+ resp_dut: Responder device
+ """
+ init_dut.pretty_name = 'Initiator'
+ resp_dut.pretty_name = 'Responder'
+
+ # Initiator+Responder: attach and wait for confirmation & identity
+ init_id = init_dut.droid.wifiAwareAttach(True)
+ wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ init_ident_event = wait_for_event(init_dut,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ init_mac = init_ident_event['data']['mac']
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = wait_for_event(resp_dut,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(WAIT_FOR_CLUSTER)
+
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = create_oob_ndp_on_sessions(
+ init_dut, resp_dut, init_id, init_mac, resp_id, resp_mac)
+
+ return (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py b/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py
new file mode 100644
index 0000000..4274603
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py
@@ -0,0 +1,278 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import contextlib
+import io
+import serial
+import time
+from acts import logger
+from acts import utils
+
+SHORT_SLEEP = 1
+CHAMBER_SLEEP = 30
+
+
+def create(configs):
+ """Factory method for OTA chambers.
+
+ Args:
+ configs: list of dicts with chamber settings. settings must contain the
+ following: type (string denoting type of chamber)
+ """
+ objs = []
+ for config in configs:
+ try:
+ chamber_class = globals()[config['model']]
+ except KeyError:
+ raise KeyError('Invalid chamber configuration.')
+ objs.append(chamber_class(config))
+ return objs
+
+
+def detroy(objs):
+ return
+
+
+class OtaChamber(object):
+ """Base class implementation for OTA chamber.
+
+ Base class provides functions whose implementation is shared by all
+ chambers.
+ """
+ def reset_chamber(self):
+ """Resets the chamber to its zero/home state."""
+ raise NotImplementedError
+
+ def set_orientation(self, orientation):
+ """Set orientation for turn table in OTA chamber.
+
+ Args:
+ angle: desired turn table orientation in degrees
+ """
+ raise NotImplementedError
+
+ def set_stirrer_pos(self, stirrer_id, position):
+ """Starts turntables and stirrers in OTA chamber."""
+ raise NotImplementedError
+
+ def start_continuous_stirrers(self):
+ """Starts turntables and stirrers in OTA chamber."""
+ raise NotImplementedError
+
+ def stop_continuous_stirrers(self):
+ """Stops turntables and stirrers in OTA chamber."""
+ raise NotImplementedError
+
+ def step_stirrers(self, steps):
+ """Move stepped stirrers in OTA chamber to next step."""
+ raise NotImplementedError
+
+
+class MockChamber(OtaChamber):
+ """Class that implements mock chamber for test development and debug."""
+ def __init__(self, config):
+ self.config = config.copy()
+ self.device_id = self.config['device_id']
+ self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
+ self.device_id))
+ self.current_mode = None
+
+ def set_orientation(self, orientation):
+ self.log.info('Setting orientation to {} degrees.'.format(orientation))
+
+ def reset_chamber(self):
+ self.log.info('Resetting chamber to home state')
+
+ def set_stirrer_pos(self, stirrer_id, position):
+ """Starts turntables and stirrers in OTA chamber."""
+ self.log.info('Setting stirrer {} to {}.'.format(stirrer_id, position))
+
+ def start_continuous_stirrers(self):
+ """Starts turntables and stirrers in OTA chamber."""
+ self.log.info('Starting continuous stirrer motion')
+
+ def stop_continuous_stirrers(self):
+ """Stops turntables and stirrers in OTA chamber."""
+ self.log.info('Stopping continuous stirrer motion')
+
+ def configure_stepped_stirrers(self, steps):
+ """Programs parameters for stepped stirrers in OTA chamber."""
+ self.log.info('Configuring stepped stirrers')
+
+ def step_stirrers(self, steps):
+ """Move stepped stirrers in OTA chamber to next step."""
+ self.log.info('Moving stirrers to the next step')
+
+
+class OctoboxChamber(OtaChamber):
+ """Class that implements Octobox chamber."""
+ def __init__(self, config):
+ self.config = config.copy()
+ self.device_id = self.config['device_id']
+ self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
+ self.device_id))
+ self.TURNTABLE_FILE_PATH = '/usr/local/bin/fnPerformaxCmd'
+ utils.exe_cmd('sudo {} -d {} -i 0'.format(self.TURNTABLE_FILE_PATH,
+ self.device_id))
+ self.current_mode = None
+
+ def set_orientation(self, orientation):
+ self.log.info('Setting orientation to {} degrees.'.format(orientation))
+ utils.exe_cmd('sudo {} -d {} -p {}'.format(self.TURNTABLE_FILE_PATH,
+ self.device_id,
+ orientation))
+
+ def reset_chamber(self):
+ self.log.info('Resetting chamber to home state')
+ self.set_orientation(0)
+
+
+class ChamberAutoConnect(object):
+ def __init__(self, chamber, chamber_config):
+ self._chamber = chamber
+ self._config = chamber_config
+
+ def __getattr__(self, item):
+ def chamber_call(*args, **kwargs):
+ self._chamber.connect(self._config['ip_address'],
+ self._config['username'],
+ self._config['password'])
+ return getattr(self._chamber, item)(*args, **kwargs)
+
+ return chamber_call
+
+
+class BluetestChamber(OtaChamber):
+ """Class that implements Octobox chamber."""
+ def __init__(self, config):
+ import flow
+ self.config = config.copy()
+ self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
+ self.config['ip_address']))
+ self.chamber = ChamberAutoConnect(flow.Flow(), self.config)
+ self.stirrer_ids = [0, 1, 2]
+ self.current_mode = None
+
+ # Capture print output decorator
+ @staticmethod
+ def _capture_output(func, *args, **kwargs):
+ """Creates a decorator to capture stdout from bluetest module"""
+ f = io.StringIO()
+ with contextlib.redirect_stdout(f):
+ func(*args, **kwargs)
+ output = f.getvalue()
+ return output
+
+ def _connect(self):
+ self.chamber.connect(self.config['ip_address'],
+ self.config['username'], self.config['password'])
+
+ def _init_manual_mode(self):
+ self.current_mode = 'manual'
+ for stirrer_id in self.stirrer_ids:
+ out = self._capture_output(
+ self.chamber.chamber_stirring_manual_init, stirrer_id)
+ if "failed" in out:
+ self.log.warning("Initialization error: {}".format(out))
+ time.sleep(CHAMBER_SLEEP)
+
+ def _init_continuous_mode(self):
+ self.current_mode = 'continuous'
+ self.chamber.chamber_stirring_continuous_init()
+
+ def _init_stepped_mode(self, steps):
+ self.current_mode = 'stepped'
+ self.current_stepped_pos = 0
+ self.chamber.chamber_stirring_stepped_init(steps, False)
+
+ def set_stirrer_pos(self, stirrer_id, position):
+ if self.current_mode != 'manual':
+ self._init_manual_mode()
+ self.log.info('Setting stirrer {} to {}.'.format(stirrer_id, position))
+ out = self._capture_output(
+ self.chamber.chamber_stirring_manual_set_pos, stirrer_id, position)
+ if "failed" in out:
+ self.log.warning("Bluetest error: {}".format(out))
+ self.log.warning("Set position failed. Retrying.")
+ self.current_mode = None
+ self.set_stirrer_pos(stirrer_id, position)
+ else:
+ self._capture_output(self.chamber.chamber_stirring_manual_wait,
+ CHAMBER_SLEEP)
+ self.log.warning('Stirrer {} at {}.'.format(stirrer_id, position))
+
+ def set_orientation(self, orientation):
+ self.set_stirrer_pos(2, orientation * 100 / 360)
+
+ def start_continuous_stirrers(self):
+ if self.current_mode != 'continuous':
+ self._init_continuous_mode()
+ self.chamber.chamber_stirring_continuous_start()
+
+ def stop_continuous_stirrers(self):
+ self.chamber.chamber_stirring_continuous_stop()
+
+ def step_stirrers(self, steps):
+ if self.current_mode != 'stepped':
+ self._init_stepped_mode(steps)
+ if self.current_stepped_pos == 0:
+ self.current_stepped_pos += 1
+ return
+ self.current_stepped_pos += 1
+ self.chamber.chamber_stirring_stepped_next_pos()
+
+ def reset_chamber(self):
+ if self.current_mode == 'continuous':
+ self._init_continuous_mode()
+ time.sleep(SHORT_SLEEP)
+ self._init_continuous_mode()
+ else:
+ self._init_manual_mode()
+
+
+class EInstrumentChamber(OtaChamber):
+ """Class that implements Einstrument Chamber."""
+ def __init__(self, config):
+ self.config = config.copy()
+ self.device_id = self.config['device_id']
+ self.log = logger.create_tagged_trace_logger('EInstrumentChamber|{}'.format(
+ self.device_id))
+ self.current_mode = None
+ self.ser = self._get_serial(config['port'])
+
+ def _get_serial(self, port, baud=9600):
+ """Read com port.
+
+ Args:
+ port: turn table com port
+ baud: baud rate
+ """
+ ser = serial.Serial(port, baud)
+ return ser
+
+ def set_orientation(self, orientation):
+ if int(orientation) > 360:
+ orientation = int(orientation) % 360
+ elif int(orientation) < 0:
+ orientation = 0
+ self.log.info('Setting orientation to {} degrees.'.format(orientation))
+ orientation = str('DG') + str(orientation) + str(';')
+ self.ser.write(orientation.encode())
+ return orientation
+
+ def reset_chamber(self):
+ self.log.info('Resetting turn table to zero degree')
+ self.set_orientation(0)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py b/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
new file mode 100644
index 0000000..73fb033
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
@@ -0,0 +1,572 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import csv
+import os
+import posixpath
+import time
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import context
+from acts import logger
+from acts import utils
+from acts.controllers.utils_lib import ssh
+
+WifiEnums = wutils.WifiEnums
+SNIFFER_TIMEOUT = 6
+
+
+def create(configs):
+ """Factory method for sniffer.
+ Args:
+ configs: list of dicts with sniffer settings.
+ Settings must contain the following : ssh_settings, type, OS, interface.
+
+ Returns:
+ objs: list of sniffer class objects.
+ """
+ objs = []
+ for config in configs:
+ try:
+ if config['type'] == 'tshark':
+ if config['os'] == 'unix':
+ objs.append(TsharkSnifferOnUnix(config))
+ elif config['os'] == 'linux':
+ objs.append(TsharkSnifferOnLinux(config))
+ else:
+ raise RuntimeError('Wrong sniffer config')
+
+ elif config['type'] == 'mock':
+ objs.append(MockSniffer(config))
+ except KeyError:
+ raise KeyError('Invalid sniffer configurations')
+ return objs
+
+
+def destroy(objs):
+ return
+
+
+class OtaSnifferBase(object):
+ """Base class defining common sniffers functions."""
+
+ _log_file_counter = 0
+
+ @property
+ def started(self):
+ raise NotImplementedError('started must be specified.')
+
+ def start_capture(self, network, duration=30):
+ """Starts the sniffer Capture.
+
+ Args:
+ network: dict containing network information such as SSID, etc.
+ duration: duration of sniffer capture in seconds.
+ """
+ raise NotImplementedError('start_capture must be specified.')
+
+ def stop_capture(self, tag=''):
+ """Stops the sniffer Capture.
+
+ Args:
+ tag: string to tag sniffer capture file name with.
+ """
+ raise NotImplementedError('stop_capture must be specified.')
+
+ def _get_remote_dump_path(self):
+ """Returns name of the sniffer dump file."""
+ remote_file_name = 'sniffer_dump.{}'.format(
+ self.sniffer_output_file_type)
+ remote_dump_path = posixpath.join(posixpath.sep, 'tmp', remote_file_name)
+ return remote_dump_path
+
+ def _get_full_file_path(self, tag=None):
+ """Returns the full file path for the sniffer capture dump file.
+
+ Returns the full file path (on test machine) for the sniffer capture
+ dump file.
+
+ Args:
+ tag: The tag appended to the sniffer capture dump file .
+ """
+ tags = [tag, 'count', OtaSnifferBase._log_file_counter]
+ out_file_name = 'Sniffer_Capture_%s.%s' % ('_'.join([
+ str(x) for x in tags if x != '' and x is not None
+ ]), self.sniffer_output_file_type)
+ OtaSnifferBase._log_file_counter += 1
+
+ file_path = os.path.join(self.log_path, out_file_name)
+ return file_path
+
+ @property
+ def log_path(self):
+ current_context = context.get_current_context()
+ full_out_dir = os.path.join(current_context.get_full_output_path(),
+ 'sniffer_captures')
+
+ # Ensure the directory exists.
+ os.makedirs(full_out_dir, exist_ok=True)
+
+ return full_out_dir
+
+
+class MockSniffer(OtaSnifferBase):
+ """Class that implements mock sniffer for test development and debug."""
+ def __init__(self, config):
+ self.log = logger.create_tagged_trace_logger('Mock Sniffer')
+
+ def start_capture(self, network, duration=30):
+ """Starts sniffer capture on the specified machine.
+
+ Args:
+ network: dict of network credentials.
+ duration: duration of the sniff.
+ """
+ self.log.info('Starting sniffer.')
+
+ def stop_capture(self):
+ """Stops the sniffer.
+
+ Returns:
+ log_file: name of processed sniffer.
+ """
+
+ self.log.info('Stopping sniffer.')
+ log_file = self._get_full_file_path()
+ with open(log_file, 'w') as file:
+ file.write('this is a sniffer dump.')
+ return log_file
+
+
+class TsharkSnifferBase(OtaSnifferBase):
+ """Class that implements Tshark based sniffer controller. """
+
+ TYPE_SUBTYPE_DICT = {
+ '0': 'Association Requests',
+ '1': 'Association Responses',
+ '2': 'Reassociation Requests',
+ '3': 'Resssociation Responses',
+ '4': 'Probe Requests',
+ '5': 'Probe Responses',
+ '8': 'Beacon',
+ '9': 'ATIM',
+ '10': 'Disassociations',
+ '11': 'Authentications',
+ '12': 'Deauthentications',
+ '13': 'Actions',
+ '24': 'Block ACK Requests',
+ '25': 'Block ACKs',
+ '26': 'PS-Polls',
+ '27': 'RTS',
+ '28': 'CTS',
+ '29': 'ACK',
+ '30': 'CF-Ends',
+ '31': 'CF-Ends/CF-Acks',
+ '32': 'Data',
+ '33': 'Data+CF-Ack',
+ '34': 'Data+CF-Poll',
+ '35': 'Data+CF-Ack+CF-Poll',
+ '36': 'Null',
+ '37': 'CF-Ack',
+ '38': 'CF-Poll',
+ '39': 'CF-Ack+CF-Poll',
+ '40': 'QoS Data',
+ '41': 'QoS Data+CF-Ack',
+ '42': 'QoS Data+CF-Poll',
+ '43': 'QoS Data+CF-Ack+CF-Poll',
+ '44': 'QoS Null',
+ '46': 'QoS CF-Poll (Null)',
+ '47': 'QoS CF-Ack+CF-Poll (Null)'
+ }
+
+ TSHARK_COLUMNS = [
+ 'frame_number', 'frame_time_relative', 'mactime', 'frame_len', 'rssi',
+ 'channel', 'ta', 'ra', 'bssid', 'type', 'subtype', 'duration', 'seq',
+ 'retry', 'pwrmgmt', 'moredata', 'ds', 'phy', 'radio_datarate',
+ 'vht_datarate', 'radiotap_mcs_index', 'vht_mcs', 'wlan_data_rate',
+ '11n_mcs_index', '11ac_mcs', '11n_bw', '11ac_bw', 'vht_nss', 'mcs_gi',
+ 'vht_gi', 'vht_coding', 'ba_bm', 'fc_status', 'bf_report'
+ ]
+
+ TSHARK_OUTPUT_COLUMNS = [
+ 'frame_number', 'frame_time_relative', 'mactime', 'ta', 'ra', 'bssid',
+ 'rssi', 'channel', 'frame_len', 'Info', 'radio_datarate',
+ 'radiotap_mcs_index', 'pwrmgmt', 'phy', 'vht_nss', 'vht_mcs',
+ 'vht_datarate', '11ac_mcs', '11ac_bw', 'vht_gi', 'vht_coding',
+ 'wlan_data_rate', '11n_mcs_index', '11n_bw', 'mcs_gi', 'type',
+ 'subtype', 'duration', 'seq', 'retry', 'moredata', 'ds', 'ba_bm',
+ 'fc_status', 'bf_report'
+ ]
+
+ TSHARK_FIELDS_LIST = [
+ 'frame.number', 'frame.time_relative', 'radiotap.mactime', 'frame.len',
+ 'radiotap.dbm_antsignal', 'wlan_radio.channel', 'wlan.ta', 'wlan.ra',
+ 'wlan.bssid', 'wlan.fc.type', 'wlan.fc.type_subtype', 'wlan.duration',
+ 'wlan.seq', 'wlan.fc.retry', 'wlan.fc.pwrmgt', 'wlan.fc.moredata',
+ 'wlan.fc.ds', 'wlan_radio.phy', 'radiotap.datarate',
+ 'radiotap.vht.datarate.0', 'radiotap.mcs.index', 'radiotap.vht.mcs.0',
+ 'wlan_radio.data_rate', 'wlan_radio.11n.mcs_index',
+ 'wlan_radio.11ac.mcs', 'wlan_radio.11n.bandwidth',
+ 'wlan_radio.11ac.bandwidth', 'radiotap.vht.nss.0', 'radiotap.mcs.gi',
+ 'radiotap.vht.gi', 'radiotap.vht.coding.0', 'wlan.ba.bm',
+ 'wlan.fcs.status', 'wlan.vht.compressed_beamforming_report.snr'
+ ]
+
+ def __init__(self, config):
+ self.sniffer_proc_pid = None
+ self.log = logger.create_tagged_trace_logger('Tshark Sniffer')
+ self.ssh_config = config['ssh_config']
+ self.sniffer_os = config['os']
+ self.run_as_sudo = config.get('run_as_sudo', False)
+ self.sniffer_output_file_type = config['output_file_type']
+ self.sniffer_snap_length = config['snap_length']
+ self.sniffer_interface = config['interface']
+
+ #Logging into sniffer
+ self.log.info('Logging into sniffer.')
+ self._sniffer_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.ssh_config))
+ # Get tshark params
+ self.tshark_fields = self._generate_tshark_fields(
+ self.TSHARK_FIELDS_LIST)
+ self.tshark_path = self._sniffer_server.run('which tshark').stdout
+
+ @property
+ def _started(self):
+ return self.sniffer_proc_pid is not None
+
+ def _scan_for_networks(self):
+ """Scans for wireless networks on the sniffer."""
+ raise NotImplementedError
+
+ def _get_tshark_command(self, duration):
+ """Frames the appropriate tshark command.
+
+ Args:
+ duration: duration to sniff for.
+
+ Returns:
+ tshark_command : appropriate tshark command.
+ """
+ tshark_command = '{} -l -i {} -I -t u -a duration:{}'.format(
+ self.tshark_path, self.sniffer_interface, int(duration))
+ if self.run_as_sudo:
+ tshark_command = 'sudo {}'.format(tshark_command)
+
+ return tshark_command
+
+ def _get_sniffer_command(self, tshark_command):
+ """
+ Frames the appropriate sniffer command.
+
+ Args:
+ tshark_command: framed tshark command
+
+ Returns:
+ sniffer_command: appropriate sniffer command
+ """
+ if self.sniffer_output_file_type in ['pcap', 'pcapng']:
+ sniffer_command = ' {tshark} -s {snaplength} -w {log_file} '.format(
+ tshark=tshark_command,
+ snaplength=self.sniffer_snap_length,
+ log_file=self._get_remote_dump_path())
+
+ elif self.sniffer_output_file_type == 'csv':
+ sniffer_command = '{tshark} {fields} > {log_file}'.format(
+ tshark=tshark_command,
+ fields=self.tshark_fields,
+ log_file=self._get_remote_dump_path())
+
+ else:
+ raise KeyError('Sniffer output file type not configured correctly')
+
+ return sniffer_command
+
+ def _generate_tshark_fields(self, fields):
+ """Generates tshark fields to be appended to the tshark command.
+
+ Args:
+ fields: list of tshark fields to be appended to the tshark command.
+
+ Returns:
+ tshark_fields: string of tshark fields to be appended
+ to the tshark command.
+ """
+ tshark_fields = "-T fields -y IEEE802_11_RADIO -E separator='^'"
+ for field in fields:
+ tshark_fields = tshark_fields + ' -e {}'.format(field)
+ return tshark_fields
+
+ def _configure_sniffer(self, network, chan, bw):
+ """ Connects to a wireless network using networksetup utility.
+
+ Args:
+ network: dictionary of network credentials; SSID and password.
+ """
+ raise NotImplementedError
+
+ def _run_tshark(self, sniffer_command):
+ """Starts the sniffer.
+
+ Args:
+ sniffer_command: sniffer command to execute.
+ """
+ self.log.info('Starting sniffer.')
+ sniffer_job = self._sniffer_server.run_async(sniffer_command)
+ self.sniffer_proc_pid = sniffer_job.stdout
+
+ def _stop_tshark(self):
+ """ Stops the sniffer."""
+ self.log.info('Stopping sniffer')
+
+ # while loop to kill the sniffer process
+ stop_time = time.time() + SNIFFER_TIMEOUT
+ while time.time() < stop_time:
+ # Wait before sending more kill signals
+ time.sleep(0.1)
+ try:
+ # Returns 1 if process was killed
+ self._sniffer_server.run(
+ 'ps aux| grep {} | grep -v grep'.format(
+ self.sniffer_proc_pid))
+ except:
+ return
+ try:
+ # Returns error if process was killed already
+ self._sniffer_server.run('sudo kill -15 {}'.format(
+ str(self.sniffer_proc_pid)))
+ except:
+ # Except is hit when tshark is already dead but we will break
+ # out of the loop when confirming process is dead using ps aux
+ pass
+ self.log.warning('Could not stop sniffer. Trying with SIGKILL.')
+ try:
+ self.log.debug('Killing sniffer with SIGKILL.')
+ self._sniffer_server.run('sudo kill -9 {}'.format(
+ str(self.sniffer_proc_pid)))
+ except:
+ self.log.debug('Sniffer process may have stopped succesfully.')
+
+ def _process_tshark_dump(self, log_file):
+ """ Process tshark dump for better readability.
+
+ Processes tshark dump for better readability and saves it to a file.
+ Adds an info column at the end of each row. Format of the info columns:
+ subtype of the frame, sequence no and retry status.
+
+ Args:
+ log_file : unprocessed sniffer output
+ Returns:
+ log_file : processed sniffer output
+ """
+ temp_dump_file = os.path.join(self.log_path, 'sniffer_temp_dump.csv')
+ utils.exe_cmd('cp {} {}'.format(log_file, temp_dump_file))
+
+ with open(temp_dump_file, 'r') as input_csv, open(log_file,
+ 'w') as output_csv:
+ reader = csv.DictReader(input_csv,
+ fieldnames=self.TSHARK_COLUMNS,
+ delimiter='^')
+ writer = csv.DictWriter(output_csv,
+ fieldnames=self.TSHARK_OUTPUT_COLUMNS,
+ delimiter='\t')
+ writer.writeheader()
+ for row in reader:
+ if row['subtype'] in self.TYPE_SUBTYPE_DICT:
+ row['Info'] = '{sub} S={seq} retry={retry_status}'.format(
+ sub=self.TYPE_SUBTYPE_DICT[row['subtype']],
+ seq=row['seq'],
+ retry_status=row['retry'])
+ else:
+ row['Info'] = '{} S={} retry={}\n'.format(
+ row['subtype'], row['seq'], row['retry'])
+ writer.writerow(row)
+
+ utils.exe_cmd('rm -f {}'.format(temp_dump_file))
+ return log_file
+
+ def start_capture(self, network, chan, bw, duration=60):
+ """Starts sniffer capture on the specified machine.
+
+ Args:
+ network: dict describing network to sniff on.
+ duration: duration of sniff.
+ """
+ # Checking for existing sniffer processes
+ if self._started:
+ self.log.info('Sniffer already running')
+ return
+
+ # Configure sniffer
+ self._configure_sniffer(network, chan, bw)
+ tshark_command = self._get_tshark_command(duration)
+ sniffer_command = self._get_sniffer_command(tshark_command)
+
+ # Starting sniffer capture by executing tshark command
+ self._run_tshark(sniffer_command)
+
+ def stop_capture(self, tag=''):
+ """Stops the sniffer.
+
+ Args:
+ tag: tag to be appended to the sniffer output file.
+ Returns:
+ log_file: path to sniffer dump.
+ """
+ # Checking if there is an ongoing sniffer capture
+ if not self._started:
+ self.log.error('No sniffer process running')
+ return
+ # Killing sniffer process
+ self._stop_tshark()
+
+ # Processing writing capture output to file
+ log_file = self._get_full_file_path(tag)
+ self._sniffer_server.run('sudo chmod 777 {}'.format(
+ self._get_remote_dump_path()))
+ self._sniffer_server.pull_file(log_file, self._get_remote_dump_path())
+
+ if self.sniffer_output_file_type == 'csv':
+ log_file = self._process_tshark_dump(log_file)
+
+ self.sniffer_proc_pid = None
+ return log_file
+
+
+class TsharkSnifferOnUnix(TsharkSnifferBase):
+ """Class that implements Tshark based sniffer controller on Unix systems."""
+ def _scan_for_networks(self):
+ """Scans the wireless networks on the sniffer.
+
+ Returns:
+ scan_results : output of the scan command.
+ """
+ scan_command = '/usr/local/bin/airport -s'
+ scan_result = self._sniffer_server.run(scan_command).stdout
+
+ return scan_result
+
+ def _configure_sniffer(self, network, chan, bw):
+ """Connects to a wireless network using networksetup utility.
+
+ Args:
+ network: dictionary of network credentials; SSID and password.
+ """
+
+ self.log.debug('Connecting to network {}'.format(network['SSID']))
+
+ if 'password' not in network:
+ network['password'] = ''
+
+ connect_command = 'networksetup -setairportnetwork en0 {} {}'.format(
+ network['SSID'], network['password'])
+ self._sniffer_server.run(connect_command)
+
+
+class TsharkSnifferOnLinux(TsharkSnifferBase):
+ """Class that implements Tshark based sniffer controller on Linux."""
+ def __init__(self, config):
+ super().__init__(config)
+ self._init_sniffer()
+ self.channel = None
+ self.bandwidth = None
+
+ def _init_sniffer(self):
+ """Function to configure interface for the first time"""
+ self._sniffer_server.run('sudo modprobe -r iwlwifi')
+ self._sniffer_server.run('sudo dmesg -C')
+ self._sniffer_server.run('cat /dev/null | sudo tee /var/log/syslog')
+ self._sniffer_server.run('sudo modprobe iwlwifi debug=0x1')
+ # Wait for wifi config changes before trying to further configuration
+ # e.g. setting monitor mode (which will fail if above is not complete)
+ time.sleep(1)
+
+ def set_monitor_mode(self, chan, bw):
+ """Function to configure interface to monitor mode
+
+ Brings up the sniffer wireless interface in monitor mode and
+ tunes it to the appropriate channel and bandwidth
+
+ Args:
+ chan: primary channel (int) to tune the sniffer to
+ bw: bandwidth (int) to tune the sniffer to
+ """
+ if chan == self.channel and bw == self.bandwidth:
+ return
+
+ self.channel = chan
+ self.bandwidth = bw
+
+ channel_map = {
+ 80: {
+ tuple(range(36, 50, 2)): 42,
+ tuple(range(52, 66, 2)): 58,
+ tuple(range(100, 114, 2)): 106,
+ tuple(range(116, 130, 2)): 122,
+ tuple(range(132, 146, 2)): 138,
+ tuple(range(149, 163, 2)): 155
+ },
+ 40: {
+ (36, 38, 40): 38,
+ (44, 46, 48): 46,
+ (52, 54, 56): 54,
+ (60, 62, 64): 62,
+ (100, 102, 104): 102,
+ (108, 110, 112): 108,
+ (116, 118, 120): 118,
+ (124, 126, 128): 126,
+ (132, 134, 136): 134,
+ (140, 142, 144): 142,
+ (149, 151, 153): 151,
+ (157, 159, 161): 159
+ }
+ }
+
+ if chan <= 13:
+ primary_freq = WifiEnums.channel_2G_to_freq[chan]
+ else:
+ primary_freq = WifiEnums.channel_5G_to_freq[chan]
+
+ self._sniffer_server.run('sudo ifconfig {} down'.format(
+ self.sniffer_interface))
+ self._sniffer_server.run('sudo iwconfig {} mode monitor'.format(
+ self.sniffer_interface))
+ self._sniffer_server.run('sudo ifconfig {} up'.format(
+ self.sniffer_interface))
+
+ if bw in channel_map:
+ for tuple_chan in channel_map[bw]:
+ if chan in tuple_chan:
+ center_freq = WifiEnums.channel_5G_to_freq[channel_map[bw]
+ [tuple_chan]]
+ self._sniffer_server.run(
+ 'sudo iw dev {} set freq {} {} {}'.format(
+ self.sniffer_interface, primary_freq, bw,
+ center_freq))
+
+ else:
+ self._sniffer_server.run('sudo iw dev {} set freq {}'.format(
+ self.sniffer_interface, primary_freq))
+
+ def _configure_sniffer(self, network, chan, bw):
+ """ Connects to a wireless network using networksetup utility.
+
+ Args:
+ network: dictionary of network credentials; SSID and password.
+ """
+
+ self.log.debug('Connecting to network {}'.format(network['SSID']))
+ self.set_monitor_mode(chan, bw)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py
new file mode 100644
index 0000000..f236470
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import acts.utils
+import re
+import time
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+
+WAIT_TIME = 60
+
+
+class WifiP2pBaseTest(BaseTestClass):
+ def __init__(self, controllers):
+ if not hasattr(self, 'android_devices'):
+ super(WifiP2pBaseTest, self).__init__(controllers)
+
+ def setup_class(self):
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ required_params = ()
+ optional_params = ("skip_read_factory_mac", )
+ self.unpack_userparams(required_params,
+ optional_params,
+ skip_read_factory_mac=0)
+
+ self.dut1 = self.android_devices[0]
+ self.dut2 = self.android_devices[1]
+ if self.skip_read_factory_mac:
+ self.dut1_mac = None
+ self.dut2_mac = None
+ else:
+ self.dut1_mac = self.get_p2p_mac_address(self.dut1)
+ self.dut2_mac = self.get_p2p_mac_address(self.dut2)
+
+ #init location before init p2p
+ acts.utils.set_location_service(self.dut1, True)
+ acts.utils.set_location_service(self.dut2, True)
+
+ wutils.wifi_test_device_init(self.dut1)
+ utils.sync_device_time(self.dut1)
+ self.dut1.droid.wifiP2pInitialize()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ asserts.assert_true(self.dut1.droid.wifiP2pIsEnabled(),
+ "DUT1's p2p should be initialized but it didn't")
+ self.dut1.name = "Android_" + self.dut1.serial
+ self.dut1.droid.wifiP2pSetDeviceName(self.dut1.name)
+ wutils.wifi_test_device_init(self.dut2)
+ utils.sync_device_time(self.dut2)
+ self.dut2.droid.wifiP2pInitialize()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ asserts.assert_true(self.dut2.droid.wifiP2pIsEnabled(),
+ "DUT2's p2p should be initialized but it didn't")
+ self.dut2.name = "Android_" + self.dut2.serial
+ self.dut2.droid.wifiP2pSetDeviceName(self.dut2.name)
+
+ if len(self.android_devices) > 2:
+ self.dut3 = self.android_devices[2]
+ acts.utils.set_location_service(self.dut3, True)
+ wutils.wifi_test_device_init(self.dut3)
+ utils.sync_device_time(self.dut3)
+ self.dut3.droid.wifiP2pInitialize()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ asserts.assert_true(
+ self.dut3.droid.wifiP2pIsEnabled(),
+ "DUT3's p2p should be initialized but it didn't")
+ self.dut3.name = "Android_" + self.dut3.serial
+ self.dut3.droid.wifiP2pSetDeviceName(self.dut3.name)
+
+ def teardown_class(self):
+ self.dut1.droid.wifiP2pClose()
+ self.dut2.droid.wifiP2pClose()
+ acts.utils.set_location_service(self.dut1, False)
+ acts.utils.set_location_service(self.dut2, False)
+
+ if len(self.android_devices) > 2:
+ self.dut3.droid.wifiP2pClose()
+ acts.utils.set_location_service(self.dut3, False)
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+
+ def setup_test(self):
+ for ad in self.android_devices:
+ ad.ed.clear_all_events()
+
+ def teardown_test(self):
+ for ad in self.android_devices:
+ # Clear p2p group info
+ ad.droid.wifiP2pRequestPersistentGroupInfo()
+ event = ad.ed.pop_event("WifiP2pOnPersistentGroupInfoAvailable",
+ p2pconsts.DEFAULT_TIMEOUT)
+ for network in event['data']:
+ ad.droid.wifiP2pDeletePersistentGroup(network['NetworkId'])
+ # Clear p2p local service
+ ad.droid.wifiP2pClearLocalServices()
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+ ad.cat_adb_log(test_name, begin_time)
+
+ def get_p2p_mac_address(self, dut):
+ """Gets the current MAC address being used for Wi-Fi Direct."""
+ dut.reboot()
+ time.sleep(WAIT_TIME)
+ out = dut.adb.shell("ifconfig p2p0")
+ return re.match(".* HWaddr (\S+).*", out, re.S).group(1)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_const.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_const.py
new file mode 100644
index 0000000..87f4059
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_const.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+######################################################
+# Wifi P2p framework designed value
+######################################################
+P2P_FIND_TIMEOUT = 120
+GO_IP_ADDRESS = '192.168.49.1'
+
+######################################################
+# Wifi P2p Acts flow control timer value
+######################################################
+
+DEFAULT_TIMEOUT = 30
+DEFAULT_SLEEPTIME = 5
+DEFAULT_FUNCTION_SWITCH_TIME = 10
+DEFAULT_SERVICE_WAITING_TIME = 20
+DEFAULT_GROUP_CLIENT_LOST_TIME = 60
+
+P2P_CONNECT_NEGOTIATION = 0
+P2P_CONNECT_JOIN = 1
+P2P_CONNECT_INVITATION = 2
+######################################################
+# Wifi P2p sl4a Event String
+######################################################
+CONNECTED_EVENT = "WifiP2pConnected"
+DISCONNECTED_EVENT = "WifiP2pDisconnected"
+PEER_AVAILABLE_EVENT = "WifiP2pOnPeersAvailable"
+CONNECTION_INFO_AVAILABLE_EVENT = "WifiP2pOnConnectionInfoAvailable"
+ONGOING_PEER_INFO_AVAILABLE_EVENT = "WifiP2pOnOngoingPeerAvailable"
+ONGOING_PEER_SET_SUCCESS_EVENT = "WifiP2psetP2pPeerConfigureOnSuccess"
+CONNECT_SUCCESS_EVENT = "WifiP2pConnectOnSuccess"
+CREATE_GROUP_SUCCESS_EVENT = "WifiP2pCreateGroupOnSuccess"
+SET_CHANNEL_SUCCESS_EVENT = "WifiP2pSetChannelsOnSuccess"
+GROUP_INFO_AVAILABLE_EVENT = "WifiP2pOnGroupInfoAvailable"
+
+######################################################
+# Wifi P2p local service event
+####################################################
+
+DNSSD_EVENT = "WifiP2pOnDnsSdServiceAvailable"
+DNSSD_TXRECORD_EVENT = "WifiP2pOnDnsSdTxtRecordAvailable"
+UPNP_EVENT = "WifiP2pOnUpnpServiceAvailable"
+
+DNSSD_EVENT_INSTANCENAME_KEY = "InstanceName"
+DNSSD_EVENT_REGISTRATIONTYPE_KEY = "RegistrationType"
+DNSSD_TXRECORD_EVENT_FULLDOMAINNAME_KEY = "FullDomainName"
+DNSSD_TXRECORD_EVENT_TXRECORDMAP_KEY = "TxtRecordMap"
+UPNP_EVENT_SERVICELIST_KEY = "ServiceList"
+
+######################################################
+# Wifi P2p local service type
+####################################################
+P2P_LOCAL_SERVICE_UPNP = 0
+P2P_LOCAL_SERVICE_IPP = 1
+P2P_LOCAL_SERVICE_AFP = 2
+
+######################################################
+# Wifi P2p group capability
+######################################################
+P2P_GROUP_CAPAB_GROUP_OWNER = 1
+
+
+######################################################
+# Wifi P2p UPnP MediaRenderer local service
+######################################################
+class UpnpTestData():
+ AVTransport = "urn:schemas-upnp-org:service:AVTransport:1"
+ ConnectionManager = "urn:schemas-upnp-org:service:ConnectionManager:1"
+ serviceType = "urn:schemas-upnp-org:device:MediaRenderer:1"
+ uuid = "6859dede-8574-59ab-9332-123456789011"
+ rootdevice = "upnp:rootdevice"
+
+
+######################################################
+# Wifi P2p Bonjour IPP & AFP local service
+######################################################
+class IppTestData():
+ ippInstanceName = "MyPrinter"
+ ippRegistrationType = "_ipp._tcp"
+ ippDomainName = "myprinter._ipp._tcp.local."
+ ipp_txtRecord = {"txtvers": "1", "pdl": "application/postscript"}
+
+
+class AfpTestData():
+ afpInstanceName = "Example"
+ afpRegistrationType = "_afpovertcp._tcp"
+ afpDomainName = "example._afpovertcp._tcp.local."
+ afp_txtRecord = {}
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py
new file mode 100755
index 0000000..006529a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py
@@ -0,0 +1,694 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import time
+
+from queue import Empty
+
+from acts import asserts
+from acts import utils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+import acts.utils
+
+
+def is_discovered(event, ad):
+ """Check an Android device exist in WifiP2pOnPeersAvailable event or not.
+
+ Args:
+ event: WifiP2pOnPeersAvailable which include all of p2p devices.
+ ad: The android device
+ Returns:
+ True: if an Android device exist in p2p list
+ False: if not exist
+ """
+ for device in event['data']['Peers']:
+ if device['Name'] == ad.name:
+ ad.deviceAddress = device['Address']
+ return True
+ return False
+
+
+def check_disconnect(ad, timeout=p2pconsts.DEFAULT_TIMEOUT):
+ """Check an Android device disconnect or not
+
+ Args:
+ ad: The android device
+ """
+ ad.droid.wifiP2pRequestConnectionInfo()
+ # wait disconnect event
+ ad.ed.pop_event(p2pconsts.DISCONNECTED_EVENT, timeout)
+
+
+def p2p_disconnect(ad):
+ """Invoke an Android device removeGroup to trigger p2p disconnect
+
+ Args:
+ ad: The android device
+ """
+ ad.log.debug("Disconnect")
+ ad.droid.wifiP2pRemoveGroup()
+ check_disconnect(ad)
+
+
+def p2p_connection_ping_test(ad, target_ip_address):
+ """Let an Android device to start ping target_ip_address
+
+ Args:
+ ad: The android device
+ target_ip_address: ip address which would like to ping
+ """
+ ad.log.debug("Run Ping Test, %s ping %s " % (ad.serial, target_ip_address))
+ asserts.assert_true(
+ acts.utils.adb_shell_ping(ad,
+ count=6,
+ dest_ip=target_ip_address,
+ timeout=20), "%s ping failed" % (ad.serial))
+
+
+def is_go(ad):
+ """Check an Android p2p role is Go or not
+
+ Args:
+ ad: The android device
+ Return:
+ True: An Android device is p2p go
+ False: An Android device is p2p gc
+ """
+ ad.log.debug("is go check")
+ ad.droid.wifiP2pRequestConnectionInfo()
+ ad_connect_info_event = ad.ed.pop_event(
+ p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+ if ad_connect_info_event['data']['isGroupOwner']:
+ return True
+ return False
+
+
+def p2p_go_ip(ad):
+ """Get GO IP address
+
+ Args:
+ ad: The android device
+ Return:
+ GO IP address
+ """
+ ad.log.debug("p2p go ip")
+ ad.droid.wifiP2pRequestConnectionInfo()
+ ad_connect_info_event = ad.ed.pop_event(
+ p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+ ad.log.debug("p2p go ip: %s" %
+ ad_connect_info_event['data']['groupOwnerHostAddress'])
+ return ad_connect_info_event['data']['groupOwnerHostAddress']
+
+
+def p2p_get_current_group(ad):
+ """Get current group information
+
+ Args:
+ ad: The android device
+ Return:
+ p2p group information
+ """
+ ad.log.debug("get current group")
+ ad.droid.wifiP2pRequestGroupInfo()
+ ad_group_info_event = ad.ed.pop_event(p2pconsts.GROUP_INFO_AVAILABLE_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ ad.log.debug(
+ "p2p group: SSID:%s, password:%s, owner address: %s, interface: %s" %
+ (ad_group_info_event['data']['NetworkName'],
+ ad_group_info_event['data']['Passphrase'],
+ ad_group_info_event['data']['OwnerAddress'],
+ ad_group_info_event['data']['Interface']))
+ return ad_group_info_event['data']
+
+
+#trigger p2p connect to ad2 from ad1
+def p2p_connect(ad1,
+ ad2,
+ isReconnect,
+ wpsSetup,
+ p2p_connect_type=p2pconsts.P2P_CONNECT_NEGOTIATION,
+ go_ad=None):
+ """trigger p2p connect to ad2 from ad1
+
+ Args:
+ ad1: The android device
+ ad2: The android device
+ isReconnect: boolean, if persist group is exist,
+ isReconnect is true, otherswise is false.
+ wpsSetup: which wps connection would like to use
+ p2p_connect_type: enumeration, which type this p2p connection is
+ go_ad: The group owner android device which is used for the invitation connection
+ """
+ ad1.log.info("Create p2p connection from %s to %s via wps: %s type %d" %
+ (ad1.name, ad2.name, wpsSetup, p2p_connect_type))
+ if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION:
+ if go_ad is None:
+ go_ad = ad1
+ find_p2p_device(ad1, ad2)
+ find_p2p_group_owner(ad2, go_ad)
+ elif p2p_connect_type == p2pconsts.P2P_CONNECT_JOIN:
+ find_p2p_group_owner(ad1, ad2)
+ else:
+ find_p2p_device(ad1, ad2)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+ wifi_p2p_config = {
+ WifiP2PEnums.WifiP2pConfig.DEVICEADDRESS_KEY: ad2.deviceAddress,
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: {
+ WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: wpsSetup
+ }
+ }
+ ad1.droid.wifiP2pConnect(wifi_p2p_config)
+ ad1.ed.pop_event(p2pconsts.CONNECT_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+ if not isReconnect:
+ ad1.droid.requestP2pPeerConfigure()
+ ad1_peerConfig = ad1.ed.pop_event(
+ p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ ad1.log.debug(ad1_peerConfig['data'])
+ ad2.droid.requestP2pPeerConfigure()
+ ad2_peerConfig = ad2.ed.pop_event(
+ p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ ad2.log.debug(ad2_peerConfig['data'])
+ if wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY:
+ asserts.assert_true(
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY in ad1_peerConfig['data'][
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY],
+ "Can't get pin value")
+ ad2_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad1_peerConfig['data'][
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY]
+ ad2.droid.setP2pPeerConfigure(ad2_peerConfig['data'])
+ ad2.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ ad2.droid.wifiP2pAcceptConnection()
+ elif wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD:
+ asserts.assert_true(
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY in ad2_peerConfig['data'][
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY],
+ "Can't get pin value")
+ ad1_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad2_peerConfig['data'][
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][
+ WifiP2PEnums.WpsInfo.WPS_PIN_KEY]
+ ad1.droid.setP2pPeerConfigure(ad1_peerConfig['data'])
+ ad1.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ #Need to Accept first in ad1 to avoid connect time out in ad2,
+ #the timeout just 1 sec in ad2
+ ad1.droid.wifiP2pAcceptConnection()
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+ ad2.droid.wifiP2pConfirmConnection()
+ elif wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC:
+ ad2.droid.wifiP2pAcceptConnection()
+ if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION:
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+ go_ad.droid.wifiP2pAcceptConnection()
+
+ #wait connected event
+ if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION:
+ go_ad.ed.pop_event(p2pconsts.CONNECTED_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ else:
+ ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+ ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+
+
+def p2p_connect_with_config(ad1, ad2, network_name, passphrase, band):
+ """trigger p2p connect to ad2 from ad1 with config
+
+ Args:
+ ad1: The android device
+ ad2: The android device
+ network_name: the network name of the desired group.
+ passphrase: the passphrase of the desired group.
+ band: the operating band of the desired group.
+ """
+ ad1.log.info("Create p2p connection from %s to %s" % (ad1.name, ad2.name))
+ find_p2p_device(ad1, ad2)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+ wifi_p2p_config = {
+ WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name,
+ WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase,
+ WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band,
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: {
+ WifiP2PEnums.WpsInfo.WPS_SETUP_KEY:
+ WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC
+ }
+ }
+ ad1.droid.wifiP2pConnect(wifi_p2p_config)
+ ad1.ed.pop_event(p2pconsts.CONNECT_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+
+ #wait connected event
+ ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+ ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT)
+
+
+def find_p2p_device(ad1, ad2):
+ """Check an Android device ad1 can discover an Android device ad2
+
+ Args:
+ ad1: The android device
+ ad2: The android device
+ """
+ ad1.droid.wifiP2pDiscoverPeers()
+ ad2.droid.wifiP2pDiscoverPeers()
+ p2p_find_result = False
+ while not p2p_find_result:
+ ad1_event = ad1.ed.pop_event(p2pconsts.PEER_AVAILABLE_EVENT,
+ p2pconsts.P2P_FIND_TIMEOUT)
+ ad1.log.debug(ad1_event['data'])
+ p2p_find_result = is_discovered(ad1_event, ad2)
+ asserts.assert_true(p2p_find_result,
+ "DUT didn't discovered peer:%s device" % (ad2.name))
+
+
+def find_p2p_group_owner(ad1, ad2):
+ """Check an Android device ad1 can discover an Android device ad2 which
+ is a group owner
+
+ Args:
+ ad1: The android device
+ ad2: The android device which is a group owner
+ """
+ ad2.droid.wifiP2pStopPeerDiscovery()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ ad1.droid.wifiP2pDiscoverPeers()
+ p2p_find_result = False
+ while not p2p_find_result:
+ ad1_event = ad1.ed.pop_event(p2pconsts.PEER_AVAILABLE_EVENT,
+ p2pconsts.P2P_FIND_TIMEOUT)
+ ad1.log.debug(ad1_event['data'])
+ for device in ad1_event['data']['Peers']:
+ if (device['Name'] == ad2.name and int(device['GroupCapability'])
+ & p2pconsts.P2P_GROUP_CAPAB_GROUP_OWNER):
+ ad2.deviceAddress = device['Address']
+ p2p_find_result = True
+ asserts.assert_true(
+ p2p_find_result,
+ "DUT didn't discovered group owner peer:%s device" % (ad2.name))
+
+
+def createP2pLocalService(ad, serviceCategory):
+ """Based on serviceCategory to create p2p local service
+ on an Android device ad
+
+ Args:
+ ad: The android device
+ serviceCategory: p2p local service type, UPNP / IPP / AFP,
+ """
+ testData = genTestData(serviceCategory)
+ if serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_UPNP:
+ ad.droid.wifiP2pCreateUpnpServiceInfo(testData[0], testData[1],
+ testData[2])
+ elif (serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_IPP
+ or serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_AFP):
+ ad.droid.wifiP2pCreateBonjourServiceInfo(testData[0], testData[1],
+ testData[2])
+ ad.droid.wifiP2pAddLocalService()
+
+
+def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver,
+ serviceType, queryString1, queryString2):
+ """Based on serviceType and query info, check service request result
+ same as expect or not on an Android device ad_serviceReceiver.
+ And remove p2p service request after result check.
+
+ Args:
+ ad_serviceProvider: The android device which provide p2p local service
+ ad_serviceReceiver: The android device which query p2p local service
+ serviceType: P2p local service type, Upnp or Bonjour
+ queryString1: Query String, NonNull
+ queryString2: Query String, used for Bonjour, Nullable
+ """
+ expectData = genExpectTestData(serviceType, queryString1, queryString2)
+ find_p2p_device(ad_serviceReceiver, ad_serviceProvider)
+ ad_serviceReceiver.droid.wifiP2pStopPeerDiscovery()
+ ad_serviceReceiver.droid.wifiP2pClearServiceRequests()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ ad_serviceReceiver.droid.wifiP2pDiscoverServices()
+ serviceData = {}
+ service_id = 0
+ if (serviceType ==
+ WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR):
+ ad_serviceReceiver.log.info(
+ "Request bonjour service in \
+ %s with Query String %s and %s " %
+ (ad_serviceReceiver.name, queryString1, queryString2))
+ ad_serviceReceiver.log.info("expectData %s" % expectData)
+ if queryString1 != None:
+ service_id = ad_serviceReceiver.droid.wifiP2pAddDnssdServiceRequest(
+ queryString1, queryString2)
+ else:
+ service_id = ad_serviceReceiver.droid.wifiP2pAddServiceRequest(
+ serviceType)
+ ad_serviceReceiver.log.info("request bonjour service id %s" %
+ service_id)
+ ad_serviceReceiver.droid.wifiP2pSetDnsSdResponseListeners()
+ ad_serviceReceiver.droid.wifiP2pDiscoverServices()
+ ad_serviceReceiver.log.info("Check Service Listener")
+ time.sleep(p2pconsts.DEFAULT_SERVICE_WAITING_TIME)
+ try:
+ dnssd_events = ad_serviceReceiver.ed.pop_all(p2pconsts.DNSSD_EVENT)
+ dnssd_txrecord_events = ad_serviceReceiver.ed.pop_all(
+ p2pconsts.DNSSD_TXRECORD_EVENT)
+ dns_service = WifiP2PEnums.WifiP2pDnsSdServiceResponse()
+ for dnssd_event in dnssd_events:
+ if dnssd_event['data'][
+ 'SourceDeviceAddress'] == ad_serviceProvider.deviceAddress:
+ dns_service.InstanceName = dnssd_event['data'][
+ p2pconsts.DNSSD_EVENT_INSTANCENAME_KEY]
+ dns_service.RegistrationType = dnssd_event['data'][
+ p2pconsts.DNSSD_EVENT_REGISTRATIONTYPE_KEY]
+ dns_service.FullDomainName = ""
+ dns_service.TxtRecordMap = ""
+ serviceData[dns_service.toString()] = 1
+ for dnssd_txrecord_event in dnssd_txrecord_events:
+ if dnssd_txrecord_event['data'][
+ 'SourceDeviceAddress'] == ad_serviceProvider.deviceAddress:
+ dns_service.InstanceName = ""
+ dns_service.RegistrationType = ""
+ dns_service.FullDomainName = dnssd_txrecord_event['data'][
+ p2pconsts.DNSSD_TXRECORD_EVENT_FULLDOMAINNAME_KEY]
+ dns_service.TxtRecordMap = dnssd_txrecord_event['data'][
+ p2pconsts.DNSSD_TXRECORD_EVENT_TXRECORDMAP_KEY]
+ serviceData[dns_service.toString()] = 1
+ ad_serviceReceiver.log.info("serviceData %s" % serviceData)
+ if len(serviceData) == 0:
+ ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(
+ service_id)
+ return -1
+ except queue.Empty as error:
+ ad_serviceReceiver.log.info("dnssd event is empty", )
+ elif (serviceType ==
+ WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP):
+ ad_serviceReceiver.log.info(
+ "Request upnp service in %s with Query String %s " %
+ (ad_serviceReceiver.name, queryString1))
+ ad_serviceReceiver.log.info("expectData %s" % expectData)
+ if queryString1 != None:
+ service_id = ad_serviceReceiver.droid.wifiP2pAddUpnpServiceRequest(
+ queryString1)
+ else:
+ service_id = ad_serviceReceiver.droid.wifiP2pAddServiceRequest(
+ WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP)
+ ad_serviceReceiver.droid.wifiP2pSetUpnpResponseListeners()
+ ad_serviceReceiver.droid.wifiP2pDiscoverServices()
+ ad_serviceReceiver.log.info("Check Service Listener")
+ time.sleep(p2pconsts.DEFAULT_SERVICE_WAITING_TIME)
+ try:
+ upnp_events = ad_serviceReceiver.ed.pop_all(p2pconsts.UPNP_EVENT)
+ for upnp_event in upnp_events:
+ if upnp_event['data']['Device'][
+ 'Address'] == ad_serviceProvider.deviceAddress:
+ for service in upnp_event['data'][
+ p2pconsts.UPNP_EVENT_SERVICELIST_KEY]:
+ serviceData[service] = 1
+ ad_serviceReceiver.log.info("serviceData %s" % serviceData)
+ if len(serviceData) == 0:
+ ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(
+ service_id)
+ return -1
+ except queue.Empty as error:
+ ad_serviceReceiver.log.info("p2p upnp event is empty", )
+
+ ad_serviceReceiver.log.info("Check ServiceList")
+ asserts.assert_true(checkServiceQueryResult(serviceData, expectData),
+ "ServiceList not same as Expect")
+ # After service checked, remove the service_id
+ ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(service_id)
+ return 0
+
+
+def requestServiceAndCheckResultWithRetry(ad_serviceProvider,
+ ad_serviceReceiver,
+ serviceType,
+ queryString1,
+ queryString2,
+ retryCount=3):
+ """ allow failures for requestServiceAndCheckResult. Service
+ discovery might fail unexpectedly because the request packet might not be
+ recevied by the service responder due to p2p state switch.
+
+ Args:
+ ad_serviceProvider: The android device which provide p2p local service
+ ad_serviceReceiver: The android device which query p2p local service
+ serviceType: P2p local service type, Upnp or Bonjour
+ queryString1: Query String, NonNull
+ queryString2: Query String, used for Bonjour, Nullable
+ retryCount: maximum retry count, default is 3
+ """
+ ret = 0
+ while retryCount > 0:
+ ret = requestServiceAndCheckResult(ad_serviceProvider,
+ ad_serviceReceiver, serviceType,
+ queryString1, queryString2)
+ if (ret == 0):
+ break
+ retryCount -= 1
+
+ asserts.assert_equal(0, ret, "cannot find any services with retries.")
+
+
+def checkServiceQueryResult(serviceList, expectServiceList):
+ """Check serviceList same as expectServiceList or not
+
+ Args:
+ serviceList: ServiceList which get from query result
+ expectServiceList: ServiceList which hardcode in genExpectTestData
+ Return:
+ True: serviceList same as expectServiceList
+ False:Exist discrepancy between serviceList and expectServiceList
+ """
+ tempServiceList = serviceList.copy()
+ tempExpectServiceList = expectServiceList.copy()
+ for service in serviceList.keys():
+ if service in expectServiceList:
+ del tempServiceList[service]
+ del tempExpectServiceList[service]
+ return len(tempExpectServiceList) == 0 and len(tempServiceList) == 0
+
+
+def genTestData(serviceCategory):
+ """Based on serviceCategory to generator Test Data
+
+ Args:
+ serviceCategory: P2p local service type, Upnp or Bonjour
+ Return:
+ TestData
+ """
+ testData = []
+ if serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_UPNP:
+ testData.append(p2pconsts.UpnpTestData.uuid)
+ testData.append(p2pconsts.UpnpTestData.serviceType)
+ testData.append([
+ p2pconsts.UpnpTestData.AVTransport,
+ p2pconsts.UpnpTestData.ConnectionManager
+ ])
+ elif serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_IPP:
+ testData.append(p2pconsts.IppTestData.ippInstanceName)
+ testData.append(p2pconsts.IppTestData.ippRegistrationType)
+ testData.append(p2pconsts.IppTestData.ipp_txtRecord)
+ elif serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_AFP:
+ testData.append(p2pconsts.AfpTestData.afpInstanceName)
+ testData.append(p2pconsts.AfpTestData.afpRegistrationType)
+ testData.append(p2pconsts.AfpTestData.afp_txtRecord)
+
+ return testData
+
+
+def genExpectTestData(serviceType, queryString1, queryString2):
+ """Based on serviceCategory to generator expect serviceList
+
+ Args:
+ serviceType: P2p local service type, Upnp or Bonjour
+ queryString1: Query String, NonNull
+ queryString2: Query String, used for Bonjour, Nullable
+ Return:
+ expectServiceList
+ """
+ expectServiceList = {}
+ if (serviceType ==
+ WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR):
+ ipp_service = WifiP2PEnums.WifiP2pDnsSdServiceResponse()
+ afp_service = WifiP2PEnums.WifiP2pDnsSdServiceResponse()
+ if queryString1 == p2pconsts.IppTestData.ippRegistrationType:
+ if queryString2 == p2pconsts.IppTestData.ippInstanceName:
+ ipp_service.InstanceName = ""
+ ipp_service.RegistrationType = ""
+ ipp_service.FullDomainName = p2pconsts.IppTestData.ippDomainName
+ ipp_service.TxtRecordMap = p2pconsts.IppTestData.ipp_txtRecord
+ expectServiceList[ipp_service.toString()] = 1
+ return expectServiceList
+ ipp_service.InstanceName = p2pconsts.IppTestData.ippInstanceName
+ ipp_service.RegistrationType = (
+ p2pconsts.IppTestData.ippRegistrationType + ".local.")
+ ipp_service.FullDomainName = ""
+ ipp_service.TxtRecordMap = ""
+ expectServiceList[ipp_service.toString()] = 1
+ return expectServiceList
+ elif queryString1 == p2pconsts.AfpTestData.afpRegistrationType:
+ if queryString2 == p2pconsts.AfpTestData.afpInstanceName:
+ afp_service.InstanceName = ""
+ afp_service.RegistrationType = ""
+ afp_service.FullDomainName = p2pconsts.AfpTestData.afpDomainName
+ afp_service.TxtRecordMap = p2pconsts.AfpTestData.afp_txtRecord
+ expectServiceList[afp_service.toString()] = 1
+ return expectServiceList
+ ipp_service.InstanceName = p2pconsts.IppTestData.ippInstanceName
+ ipp_service.RegistrationType = (
+ p2pconsts.IppTestData.ippRegistrationType + ".local.")
+ ipp_service.FullDomainName = ""
+ ipp_service.TxtRecordMap = ""
+ expectServiceList[ipp_service.toString()] = 1
+
+ ipp_service.InstanceName = ""
+ ipp_service.RegistrationType = ""
+ ipp_service.FullDomainName = p2pconsts.IppTestData.ippDomainName
+ ipp_service.TxtRecordMap = p2pconsts.IppTestData.ipp_txtRecord
+ expectServiceList[ipp_service.toString()] = 1
+
+ afp_service.InstanceName = p2pconsts.AfpTestData.afpInstanceName
+ afp_service.RegistrationType = (
+ p2pconsts.AfpTestData.afpRegistrationType + ".local.")
+ afp_service.FullDomainName = ""
+ afp_service.TxtRecordMap = ""
+ expectServiceList[afp_service.toString()] = 1
+
+ afp_service.InstanceName = ""
+ afp_service.RegistrationType = ""
+ afp_service.FullDomainName = p2pconsts.AfpTestData.afpDomainName
+ afp_service.TxtRecordMap = p2pconsts.AfpTestData.afp_txtRecord
+ expectServiceList[afp_service.toString()] = 1
+
+ return expectServiceList
+ elif serviceType == WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP:
+ upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + "::" + (
+ p2pconsts.UpnpTestData.rootdevice)
+ expectServiceList[upnp_service] = 1
+ if queryString1 != "upnp:rootdevice":
+ upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + (
+ "::" + p2pconsts.UpnpTestData.AVTransport)
+ expectServiceList[upnp_service] = 1
+ upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + (
+ "::" + p2pconsts.UpnpTestData.ConnectionManager)
+ expectServiceList[upnp_service] = 1
+ upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + (
+ "::" + p2pconsts.UpnpTestData.serviceType)
+ expectServiceList[upnp_service] = 1
+ upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid
+ expectServiceList[upnp_service] = 1
+
+ return expectServiceList
+
+
+def p2p_create_group(ad):
+ """Create a group as Group Owner
+
+ Args:
+ ad: The android device
+ """
+ ad.droid.wifiP2pCreateGroup()
+ ad.ed.pop_event(p2pconsts.CREATE_GROUP_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+
+
+def p2p_create_group_with_config(ad, network_name, passphrase, band):
+ """Create a group as Group Owner
+
+ Args:
+ ad: The android device
+ """
+ wifi_p2p_config = {
+ WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name,
+ WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase,
+ WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band,
+ WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: {
+ WifiP2PEnums.WpsInfo.WPS_SETUP_KEY:
+ WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC
+ }
+ }
+ ad.droid.wifiP2pCreateGroupWithConfig(wifi_p2p_config)
+ ad.ed.pop_event(p2pconsts.CREATE_GROUP_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ time.sleep(p2pconsts.DEFAULT_SLEEPTIME)
+
+
+def wifi_p2p_set_channels_for_current_group(ad, listening_chan,
+ operating_chan):
+ """Sets the listening channel and operating channel of the current group
+ created with initialize.
+
+ Args:
+ ad: The android device
+ listening_chan: Integer, the listening channel
+ operating_chan: Integer, the operating channel
+ """
+ ad.droid.wifiP2pSetChannelsForCurrentGroup(listening_chan, operating_chan)
+ ad.ed.pop_event(p2pconsts.SET_CHANNEL_SUCCESS_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+
+
+class WifiP2PEnums():
+ class WifiP2pConfig():
+ DEVICEADDRESS_KEY = "deviceAddress"
+ WPSINFO_KEY = "wpsInfo"
+ GO_INTENT_KEY = "groupOwnerIntent"
+ NETID_KEY = "netId"
+ NETWORK_NAME = "networkName"
+ PASSPHRASE = "passphrase"
+ GROUP_BAND = "groupOwnerBand"
+
+ class WpsInfo():
+ WPS_SETUP_KEY = "setup"
+ BSSID_KEY = "BSSID"
+ WPS_PIN_KEY = "pin"
+ #TODO: remove it from wifi_test_utils.py
+ WIFI_WPS_INFO_PBC = 0
+ WIFI_WPS_INFO_DISPLAY = 1
+ WIFI_WPS_INFO_KEYPAD = 2
+ WIFI_WPS_INFO_LABEL = 3
+ WIFI_WPS_INFO_INVALID = 4
+
+ class WifiP2pServiceInfo():
+ #TODO: remove it from wifi_test_utils.py
+ # Macros for wifi p2p.
+ WIFI_P2P_SERVICE_TYPE_ALL = 0
+ WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
+ WIFI_P2P_SERVICE_TYPE_UPNP = 2
+ WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
+
+ class WifiP2pDnsSdServiceResponse():
+ def __init__(self):
+ pass
+
+ InstanceName = ""
+ RegistrationType = ""
+ FullDomainName = ""
+ TxtRecordMap = {}
+
+ def toString(self):
+ return self.InstanceName + self.RegistrationType + (
+ self.FullDomainName + str(self.TxtRecordMap))
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rpm_controller_utils.py b/acts_tests/acts_contrib/test_utils/wifi/rpm_controller_utils.py
new file mode 100644
index 0000000..6aa8e3e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/rpm_controller_utils.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from acts.controllers.attenuator_lib._tnhelper import _ascii_string
+
+import logging
+import telnetlib
+
+ID = '.A'
+LOGIN_PWD = 'admn'
+ON = 'On'
+OFF = 'Off'
+PASSWORD = 'Password: '
+PORT = 23
+RPM_PROMPT = 'Switched CDU: '
+SEPARATOR = '\n'
+TIMEOUT = 3
+USERNAME = 'Username: '
+
+
+class RpmControllerError(Exception):
+ """Error related to RPM switch."""
+
+class RpmController(object):
+ """Class representing telnet to RPM switch.
+
+ Each object represents a telnet connection to the RPM switch's IP.
+
+ Attributes:
+ tn: represents a connection to RPM switch.
+ host: IP address of the RPM controller.
+ """
+ def __init__(self, host):
+ """Initializes the RPM controller object.
+
+ Establishes a telnet connection and login to the switch.
+ """
+ self.host = host
+ logging.info('RPM IP: %s' % self.host)
+
+ self.tn = telnetlib.Telnet(self.host)
+ self.tn.open(self.host, PORT, TIMEOUT)
+ self.run(USERNAME, LOGIN_PWD)
+ result = self.run(PASSWORD, LOGIN_PWD)
+ if RPM_PROMPT not in result:
+ raise RpmControllerError('Failed to login to rpm controller %s'
+ % self.host)
+
+ def run(self, prompt, cmd_str):
+ """Method to run commands on the RPM.
+
+ This method simply runs a command and returns output in decoded format.
+ The calling methods should take care of parsing the expected result
+ from this output.
+
+ Args:
+ prompt: Expected prompt before running a command.
+ cmd_str: Command to run on RPM.
+
+ Returns:
+ Decoded text returned by the command.
+ """
+ cmd_str = '%s%s' % (cmd_str, SEPARATOR)
+ res = self.tn.read_until(_ascii_string(prompt), TIMEOUT)
+
+ self.tn.write(_ascii_string(cmd_str))
+ idx, val, txt = self.tn.expect(
+ [_ascii_string('\S+%s' % SEPARATOR)], TIMEOUT)
+
+ return txt.decode()
+
+ def set_rpm_port_state(self, rpm_port, state):
+ """Method to turn on/off rpm port.
+
+ Args:
+ rpm_port: port number of the switch to turn on.
+ state: 'on' or 'off'
+
+ Returns:
+ True: if the state is set to the expected value
+ """
+ port = '%s%s' % (ID, rpm_port)
+ logging.info('Turning %s port: %s' % (state, port))
+ self.run(RPM_PROMPT, '%s %s' % (state.lower(), port))
+ result = self.run(RPM_PROMPT, 'status %s' % port)
+ if port not in result:
+ raise RpmControllerError('Port %s doesn\'t exist' % port)
+ return state in result
+
+ def turn_on(self, rpm_port):
+ """Method to turn on a port on the RPM switch.
+
+ Args:
+ rpm_port: port number of the switch to turn on.
+
+ Returns:
+ True if the port is turned on.
+ False if not turned on.
+ """
+ return self.set_rpm_port_state(rpm_port, ON)
+
+ def turn_off(self, rpm_port):
+ """Method to turn off a port on the RPM switch.
+
+ Args:
+ rpm_port: port number of the switch to turn off.
+
+ Returns:
+ True if the port is turned off.
+ False if not turned off.
+ """
+ return self.set_rpm_port_state(rpm_port, OFF)
+
+ def __del__(self):
+ """Close the telnet connection. """
+ self.tn.close()
+
+
+def create_telnet_session(ip):
+ """Returns telnet connection object to RPM's IP."""
+ return RpmController(ip)
+
+def turn_on_ap(pcap, ssid, rpm_port, rpm_ip=None, rpm=None):
+ """Turn on the AP.
+
+ This method turns on the RPM port the AP is connected to,
+ verify the SSID of the AP is found in the scan result through the
+ packet capturer.
+
+ Either IP addr of the RPM switch or the existing telnet connection
+ to the RPM is required. Multiple APs might be connected to the same RPM
+ switch. Instead of connecting/terminating telnet for each AP, the test
+ can maintain a single telnet connection for all the APs.
+
+ Args:
+ pcap: packet capture object.
+ ssid: SSID of the wifi network.
+ rpm_port: Port number on the RPM switch the AP is connected to.
+ rpm_ip: IP address of the RPM switch.
+ rpm: telnet connection object to the RPM switch.
+ """
+ if not rpm and not rpm_ip:
+ logging.error("Failed to turn on AP. Need telnet object or RPM IP")
+ return False
+ elif not rpm:
+ rpm = create_telnet_session(rpm_ip)
+
+ return rpm.turn_on(rpm_port) and pcap.start_scan_and_find_network(ssid)
+
+def turn_off_ap(rpm_port, rpm_ip=None, rpm=None):
+ """ Turn off AP.
+
+ This method turns off the RPM port the AP is connected to.
+
+ Either IP addr of the RPM switch or the existing telnet connection
+ to the RPM is required.
+
+ Args:
+ rpm_port: Port number on the RPM switch the AP is connected to.
+ rpm_ip: IP address of the RPM switch.
+ rpm: telnet connection object to the RPM switch.
+ """
+ if not rpm and not rpm_ip:
+ logging.error("Failed to turn off AP. Need telnet object or RPM IP")
+ return False
+ elif not rpm:
+ rpm = create_telnet_session(rpm_ip)
+
+ return rpm.turn_off(rpm_port)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py
new file mode 100644
index 0000000..f2cc393
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+
+
+class RttBaseTest(BaseTestClass):
+ def setup_test(self):
+ required_params = ("lci_reference", "lcr_reference",
+ "rtt_reference_distance_mm",
+ "stress_test_min_iteration_count",
+ "stress_test_target_run_time_sec")
+ self.unpack_userparams(required_params)
+
+ # can be moved to JSON config file
+ self.rtt_reference_distance_margin_mm = 2000
+ self.rtt_max_failure_rate_two_sided_rtt_percentage = 20
+ self.rtt_max_failure_rate_one_sided_rtt_percentage = 50
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage = 10
+ self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage = 50
+ self.rtt_min_expected_rssi_dbm = -100
+
+ for ad in self.android_devices:
+ utils.set_location_service(ad, True)
+ asserts.skip_if(
+ not ad.droid.doesDeviceSupportWifiRttFeature(),
+ "Device under test does not support Wi-Fi RTT - skipping test")
+ wutils.wifi_toggle_state(ad, True)
+ rtt_avail = ad.droid.wifiIsRttAvailable()
+ if not rtt_avail:
+ self.log.info('RTT not available. Waiting ...')
+ rutils.wait_for_event(ad, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+ ad.ed.clear_all_events()
+ rutils.config_privilege_override(ad, False)
+ wutils.set_wifi_country_code(ad, wutils.WifiEnums.CountryCode.US)
+ ad.rtt_capabilities = rutils.get_rtt_capabilities(ad)
+
+ def teardown_test(self):
+ for ad in self.android_devices:
+ if not ad.droid.doesDeviceSupportWifiRttFeature():
+ return
+
+ # clean-up queue from the System Service UID
+ ad.droid.wifiRttCancelRanging([1000])
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+ ad.cat_adb_log(test_name, begin_time)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rtt/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/rtt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/rtt/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_const.py b/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_const.py
new file mode 100644
index 0000000..34e6701
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_const.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+######################################################
+# Broadcast events
+######################################################
+BROADCAST_WIFI_RTT_AVAILABLE = "WifiRttAvailable"
+BROADCAST_WIFI_RTT_NOT_AVAILABLE = "WifiRttNotAvailable"
+
+######################################################
+# RangingResultCallback events
+######################################################
+EVENT_CB_RANGING_ON_FAIL = "WifiRttRangingFailure"
+EVENT_CB_RANGING_ON_RESULT = "WifiRttRangingResults"
+
+EVENT_CB_RANGING_KEY_RESULTS = "Results"
+
+EVENT_CB_RANGING_KEY_STATUS = "status"
+EVENT_CB_RANGING_KEY_DISTANCE_MM = "distanceMm"
+EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM = "distanceStdDevMm"
+EVENT_CB_RANGING_KEY_RSSI = "rssi"
+EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS = "numAttemptedMeasurements"
+EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS = "numSuccessfulMeasurements"
+EVENT_CB_RANGING_KEY_LCI = "lci"
+EVENT_CB_RANGING_KEY_LCR = "lcr"
+EVENT_CB_RANGING_KEY_TIMESTAMP = "timestamp"
+EVENT_CB_RANGING_KEY_MAC = "mac"
+EVENT_CB_RANGING_KEY_PEER_ID = "peerId"
+EVENT_CB_RANGING_KEY_MAC_AS_STRING = "macAsString"
+
+EVENT_CB_RANGING_STATUS_SUCCESS = 0
+EVENT_CB_RANGING_STATUS_FAIL = 1
+EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2
+
+######################################################
+# status codes
+######################################################
+
+RANGING_FAIL_CODE_GENERIC = 1
+RANGING_FAIL_CODE_RTT_NOT_AVAILABLE = 2
+
+######################################################
+# ScanResults keys
+######################################################
+
+SCAN_RESULT_KEY_RTT_RESPONDER = "is80211McRTTResponder"
+
+######################################################
+# Capabilities keys
+######################################################
+
+CAP_RTT_ONE_SIDED_SUPPORTED = "rttOneSidedSupported"
+CAP_FTM_SUPPORTED = "rttFtmSupported"
+CAP_LCI_SUPPORTED = "lciSupported"
+CAP_LCR_SUPPORTED = "lcrSupported"
+CAP_RESPONDER_SUPPORTED = "responderSupported"
+CAP_MC_VERSION = "mcVersion"
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_test_utils.py
new file mode 100644
index 0000000..eac7378
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/rtt/rtt_test_utils.py
@@ -0,0 +1,501 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import queue
+import statistics
+import time
+
+from acts import asserts
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 15
+
+
+def decorate_event(event_name, id):
+ return '%s_%d' % (event_name, id)
+
+
+def wait_for_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for the specified event or timeout.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait on
+ timeout: Number of seconds to wait
+ Returns:
+ The event (if available)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%s%s: %s', prefix, event_name, event['data'])
+ return event
+ except queue.Empty:
+ ad.log.info('%sTimed out while waiting for %s', prefix, event_name)
+ asserts.fail(event_name)
+
+
+def fail_on_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for a timeout period and looks for the specified event - fails if it
+ is observed.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait for (and fail on its appearance)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%sReceived unwanted %s: %s', prefix, event_name,
+ event['data'])
+ asserts.fail(event_name, extras=event)
+ except queue.Empty:
+ ad.log.info('%s%s not seen (as expected)', prefix, event_name)
+ return
+
+
+def get_rtt_capabilities(ad):
+ """Get the Wi-Fi RTT capabilities from the specified device. The
+ capabilities are a dictionary keyed by rtt_const.CAP_* keys.
+
+ Args:
+ ad: the Android device
+ Returns: the capability dictionary.
+ """
+ return json.loads(ad.adb.shell('cmd wifirtt get_capabilities'))
+
+
+def config_privilege_override(dut, override_to_no_privilege):
+ """Configure the device to override the permission check and to disallow any
+ privileged RTT operations, e.g. disallow one-sided RTT to Responders (APs)
+ which do not support IEEE 802.11mc.
+
+ Args:
+ dut: Device to configure.
+ override_to_no_privilege: True to indicate no privileged ops, False for
+ default (which will allow privileged ops).
+ """
+ dut.adb.shell("cmd wifirtt set override_assume_no_privilege %d" %
+ (1 if override_to_no_privilege else 0))
+
+
+def get_rtt_constrained_results(scanned_networks, support_rtt):
+ """Filter the input list and only return those networks which either support
+ or do not support RTT (IEEE 802.11mc.)
+
+ Args:
+ scanned_networks: A list of networks from scan results.
+ support_rtt: True - only return those APs which support RTT, False - only
+ return those APs which do not support RTT.
+
+ Returns: a sub-set of the scanned_networks per support_rtt constraint.
+ """
+ matching_networks = []
+ for network in scanned_networks:
+ if support_rtt:
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in network
+ and network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ matching_networks.append(network)
+ else:
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER not in network
+ or not network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ matching_networks.append(network)
+
+ return matching_networks
+
+
+def scan_networks(dut, max_tries=3):
+ """Perform a scan and return scan results.
+
+ Args:
+ dut: Device under test.
+ max_retries: Retry scan to ensure network is found
+
+ Returns: an array of scan results.
+ """
+ scan_results = []
+ for num_tries in range(max_tries):
+ wutils.start_wifi_connection_scan(dut)
+ scan_results = dut.droid.wifiGetScanResults()
+ if scan_results:
+ break
+ return scan_results
+
+
+def scan_with_rtt_support_constraint(dut, support_rtt, repeat=0):
+ """Perform a scan and return scan results of APs: only those that support or
+ do not support RTT (IEEE 802.11mc) - per the support_rtt parameter.
+
+ Args:
+ dut: Device under test.
+ support_rtt: True - only return those APs which support RTT, False - only
+ return those APs which do not support RTT.
+ repeat: Re-scan this many times to find an RTT supporting network.
+
+ Returns: an array of scan results.
+ """
+ for i in range(repeat + 1):
+ scan_results = scan_networks(dut)
+ aps = get_rtt_constrained_results(scan_results, support_rtt)
+ if len(aps) != 0:
+ return aps
+
+ return []
+
+
+def select_best_scan_results(scans, select_count, lowest_rssi=-80):
+ """Select the strongest 'select_count' scans in the input list based on
+ highest RSSI. Exclude all very weak signals, even if results in a shorter
+ list.
+
+ Args:
+ scans: List of scan results.
+ select_count: An integer specifying how many scans to return at most.
+ lowest_rssi: The lowest RSSI to accept into the output.
+ Returns: a list of the strongest 'select_count' scan results from the scans
+ list.
+ """
+
+ def takeRssi(element):
+ return element['level']
+
+ result = []
+ scans.sort(key=takeRssi, reverse=True)
+ for scan in scans:
+ if len(result) == select_count:
+ break
+ if scan['level'] < lowest_rssi:
+ break # rest are lower since we're sorted
+ result.append(scan)
+
+ return result
+
+
+def validate_ap_result(scan_result, range_result):
+ """Validate the range results:
+ - Successful if AP (per scan result) support 802.11mc (allowed to fail
+ otherwise)
+ - MAC of result matches the BSSID
+
+ Args:
+ scan_result: Scan result for the AP
+ range_result: Range result returned by the RTT API
+ """
+ asserts.assert_equal(
+ scan_result[wutils.WifiEnums.BSSID_KEY],
+ range_result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID],
+ 'MAC/BSSID mismatch')
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scan_result
+ and scan_result[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ asserts.assert_true(
+ range_result[rconsts.EVENT_CB_RANGING_KEY_STATUS] ==
+ rconsts.EVENT_CB_RANGING_STATUS_SUCCESS,
+ 'Ranging failed for an AP which supports 802.11mc!')
+
+
+def validate_ap_results(scan_results, range_results):
+ """Validate an array of ranging results against the scan results used to
+ trigger the range. The assumption is that the results are returned in the
+ same order as the request (which were the scan results).
+
+ Args:
+ scan_results: Scans results used to trigger the range request
+ range_results: Range results returned by the RTT API
+ """
+ asserts.assert_equal(
+ len(scan_results), len(range_results),
+ 'Mismatch in length of scan results and range results')
+
+ # sort first based on BSSID/MAC
+ scan_results.sort(key=lambda x: x[wutils.WifiEnums.BSSID_KEY])
+ range_results.sort(
+ key=lambda x: x[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID])
+
+ for i in range(len(scan_results)):
+ validate_ap_result(scan_results[i], range_results[i])
+
+
+def validate_aware_mac_result(range_result, mac, description):
+ """Validate the range result for an Aware peer specified with a MAC address:
+ - Correct MAC address.
+
+ The MAC addresses may contain ":" (which are ignored for the comparison) and
+ may be in any case (which is ignored for the comparison).
+
+ Args:
+ range_result: Range result returned by the RTT API
+ mac: MAC address of the peer
+ description: Additional content to print on failure
+ """
+ mac1 = mac.replace(':', '').lower()
+ mac2 = range_result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING].replace(
+ ':', '').lower()
+ asserts.assert_equal(mac1, mac2, '%s: MAC mismatch' % description)
+
+
+def validate_aware_peer_id_result(range_result, peer_id, description):
+ """Validate the range result for An Aware peer specified with a Peer ID:
+ - Correct Peer ID
+ - MAC address information not available
+
+ Args:
+ range_result: Range result returned by the RTT API
+ peer_id: Peer ID of the peer
+ description: Additional content to print on failure
+ """
+ asserts.assert_equal(peer_id,
+ range_result[rconsts.EVENT_CB_RANGING_KEY_PEER_ID],
+ '%s: Peer Id mismatch' % description)
+ asserts.assert_false(rconsts.EVENT_CB_RANGING_KEY_MAC in range_result,
+ '%s: MAC Address not empty!' % description)
+
+
+def extract_stats(results,
+ range_reference_mm,
+ range_margin_mm,
+ min_rssi,
+ reference_lci=[],
+ reference_lcr=[],
+ summary_only=False):
+ """Extract statistics from a list of RTT results. Returns a dictionary
+ with results:
+ - num_results (success or fails)
+ - num_success_results
+ - num_no_results (e.g. timeout)
+ - num_failures
+ - num_range_out_of_margin (only for successes)
+ - num_invalid_rssi (only for successes)
+ - distances: extracted list of distances
+ - distance_std_devs: extracted list of distance standard-deviations
+ - rssis: extracted list of RSSI
+ - distance_mean
+ - distance_std_dev (based on distance - ignoring the individual std-devs)
+ - rssi_mean
+ - rssi_std_dev
+ - status_codes
+ - lcis: extracted list of all of the individual LCI
+ - lcrs: extracted list of all of the individual LCR
+ - any_lci_mismatch: True/False - checks if all LCI results are identical to
+ the reference LCI.
+ - any_lcr_mismatch: True/False - checks if all LCR results are identical to
+ the reference LCR.
+ - num_attempted_measurements: extracted list of all of the individual
+ number of attempted measurements.
+ - num_successful_measurements: extracted list of all of the individual
+ number of successful measurements.
+ - invalid_num_attempted: True/False - checks if number of attempted
+ measurements is non-zero for successful results.
+ - invalid_num_successful: True/False - checks if number of successful
+ measurements is non-zero for successful results.
+
+ Args:
+ results: List of RTT results.
+ range_reference_mm: Reference value for the distance (in mm)
+ range_margin_mm: Acceptable absolute margin for distance (in mm)
+ min_rssi: Acceptable minimum RSSI value.
+ reference_lci, reference_lcr: Reference values for LCI and LCR.
+ summary_only: Only include summary keys (reduce size).
+
+ Returns: A dictionary of stats.
+ """
+ stats = {}
+ stats['num_results'] = 0
+ stats['num_success_results'] = 0
+ stats['num_no_results'] = 0
+ stats['num_failures'] = 0
+ stats['num_range_out_of_margin'] = 0
+ stats['num_invalid_rssi'] = 0
+ stats['any_lci_mismatch'] = False
+ stats['any_lcr_mismatch'] = False
+ stats['invalid_num_attempted'] = False
+ stats['invalid_num_successful'] = False
+
+ range_max_mm = range_reference_mm + range_margin_mm
+ range_min_mm = range_reference_mm - range_margin_mm
+
+ distances = []
+ distance_std_devs = []
+ rssis = []
+ num_attempted_measurements = []
+ num_successful_measurements = []
+ status_codes = []
+ lcis = []
+ lcrs = []
+
+ for i in range(len(results)):
+ result = results[i]
+
+ if result is None: # None -> timeout waiting for RTT result
+ stats['num_no_results'] = stats['num_no_results'] + 1
+ continue
+ stats['num_results'] = stats['num_results'] + 1
+
+ status_codes.append(result[rconsts.EVENT_CB_RANGING_KEY_STATUS])
+ if status_codes[-1] != rconsts.EVENT_CB_RANGING_STATUS_SUCCESS:
+ stats['num_failures'] = stats['num_failures'] + 1
+ continue
+ stats['num_success_results'] = stats['num_success_results'] + 1
+
+ distance_mm = result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_MM]
+ distances.append(distance_mm)
+ if not range_min_mm <= distance_mm <= range_max_mm:
+ stats[
+ 'num_range_out_of_margin'] = stats['num_range_out_of_margin'] + 1
+ distance_std_devs.append(
+ result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM])
+
+ rssi = result[rconsts.EVENT_CB_RANGING_KEY_RSSI]
+ rssis.append(rssi)
+ if not min_rssi <= rssi <= 0:
+ stats['num_invalid_rssi'] = stats['num_invalid_rssi'] + 1
+
+ num_attempted = result[
+ rconsts.EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS]
+ num_attempted_measurements.append(num_attempted)
+ if num_attempted == 0:
+ stats['invalid_num_attempted'] = True
+
+ num_successful = result[
+ rconsts.EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS]
+ num_successful_measurements.append(num_successful)
+ if num_successful == 0:
+ stats['invalid_num_successful'] = True
+
+ lcis.append(result[rconsts.EVENT_CB_RANGING_KEY_LCI])
+ if (result[rconsts.EVENT_CB_RANGING_KEY_LCI] != reference_lci):
+ stats['any_lci_mismatch'] = True
+ lcrs.append(result[rconsts.EVENT_CB_RANGING_KEY_LCR])
+ if (result[rconsts.EVENT_CB_RANGING_KEY_LCR] != reference_lcr):
+ stats['any_lcr_mismatch'] = True
+
+ if len(distances) > 0:
+ stats['distance_mean'] = statistics.mean(distances)
+ if len(distances) > 1:
+ stats['distance_std_dev'] = statistics.stdev(distances)
+ if len(rssis) > 0:
+ stats['rssi_mean'] = statistics.mean(rssis)
+ if len(rssis) > 1:
+ stats['rssi_std_dev'] = statistics.stdev(rssis)
+ if not summary_only:
+ stats['distances'] = distances
+ stats['distance_std_devs'] = distance_std_devs
+ stats['rssis'] = rssis
+ stats['num_attempted_measurements'] = num_attempted_measurements
+ stats['num_successful_measurements'] = num_successful_measurements
+ stats['status_codes'] = status_codes
+ stats['lcis'] = lcis
+ stats['lcrs'] = lcrs
+
+ return stats
+
+
+def run_ranging(dut,
+ aps,
+ iter_count,
+ time_between_iterations,
+ target_run_time_sec=0):
+ """Executing ranging to the set of APs.
+
+ Will execute a minimum of 'iter_count' iterations. Will continue to run
+ until execution time (just) exceeds 'target_run_time_sec'.
+
+ Args:
+ dut: Device under test
+ aps: A list of APs (Access Points) to range to.
+ iter_count: (Minimum) Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ target_run_time_sec: The target run time in seconds.
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement).
+ """
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ asserts.assert_true(len(aps) > 0, "Need at least one AP!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ events = {} # need to keep track per BSSID!
+ for ap in aps:
+ events[ap["BSSID"]] = []
+
+ start_clock = time.time()
+ iterations_done = 0
+ run_time = 0
+ while iterations_done < iter_count or (target_run_time_sec != 0
+ and run_time < target_run_time_sec):
+ if iterations_done != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ id = dut.droid.wifiRttStartRangingToAccessPoints(aps)
+ try:
+ event = dut.ed.pop_event(
+ decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id),
+ EVENT_TIMEOUT)
+ range_results = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS]
+ asserts.assert_equal(
+ len(aps), len(range_results),
+ 'Mismatch in length of scan results and range results')
+ for result in range_results:
+ bssid = result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING]
+ asserts.assert_true(
+ bssid in events,
+ "Result BSSID %s not in requested AP!?" % bssid)
+ asserts.assert_equal(
+ len(events[bssid]), iterations_done,
+ "Duplicate results for BSSID %s!?" % bssid)
+ events[bssid].append(result)
+ except queue.Empty:
+ for ap in aps:
+ events[ap["BSSID"]].append(None)
+
+ iterations_done = iterations_done + 1
+ run_time = time.time() - start_clock
+
+ return events
+
+
+def analyze_results(all_aps_events,
+ rtt_reference_distance_mm,
+ distance_margin_mm,
+ min_expected_rssi,
+ lci_reference,
+ lcr_reference,
+ summary_only=False):
+ """Verifies the results of the RTT experiment.
+
+ Args:
+ all_aps_events: Dictionary of APs, each a list of RTT result events.
+ rtt_reference_distance_mm: Expected distance to the AP (source of truth).
+ distance_margin_mm: Accepted error marging in distance measurement.
+ min_expected_rssi: Minimum acceptable RSSI value
+ lci_reference, lcr_reference: Expected LCI/LCR values (arrays of bytes).
+ summary_only: Only include summary keys (reduce size).
+ """
+ all_stats = {}
+ for bssid, events in all_aps_events.items():
+ stats = extract_stats(events, rtt_reference_distance_mm,
+ distance_margin_mm, min_expected_rssi,
+ lci_reference, lcr_reference, summary_only)
+ all_stats[bssid] = stats
+ return all_stats
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py
new file mode 100644
index 0000000..3a49905
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Constants for Wifi related events.
+WIFI_CONNECTED = "WifiNetworkConnected"
+WIFI_DISCONNECTED = "WifiNetworkDisconnected"
+SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
+WIFI_STATE_CHANGED = "WifiStateChanged"
+WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
+WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH = "WifiManagerNetworkRequestMatchCallbackOnMatch"
+WIFI_NETWORK_REQUEST_MATCH_CB_ON_CONNECT_SUCCESS = "WifiManagerNetworkRequestMatchCallbackOnUserSelectionConnectSuccess"
+WIFI_NETWORK_REQUEST_MATCH_CB_ON_CONNECT_FAILURE = "WifiManagerNetworkRequestMatchCallbackOnUserSelectionConnectFailure"
+WIFI_NETWORK_CB_ON_AVAILABLE = "WifiManagerNetworkCallbackOnAvailable"
+WIFI_NETWORK_CB_ON_UNAVAILABLE = "WifiManagerNetworkCallbackOnUnavailable"
+WIFI_NETWORK_CB_ON_LOST = "WifiManagerNetworkCallbackOnLost"
+WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "WifiNetworkSuggestionPostConnection"
+
+# These constants will be used by the ACTS wifi tests.
+CONNECT_BY_CONFIG_SUCCESS = 'WifiManagerConnectByConfigOnSuccess'
+CONNECT_BY_NETID_SUCCESS = 'WifiManagerConnectByNetIdOnSuccess'
+
+# Softap related constants
+SOFTAP_CALLBACK_EVENT = "WifiManagerSoftApCallback-"
+# Callback Event for softap state change
+# WifiManagerSoftApCallback-[callbackId]-OnStateChanged
+SOFTAP_STATE_CHANGED = "-OnStateChanged"
+SOFTAP_STATE_CHANGE_CALLBACK_KEY = "State"
+WIFI_AP_DISABLING_STATE = 10
+WIFI_AP_DISABLED_STATE = 11
+WIFI_AP_ENABLING_STATE = 12
+WIFI_AP_ENABLED_STATE = 13
+WIFI_AP_FAILED_STATE = 14
+
+# Callback Event for client number change:
+# WifiManagerSoftApCallback-[callbackId]-OnNumClientsChanged
+SOFTAP_NUMBER_CLIENTS_CHANGED = "-OnNumClientsChanged"
+SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY = "NumClients"
+SOFTAP_CLIENTS_MACS_CALLBACK_KEY = "MacAddresses"
+# Callback Event for softap info change
+SOFTAP_INFO_CHANGED = "-OnInfoChanged"
+SOFTAP_INFO_FREQUENCY_CALLBACK_KEY = "frequency"
+SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY = "bandwidth"
+# Callback Event for softap client blocking
+SOFTAP_BLOCKING_CLIENT_CONNECTING = "-OnBlockedClientConnecting"
+SOFTAP_BLOCKING_CLIENT_REASON_KEY = "BlockedReason"
+SOFTAP_BLOCKING_CLIENT_WIFICLIENT_KEY = "WifiClient"
+SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0
+SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1
+
+# Callback Event for softap capability
+SOFTAP_CAPABILITY_CHANGED = "-OnCapabilityChanged"
+SOFTAP_CAPABILITY_MAX_SUPPORTED_CLIENTS = "maxSupportedClients"
+SOFTAP_CAPABILITY_FEATURE_ACS = "acsOffloadSupported"
+SOFTAP_CAPABILITY_FEATURE_CLIENT_CONTROL = "clientForceDisconnectSupported"
+SOFTAP_CAPABILITY_FEATURE_WPA3_SAE = "wpa3SaeSupported"
+
+DEFAULT_SOFTAP_TIMEOUT_S = 600 # 10 minutes
+
+# AP related constants
+AP_MAIN = "main_AP"
+AP_AUX = "aux_AP"
+SSID = "SSID"
+
+# cnss_diag property related constants
+DEVICES_USING_LEGACY_PROP = ["sailfish", "marlin", "walleye", "taimen", "muskie"]
+CNSS_DIAG_PROP = "persist.vendor.sys.cnss.diag_txt"
+LEGACY_CNSS_DIAG_PROP = "persist.sys.cnss.diag_txt"
+
+# Delay before registering the match callback.
+NETWORK_REQUEST_CB_REGISTER_DELAY_SEC = 2
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py
new file mode 100755
index 0000000..a79ff77
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import logging
+import pprint
+import requests
+import time
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts_contrib.test_utils.wifi import wifi_constants
+
+"""This file consists of all the helper methods needed to interact with the
+ Datastore @ https://chaos-188802.appspot.com/ used for Android Interop
+ testing.
+"""
+
+DATASTORE_HOST = "https://chaos-188802.appspot.com"
+
+# The Datastore defines the following paths for operating methods.
+ADD_DEVICE = "devices/new"
+REMOVE_DEVICE = "devices/delete"
+LOCK_DEVICE = "devices/lock"
+UNLOCK_DEVICE = "devices/unlock"
+SHOW_DEVICE = "devices/"
+GET_DEVICES = "devices/"
+
+# HTTP content type. JSON encoded with UTF-8 character encoding.
+HTTP_HEADER = {'content-type': 'application/json'}
+
+def add_device(name, ap_label, lab_label):
+ """Add a device(AP or Packet Capturer) in datastore.
+
+ Args:
+ name: string, hostname of the device.
+ ap_label: string, AP brand name.
+ lab_label: string, lab label for AP.
+ Returns:
+ True if device was added successfully; 0 otherwise.
+ """
+ request = DATASTORE_HOST + '/' + ADD_DEVICE
+ logging.debug("Request = %s" % request)
+ response = requests.post(request,
+ headers=HTTP_HEADER,
+ data=json.dumps({"hostname":name,
+ "ap_label":ap_label,
+ "lab_label":lab_label}))
+ if response.json()['result'] == 'success':
+ logging.info("Added device %s to datastore" % name)
+ return True
+ return False
+
+def remove_device(name):
+ """Delete a device(AP or Packet Capturer) in datastore.
+
+ Args:
+ name: string, hostname of the device to delete.
+ Returns:
+ True if device was deleted successfully; 0 otherwise.
+ """
+ request = DATASTORE_HOST + '/' + REMOVE_DEVICE
+ logging.debug("Request = %s" % request)
+ response = requests.put(request,
+ headers=HTTP_HEADER,
+ data=json.dumps({"hostname":name}))
+ result_str = "%s deleted." % name
+ if result_str in response.text:
+ logging.info("Removed device %s from datastore" % name)
+ return True
+ return False
+
+def lock_device(name, admin):
+ """Lock a device(AP or Packet Capturer) in datastore.
+
+ Args:
+ name: string, hostname of the device in datastore.
+ admin: string, unique admin name for locking.
+ Returns:
+ True if operation was successful; 0 otherwise.
+ """
+ request = DATASTORE_HOST + '/' + LOCK_DEVICE
+ logging.debug("Request = %s" % request)
+ response = requests.put(request,
+ headers=HTTP_HEADER,
+ data=json.dumps({"hostname":name, "locked_by":admin}))
+ if response.json()['result']:
+ logging.info("Locked device %s in datastore" % name)
+ return True
+ return False
+
+def unlock_device(name):
+ """Un-lock a device(AP or Packet Capturer) in datastore.
+
+ Args:
+ name: string, hostname of the device in datastore.
+ Returns:
+ True if operation was successful; 0 otherwise.
+ """
+ request = DATASTORE_HOST + '/' + UNLOCK_DEVICE
+ logging.debug("Request = %s" % request)
+ response = requests.put(request,
+ headers=HTTP_HEADER,
+ data=json.dumps({"hostname":name}))
+ if response.json()['result']:
+ logging.info("Finished un-locking AP %s in datastore" % name)
+ return True
+ return False
+
+def show_device(name):
+ """Show device properties for a given device(AP or Packet Capturer).
+
+ Args:
+ name: string, hostname of the device in datastore to fetch info.
+ Returns: dict of device name:value properties if successful;
+ None otherwise.
+ """
+ request = DATASTORE_HOST + '/' + SHOW_DEVICE + name
+ logging.debug("Request = %s" % request)
+ response = requests.get(request)
+ if 'error' in response.text:
+ return None
+ return response.json()
+
+def get_devices():
+ """Get a list of all devices in the datastore.
+
+ Returns: dict of all devices' name:value properties if successful;
+ None otherwise.
+ """
+ request = DATASTORE_HOST + '/' + GET_DEVICES
+ logging.debug("Request = %s" % request)
+ response = requests.get(request)
+ if 'error' in response.text:
+ return None
+ return response.json()
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py
new file mode 100644
index 0000000..88309ed
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py
@@ -0,0 +1,1379 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import bokeh, bokeh.plotting
+import collections
+import hashlib
+import itertools
+import json
+import logging
+import math
+import os
+import re
+import statistics
+import time
+from acts.controllers.android_device import AndroidDevice
+from acts.controllers.utils_lib import ssh
+from acts import utils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from concurrent.futures import ThreadPoolExecutor
+
+SHORT_SLEEP = 1
+MED_SLEEP = 6
+TEST_TIMEOUT = 10
+STATION_DUMP = 'iw wlan0 station dump'
+SCAN = 'wpa_cli scan'
+SCAN_RESULTS = 'wpa_cli scan_results'
+SIGNAL_POLL = 'wpa_cli signal_poll'
+WPA_CLI_STATUS = 'wpa_cli status'
+CONST_3dB = 3.01029995664
+RSSI_ERROR_VAL = float('nan')
+RTT_REGEX = re.compile(r'^\[(?P<timestamp>\S+)\] .*? time=(?P<rtt>\S+)')
+LOSS_REGEX = re.compile(r'(?P<loss>\S+)% packet loss')
+FW_REGEX = re.compile(r'FW:(?P<firmware>\S+) HW:')
+
+
+# Threading decorator
+def nonblocking(f):
+ """Creates a decorator transforming function calls to non-blocking"""
+ def wrap(*args, **kwargs):
+ executor = ThreadPoolExecutor(max_workers=1)
+ thread_future = executor.submit(f, *args, **kwargs)
+ # Ensure resources are freed up when executor ruturns or raises
+ executor.shutdown(wait=False)
+ return thread_future
+
+ return wrap
+
+
+# Link layer stats utilities
+class LinkLayerStats():
+
+ LLSTATS_CMD = 'cat /d/wlan0/ll_stats'
+ PEER_REGEX = 'LL_STATS_PEER_ALL'
+ MCS_REGEX = re.compile(
+ r'preamble: (?P<mode>\S+), nss: (?P<num_streams>\S+), bw: (?P<bw>\S+), '
+ 'mcs: (?P<mcs>\S+), bitrate: (?P<rate>\S+), txmpdu: (?P<txmpdu>\S+), '
+ 'rxmpdu: (?P<rxmpdu>\S+), mpdu_lost: (?P<mpdu_lost>\S+), '
+ 'retries: (?P<retries>\S+), retries_short: (?P<retries_short>\S+), '
+ 'retries_long: (?P<retries_long>\S+)')
+ MCS_ID = collections.namedtuple(
+ 'mcs_id', ['mode', 'num_streams', 'bandwidth', 'mcs', 'rate'])
+ MODE_MAP = {'0': '11a/g', '1': '11b', '2': '11n', '3': '11ac'}
+ BW_MAP = {'0': 20, '1': 40, '2': 80}
+
+ def __init__(self, dut, llstats_enabled=True):
+ self.dut = dut
+ self.llstats_enabled = llstats_enabled
+ self.llstats_cumulative = self._empty_llstats()
+ self.llstats_incremental = self._empty_llstats()
+
+ def update_stats(self):
+ if self.llstats_enabled:
+ try:
+ llstats_output = self.dut.adb.shell(self.LLSTATS_CMD,
+ timeout=0.1)
+ except:
+ llstats_output = ''
+ else:
+ llstats_output = ''
+ self._update_stats(llstats_output)
+
+ def reset_stats(self):
+ self.llstats_cumulative = self._empty_llstats()
+ self.llstats_incremental = self._empty_llstats()
+
+ def _empty_llstats(self):
+ return collections.OrderedDict(mcs_stats=collections.OrderedDict(),
+ summary=collections.OrderedDict())
+
+ def _empty_mcs_stat(self):
+ return collections.OrderedDict(txmpdu=0,
+ rxmpdu=0,
+ mpdu_lost=0,
+ retries=0,
+ retries_short=0,
+ retries_long=0)
+
+ def _mcs_id_to_string(self, mcs_id):
+ mcs_string = '{} {}MHz Nss{} MCS{} {}Mbps'.format(
+ mcs_id.mode, mcs_id.bandwidth, mcs_id.num_streams, mcs_id.mcs,
+ mcs_id.rate)
+ return mcs_string
+
+ def _parse_mcs_stats(self, llstats_output):
+ llstats_dict = {}
+ # Look for per-peer stats
+ match = re.search(self.PEER_REGEX, llstats_output)
+ if not match:
+ self.reset_stats()
+ return collections.OrderedDict()
+ # Find and process all matches for per stream stats
+ match_iter = re.finditer(self.MCS_REGEX, llstats_output)
+ for match in match_iter:
+ current_mcs = self.MCS_ID(self.MODE_MAP[match.group('mode')],
+ int(match.group('num_streams')) + 1,
+ self.BW_MAP[match.group('bw')],
+ int(match.group('mcs')),
+ int(match.group('rate'), 16) / 1000)
+ current_stats = collections.OrderedDict(
+ txmpdu=int(match.group('txmpdu')),
+ rxmpdu=int(match.group('rxmpdu')),
+ mpdu_lost=int(match.group('mpdu_lost')),
+ retries=int(match.group('retries')),
+ retries_short=int(match.group('retries_short')),
+ retries_long=int(match.group('retries_long')))
+ llstats_dict[self._mcs_id_to_string(current_mcs)] = current_stats
+ return llstats_dict
+
+ def _diff_mcs_stats(self, new_stats, old_stats):
+ stats_diff = collections.OrderedDict()
+ for stat_key in new_stats.keys():
+ stats_diff[stat_key] = new_stats[stat_key] - old_stats[stat_key]
+ return stats_diff
+
+ def _generate_stats_summary(self, llstats_dict):
+ llstats_summary = collections.OrderedDict(common_tx_mcs=None,
+ common_tx_mcs_count=0,
+ common_tx_mcs_freq=0,
+ common_rx_mcs=None,
+ common_rx_mcs_count=0,
+ common_rx_mcs_freq=0)
+ txmpdu_count = 0
+ rxmpdu_count = 0
+ for mcs_id, mcs_stats in llstats_dict['mcs_stats'].items():
+ if mcs_stats['txmpdu'] > llstats_summary['common_tx_mcs_count']:
+ llstats_summary['common_tx_mcs'] = mcs_id
+ llstats_summary['common_tx_mcs_count'] = mcs_stats['txmpdu']
+ if mcs_stats['rxmpdu'] > llstats_summary['common_rx_mcs_count']:
+ llstats_summary['common_rx_mcs'] = mcs_id
+ llstats_summary['common_rx_mcs_count'] = mcs_stats['rxmpdu']
+ txmpdu_count += mcs_stats['txmpdu']
+ rxmpdu_count += mcs_stats['rxmpdu']
+ if txmpdu_count:
+ llstats_summary['common_tx_mcs_freq'] = (
+ llstats_summary['common_tx_mcs_count'] / txmpdu_count)
+ if rxmpdu_count:
+ llstats_summary['common_rx_mcs_freq'] = (
+ llstats_summary['common_rx_mcs_count'] / rxmpdu_count)
+ return llstats_summary
+
+ def _update_stats(self, llstats_output):
+ # Parse stats
+ new_llstats = self._empty_llstats()
+ new_llstats['mcs_stats'] = self._parse_mcs_stats(llstats_output)
+ # Save old stats and set new cumulative stats
+ old_llstats = self.llstats_cumulative.copy()
+ self.llstats_cumulative = new_llstats.copy()
+ # Compute difference between new and old stats
+ self.llstats_incremental = self._empty_llstats()
+ for mcs_id, new_mcs_stats in new_llstats['mcs_stats'].items():
+ old_mcs_stats = old_llstats['mcs_stats'].get(
+ mcs_id, self._empty_mcs_stat())
+ self.llstats_incremental['mcs_stats'][
+ mcs_id] = self._diff_mcs_stats(new_mcs_stats, old_mcs_stats)
+ # Generate llstats summary
+ self.llstats_incremental['summary'] = self._generate_stats_summary(
+ self.llstats_incremental)
+ self.llstats_cumulative['summary'] = self._generate_stats_summary(
+ self.llstats_cumulative)
+
+
+# JSON serializer
+def serialize_dict(input_dict):
+ """Function to serialize dicts to enable JSON output"""
+ output_dict = collections.OrderedDict()
+ for key, value in input_dict.items():
+ output_dict[_serialize_value(key)] = _serialize_value(value)
+ return output_dict
+
+
+def _serialize_value(value):
+ """Function to recursively serialize dict entries to enable JSON output"""
+ if isinstance(value, tuple):
+ return str(value)
+ if isinstance(value, list):
+ return [_serialize_value(x) for x in value]
+ elif isinstance(value, dict):
+ return serialize_dict(value)
+ else:
+ return value
+
+
+# Plotting Utilities
+class BokehFigure():
+ """Class enabling simplified Bokeh plotting."""
+
+ COLORS = [
+ 'black',
+ 'blue',
+ 'blueviolet',
+ 'brown',
+ 'burlywood',
+ 'cadetblue',
+ 'cornflowerblue',
+ 'crimson',
+ 'cyan',
+ 'darkblue',
+ 'darkgreen',
+ 'darkmagenta',
+ 'darkorange',
+ 'darkred',
+ 'deepskyblue',
+ 'goldenrod',
+ 'green',
+ 'grey',
+ 'indigo',
+ 'navy',
+ 'olive',
+ 'orange',
+ 'red',
+ 'salmon',
+ 'teal',
+ 'yellow',
+ ]
+ MARKERS = [
+ 'asterisk', 'circle', 'circle_cross', 'circle_x', 'cross', 'diamond',
+ 'diamond_cross', 'hex', 'inverted_triangle', 'square', 'square_x',
+ 'square_cross', 'triangle', 'x'
+ ]
+
+ TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save')
+ TOOLTIPS = [
+ ('index', '$index'),
+ ('(x,y)', '($x, $y)'),
+ ('info', '@hover_text'),
+ ]
+
+ def __init__(self,
+ title=None,
+ x_label=None,
+ primary_y_label=None,
+ secondary_y_label=None,
+ height=700,
+ width=1100,
+ title_size='15pt',
+ axis_label_size='12pt',
+ json_file=None):
+ if json_file:
+ self.load_from_json(json_file)
+ else:
+ self.figure_data = []
+ self.fig_property = {
+ 'title': title,
+ 'x_label': x_label,
+ 'primary_y_label': primary_y_label,
+ 'secondary_y_label': secondary_y_label,
+ 'num_lines': 0,
+ 'height': height,
+ 'width': width,
+ 'title_size': title_size,
+ 'axis_label_size': axis_label_size
+ }
+
+ def init_plot(self):
+ self.plot = bokeh.plotting.figure(
+ sizing_mode='scale_both',
+ plot_width=self.fig_property['width'],
+ plot_height=self.fig_property['height'],
+ title=self.fig_property['title'],
+ tools=self.TOOLS,
+ output_backend='webgl')
+ self.plot.hover.tooltips = self.TOOLTIPS
+ self.plot.add_tools(
+ bokeh.models.tools.WheelZoomTool(dimensions='width'))
+ self.plot.add_tools(
+ bokeh.models.tools.WheelZoomTool(dimensions='height'))
+
+ def _filter_line(self, x_data, y_data, hover_text=None):
+ """Function to remove NaN points from bokeh plots."""
+ x_data_filtered = []
+ y_data_filtered = []
+ hover_text_filtered = []
+ for x, y, hover in itertools.zip_longest(x_data, y_data, hover_text):
+ if not math.isnan(y):
+ x_data_filtered.append(x)
+ y_data_filtered.append(y)
+ hover_text_filtered.append(hover)
+ return x_data_filtered, y_data_filtered, hover_text_filtered
+
+ def add_line(self,
+ x_data,
+ y_data,
+ legend,
+ hover_text=None,
+ color=None,
+ width=3,
+ style='solid',
+ marker=None,
+ marker_size=10,
+ shaded_region=None,
+ y_axis='default'):
+ """Function to add line to existing BokehFigure.
+
+ Args:
+ x_data: list containing x-axis values for line
+ y_data: list containing y_axis values for line
+ legend: string containing line title
+ hover_text: text to display when hovering over lines
+ color: string describing line color
+ width: integer line width
+ style: string describing line style, e.g, solid or dashed
+ marker: string specifying line marker, e.g., cross
+ shaded region: data describing shaded region to plot
+ y_axis: identifier for y-axis to plot line against
+ """
+ if y_axis not in ['default', 'secondary']:
+ raise ValueError('y_axis must be default or secondary')
+ if color == None:
+ color = self.COLORS[self.fig_property['num_lines'] %
+ len(self.COLORS)]
+ if style == 'dashed':
+ style = [5, 5]
+ if not hover_text:
+ hover_text = ['y={}'.format(y) for y in y_data]
+ x_data_filter, y_data_filter, hover_text_filter = self._filter_line(
+ x_data, y_data, hover_text)
+ self.figure_data.append({
+ 'x_data': x_data_filter,
+ 'y_data': y_data_filter,
+ 'legend': legend,
+ 'hover_text': hover_text_filter,
+ 'color': color,
+ 'width': width,
+ 'style': style,
+ 'marker': marker,
+ 'marker_size': marker_size,
+ 'shaded_region': shaded_region,
+ 'y_axis': y_axis
+ })
+ self.fig_property['num_lines'] += 1
+
+ def add_scatter(self,
+ x_data,
+ y_data,
+ legend,
+ hover_text=None,
+ color=None,
+ marker=None,
+ marker_size=10,
+ y_axis='default'):
+ """Function to add line to existing BokehFigure.
+
+ Args:
+ x_data: list containing x-axis values for line
+ y_data: list containing y_axis values for line
+ legend: string containing line title
+ hover_text: text to display when hovering over lines
+ color: string describing line color
+ marker: string specifying marker, e.g., cross
+ y_axis: identifier for y-axis to plot line against
+ """
+ if y_axis not in ['default', 'secondary']:
+ raise ValueError('y_axis must be default or secondary')
+ if color == None:
+ color = self.COLORS[self.fig_property['num_lines'] %
+ len(self.COLORS)]
+ if marker == None:
+ marker = self.MARKERS[self.fig_property['num_lines'] %
+ len(self.MARKERS)]
+ if not hover_text:
+ hover_text = ['y={}'.format(y) for y in y_data]
+ self.figure_data.append({
+ 'x_data': x_data,
+ 'y_data': y_data,
+ 'legend': legend,
+ 'hover_text': hover_text,
+ 'color': color,
+ 'width': 0,
+ 'style': 'solid',
+ 'marker': marker,
+ 'marker_size': marker_size,
+ 'shaded_region': None,
+ 'y_axis': y_axis
+ })
+ self.fig_property['num_lines'] += 1
+
+ def generate_figure(self, output_file=None, save_json=True):
+ """Function to generate and save BokehFigure.
+
+ Args:
+ output_file: string specifying output file path
+ """
+ self.init_plot()
+ two_axes = False
+ for line in self.figure_data:
+ source = bokeh.models.ColumnDataSource(
+ data=dict(x=line['x_data'],
+ y=line['y_data'],
+ hover_text=line['hover_text']))
+ if line['width'] > 0:
+ self.plot.line(x='x',
+ y='y',
+ legend_label=line['legend'],
+ line_width=line['width'],
+ color=line['color'],
+ line_dash=line['style'],
+ name=line['y_axis'],
+ y_range_name=line['y_axis'],
+ source=source)
+ if line['shaded_region']:
+ band_x = line['shaded_region']['x_vector']
+ band_x.extend(line['shaded_region']['x_vector'][::-1])
+ band_y = line['shaded_region']['lower_limit']
+ band_y.extend(line['shaded_region']['upper_limit'][::-1])
+ self.plot.patch(band_x,
+ band_y,
+ color='#7570B3',
+ line_alpha=0.1,
+ fill_alpha=0.1)
+ if line['marker'] in self.MARKERS:
+ marker_func = getattr(self.plot, line['marker'])
+ marker_func(x='x',
+ y='y',
+ size=line['marker_size'],
+ legend_label=line['legend'],
+ line_color=line['color'],
+ fill_color=line['color'],
+ name=line['y_axis'],
+ y_range_name=line['y_axis'],
+ source=source)
+ if line['y_axis'] == 'secondary':
+ two_axes = True
+
+ #x-axis formatting
+ self.plot.xaxis.axis_label = self.fig_property['x_label']
+ self.plot.x_range.range_padding = 0
+ self.plot.xaxis[0].axis_label_text_font_size = self.fig_property[
+ 'axis_label_size']
+ #y-axis formatting
+ self.plot.yaxis[0].axis_label = self.fig_property['primary_y_label']
+ self.plot.yaxis[0].axis_label_text_font_size = self.fig_property[
+ 'axis_label_size']
+ self.plot.y_range = bokeh.models.DataRange1d(names=['default'])
+ if two_axes and 'secondary' not in self.plot.extra_y_ranges:
+ self.plot.extra_y_ranges = {
+ 'secondary': bokeh.models.DataRange1d(names=['secondary'])
+ }
+ self.plot.add_layout(
+ bokeh.models.LinearAxis(
+ y_range_name='secondary',
+ axis_label=self.fig_property['secondary_y_label'],
+ axis_label_text_font_size=self.
+ fig_property['axis_label_size']), 'right')
+ # plot formatting
+ self.plot.legend.location = 'top_right'
+ self.plot.legend.click_policy = 'hide'
+ self.plot.title.text_font_size = self.fig_property['title_size']
+
+ if output_file is not None:
+ self.save_figure(output_file, save_json)
+ return self.plot
+
+ def load_from_json(self, file_path):
+ with open(file_path, 'r') as json_file:
+ fig_dict = json.load(json_file)
+ self.fig_property = fig_dict['fig_property']
+ self.figure_data = fig_dict['figure_data']
+
+ def _save_figure_json(self, output_file):
+ """Function to save a json format of a figure"""
+ figure_dict = collections.OrderedDict(fig_property=self.fig_property,
+ figure_data=self.figure_data)
+ output_file = output_file.replace('.html', '_plot_data.json')
+ with open(output_file, 'w') as outfile:
+ json.dump(figure_dict, outfile, indent=4)
+
+ def save_figure(self, output_file, save_json=True):
+ """Function to save BokehFigure.
+
+ Args:
+ output_file: string specifying output file path
+ save_json: flag controlling json outputs
+ """
+ bokeh.plotting.output_file(output_file)
+ bokeh.plotting.save(self.plot)
+ if save_json:
+ self._save_figure_json(output_file)
+
+ @staticmethod
+ def save_figures(figure_array, output_file_path, save_json=True):
+ """Function to save list of BokehFigures in one file.
+
+ Args:
+ figure_array: list of BokehFigure object to be plotted
+ output_file: string specifying output file path
+ """
+ for idx, figure in enumerate(figure_array):
+ figure.generate_figure()
+ if save_json:
+ json_file_path = output_file_path.replace(
+ '.html', '{}-plot_data.json'.format(idx))
+ figure._save_figure_json(json_file_path)
+ plot_array = [figure.plot for figure in figure_array]
+ all_plots = bokeh.layouts.column(children=plot_array,
+ sizing_mode='scale_width')
+ bokeh.plotting.output_file(output_file_path)
+ bokeh.plotting.save(all_plots)
+
+
+# Ping utilities
+class PingResult(object):
+ """An object that contains the results of running ping command.
+
+ Attributes:
+ connected: True if a connection was made. False otherwise.
+ packet_loss_percentage: The total percentage of packets lost.
+ transmission_times: The list of PingTransmissionTimes containing the
+ timestamps gathered for transmitted packets.
+ rtts: An list-like object enumerating all round-trip-times of
+ transmitted packets.
+ timestamps: A list-like object enumerating the beginning timestamps of
+ each packet transmission.
+ ping_interarrivals: A list-like object enumerating the amount of time
+ between the beginning of each subsequent transmission.
+ """
+ def __init__(self, ping_output):
+ self.packet_loss_percentage = 100
+ self.transmission_times = []
+
+ self.rtts = _ListWrap(self.transmission_times, lambda entry: entry.rtt)
+ self.timestamps = _ListWrap(self.transmission_times,
+ lambda entry: entry.timestamp)
+ self.ping_interarrivals = _PingInterarrivals(self.transmission_times)
+
+ self.start_time = 0
+ for line in ping_output:
+ if 'loss' in line:
+ match = re.search(LOSS_REGEX, line)
+ self.packet_loss_percentage = float(match.group('loss'))
+ if 'time=' in line:
+ match = re.search(RTT_REGEX, line)
+ if self.start_time == 0:
+ self.start_time = float(match.group('timestamp'))
+ self.transmission_times.append(
+ PingTransmissionTimes(
+ float(match.group('timestamp')) - self.start_time,
+ float(match.group('rtt'))))
+ self.connected = len(
+ ping_output) > 1 and self.packet_loss_percentage < 100
+
+ def __getitem__(self, item):
+ if item == 'rtt':
+ return self.rtts
+ if item == 'connected':
+ return self.connected
+ if item == 'packet_loss_percentage':
+ return self.packet_loss_percentage
+ raise ValueError('Invalid key. Please use an attribute instead.')
+
+ def as_dict(self):
+ return {
+ 'connected': 1 if self.connected else 0,
+ 'rtt': list(self.rtts),
+ 'time_stamp': list(self.timestamps),
+ 'ping_interarrivals': list(self.ping_interarrivals),
+ 'packet_loss_percentage': self.packet_loss_percentage
+ }
+
+
+class PingTransmissionTimes(object):
+ """A class that holds the timestamps for a packet sent via the ping command.
+
+ Attributes:
+ rtt: The round trip time for the packet sent.
+ timestamp: The timestamp the packet started its trip.
+ """
+ def __init__(self, timestamp, rtt):
+ self.rtt = rtt
+ self.timestamp = timestamp
+
+
+class _ListWrap(object):
+ """A convenient helper class for treating list iterators as native lists."""
+ def __init__(self, wrapped_list, func):
+ self.__wrapped_list = wrapped_list
+ self.__func = func
+
+ def __getitem__(self, key):
+ return self.__func(self.__wrapped_list[key])
+
+ def __iter__(self):
+ for item in self.__wrapped_list:
+ yield self.__func(item)
+
+ def __len__(self):
+ return len(self.__wrapped_list)
+
+
+class _PingInterarrivals(object):
+ """A helper class for treating ping interarrivals as a native list."""
+ def __init__(self, ping_entries):
+ self.__ping_entries = ping_entries
+
+ def __getitem__(self, key):
+ return (self.__ping_entries[key + 1].timestamp -
+ self.__ping_entries[key].timestamp)
+
+ def __iter__(self):
+ for index in range(len(self.__ping_entries) - 1):
+ yield self[index]
+
+ def __len__(self):
+ return max(0, len(self.__ping_entries) - 1)
+
+
+def get_ping_stats(src_device, dest_address, ping_duration, ping_interval,
+ ping_size):
+ """Run ping to or from the DUT.
+
+ The function computes either pings the DUT or pings a remote ip from
+ DUT.
+
+ Args:
+ src_device: object representing device to ping from
+ dest_address: ip address to ping
+ ping_duration: timeout to set on the the ping process (in seconds)
+ ping_interval: time between pings (in seconds)
+ ping_size: size of ping packet payload
+ Returns:
+ ping_result: dict containing ping results and other meta data
+ """
+ ping_count = int(ping_duration / ping_interval)
+ ping_deadline = int(ping_count * ping_interval) + 1
+ ping_cmd = 'ping -c {} -w {} -i {} -s {} -D'.format(
+ ping_count,
+ ping_deadline,
+ ping_interval,
+ ping_size,
+ )
+ if isinstance(src_device, AndroidDevice):
+ ping_cmd = '{} {}'.format(ping_cmd, dest_address)
+ ping_output = src_device.adb.shell(ping_cmd,
+ timeout=ping_deadline + SHORT_SLEEP,
+ ignore_status=True)
+ elif isinstance(src_device, ssh.connection.SshConnection):
+ ping_cmd = 'sudo {} {}'.format(ping_cmd, dest_address)
+ ping_output = src_device.run(ping_cmd,
+ timeout=ping_deadline + SHORT_SLEEP,
+ ignore_status=True).stdout
+ else:
+ raise TypeError('Unable to ping using src_device of type %s.' %
+ type(src_device))
+ return PingResult(ping_output.splitlines())
+
+
+@nonblocking
+def get_ping_stats_nb(src_device, dest_address, ping_duration, ping_interval,
+ ping_size):
+ return get_ping_stats(src_device, dest_address, ping_duration,
+ ping_interval, ping_size)
+
+
+# Iperf utilities
+@nonblocking
+def start_iperf_client_nb(iperf_client, iperf_server_address, iperf_args, tag,
+ timeout):
+ return iperf_client.start(iperf_server_address, iperf_args, tag, timeout)
+
+
+def get_iperf_arg_string(duration,
+ reverse_direction,
+ interval=1,
+ traffic_type='TCP',
+ socket_size=None,
+ num_processes=1,
+ udp_throughput='1000M',
+ ipv6=False):
+ """Function to format iperf client arguments.
+
+ This function takes in iperf client parameters and returns a properly
+ formatter iperf arg string to be used in throughput tests.
+
+ Args:
+ duration: iperf duration in seconds
+ reverse_direction: boolean controlling the -R flag for iperf clients
+ interval: iperf print interval
+ traffic_type: string specifying TCP or UDP traffic
+ socket_size: string specifying TCP window or socket buffer, e.g., 2M
+ num_processes: int specifying number of iperf processes
+ udp_throughput: string specifying TX throughput in UDP tests, e.g. 100M
+ ipv6: boolean controlling the use of IP V6
+ Returns:
+ iperf_args: string of formatted iperf args
+ """
+ iperf_args = '-i {} -t {} -J '.format(interval, duration)
+ if ipv6:
+ iperf_args = iperf_args + '-6 '
+ if traffic_type.upper() == 'UDP':
+ iperf_args = iperf_args + '-u -b {} -l 1400 -P {} '.format(
+ udp_throughput, num_processes)
+ elif traffic_type.upper() == 'TCP':
+ iperf_args = iperf_args + '-P {} '.format(num_processes)
+ if socket_size:
+ iperf_args = iperf_args + '-w {} '.format(socket_size)
+ if reverse_direction:
+ iperf_args = iperf_args + ' -R'
+ return iperf_args
+
+
+# Rssi Utilities
+def empty_rssi_result():
+ return collections.OrderedDict([('data', []), ('mean', None),
+ ('stdev', None)])
+
+
+def get_connected_rssi(dut,
+ num_measurements=1,
+ polling_frequency=SHORT_SLEEP,
+ first_measurement_delay=0,
+ disconnect_warning=True,
+ ignore_samples=0,
+ interface=None):
+ """Gets all RSSI values reported for the connected access point/BSSID.
+
+ Args:
+ dut: android device object from which to get RSSI
+ num_measurements: number of scans done, and RSSIs collected
+ polling_frequency: time to wait between RSSI measurements
+ disconnect_warning: boolean controlling disconnection logging messages
+ ignore_samples: number of leading samples to ignore
+ Returns:
+ connected_rssi: dict containing the measurements results for
+ all reported RSSI values (signal_poll, per chain, etc.) and their
+ statistics
+ """
+ # yapf: disable
+ connected_rssi = collections.OrderedDict(
+ [('time_stamp', []),
+ ('bssid', []), ('ssid', []), ('frequency', []),
+ ('signal_poll_rssi', empty_rssi_result()),
+ ('signal_poll_avg_rssi', empty_rssi_result()),
+ ('chain_0_rssi', empty_rssi_result()),
+ ('chain_1_rssi', empty_rssi_result())])
+ # yapf: enable
+ previous_bssid = 'disconnected'
+ t0 = time.time()
+ time.sleep(first_measurement_delay)
+ for idx in range(num_measurements):
+ measurement_start_time = time.time()
+ connected_rssi['time_stamp'].append(measurement_start_time - t0)
+ # Get signal poll RSSI
+ if interface is None:
+ status_output = dut.adb.shell(WPA_CLI_STATUS)
+ else:
+ status_output = dut.adb.shell(
+ 'wpa_cli -i {} status'.format(interface))
+ match = re.search('bssid=.*', status_output)
+ if match:
+ current_bssid = match.group(0).split('=')[1]
+ connected_rssi['bssid'].append(current_bssid)
+ else:
+ current_bssid = 'disconnected'
+ connected_rssi['bssid'].append(current_bssid)
+ if disconnect_warning and previous_bssid != 'disconnected':
+ logging.warning('WIFI DISCONNECT DETECTED!')
+ previous_bssid = current_bssid
+ match = re.search('\s+ssid=.*', status_output)
+ if match:
+ ssid = match.group(0).split('=')[1]
+ connected_rssi['ssid'].append(ssid)
+ else:
+ connected_rssi['ssid'].append('disconnected')
+ if interface is None:
+ signal_poll_output = dut.adb.shell(SIGNAL_POLL)
+ else:
+ signal_poll_output = dut.adb.shell(
+ 'wpa_cli -i {} signal_poll'.format(interface))
+ match = re.search('FREQUENCY=.*', signal_poll_output)
+ if match:
+ frequency = int(match.group(0).split('=')[1])
+ connected_rssi['frequency'].append(frequency)
+ else:
+ connected_rssi['frequency'].append(RSSI_ERROR_VAL)
+ match = re.search('RSSI=.*', signal_poll_output)
+ if match:
+ temp_rssi = int(match.group(0).split('=')[1])
+ if temp_rssi == -9999 or temp_rssi == 0:
+ connected_rssi['signal_poll_rssi']['data'].append(
+ RSSI_ERROR_VAL)
+ else:
+ connected_rssi['signal_poll_rssi']['data'].append(temp_rssi)
+ else:
+ connected_rssi['signal_poll_rssi']['data'].append(RSSI_ERROR_VAL)
+ match = re.search('AVG_RSSI=.*', signal_poll_output)
+ if match:
+ connected_rssi['signal_poll_avg_rssi']['data'].append(
+ int(match.group(0).split('=')[1]))
+ else:
+ connected_rssi['signal_poll_avg_rssi']['data'].append(
+ RSSI_ERROR_VAL)
+
+ # Get per chain RSSI
+ if interface is None:
+ per_chain_rssi = dut.adb.shell(STATION_DUMP)
+ else:
+ per_chain_rssi = ''
+ match = re.search('.*signal avg:.*', per_chain_rssi)
+ if match:
+ per_chain_rssi = per_chain_rssi[per_chain_rssi.find('[') +
+ 1:per_chain_rssi.find(']')]
+ per_chain_rssi = per_chain_rssi.split(', ')
+ connected_rssi['chain_0_rssi']['data'].append(
+ int(per_chain_rssi[0]))
+ connected_rssi['chain_1_rssi']['data'].append(
+ int(per_chain_rssi[1]))
+ else:
+ connected_rssi['chain_0_rssi']['data'].append(RSSI_ERROR_VAL)
+ connected_rssi['chain_1_rssi']['data'].append(RSSI_ERROR_VAL)
+ measurement_elapsed_time = time.time() - measurement_start_time
+ time.sleep(max(0, polling_frequency - measurement_elapsed_time))
+
+ # Compute mean RSSIs. Only average valid readings.
+ # Output RSSI_ERROR_VAL if no valid connected readings found.
+ for key, val in connected_rssi.copy().items():
+ if 'data' not in val:
+ continue
+ filtered_rssi_values = [x for x in val['data'] if not math.isnan(x)]
+ if len(filtered_rssi_values) > ignore_samples:
+ filtered_rssi_values = filtered_rssi_values[ignore_samples:]
+ if filtered_rssi_values:
+ connected_rssi[key]['mean'] = statistics.mean(filtered_rssi_values)
+ if len(filtered_rssi_values) > 1:
+ connected_rssi[key]['stdev'] = statistics.stdev(
+ filtered_rssi_values)
+ else:
+ connected_rssi[key]['stdev'] = 0
+ else:
+ connected_rssi[key]['mean'] = RSSI_ERROR_VAL
+ connected_rssi[key]['stdev'] = RSSI_ERROR_VAL
+ return connected_rssi
+
+
+@nonblocking
+def get_connected_rssi_nb(dut,
+ num_measurements=1,
+ polling_frequency=SHORT_SLEEP,
+ first_measurement_delay=0,
+ disconnect_warning=True,
+ ignore_samples=0,
+ interface=None):
+ return get_connected_rssi(dut, num_measurements, polling_frequency,
+ first_measurement_delay, disconnect_warning,
+ ignore_samples, interface)
+
+
+def get_scan_rssi(dut, tracked_bssids, num_measurements=1):
+ """Gets scan RSSI for specified BSSIDs.
+
+ Args:
+ dut: android device object from which to get RSSI
+ tracked_bssids: array of BSSIDs to gather RSSI data for
+ num_measurements: number of scans done, and RSSIs collected
+ Returns:
+ scan_rssi: dict containing the measurement results as well as the
+ statistics of the scan RSSI for all BSSIDs in tracked_bssids
+ """
+ scan_rssi = collections.OrderedDict()
+ for bssid in tracked_bssids:
+ scan_rssi[bssid] = empty_rssi_result()
+ for idx in range(num_measurements):
+ scan_output = dut.adb.shell(SCAN)
+ time.sleep(MED_SLEEP)
+ scan_output = dut.adb.shell(SCAN_RESULTS)
+ for bssid in tracked_bssids:
+ bssid_result = re.search(bssid + '.*',
+ scan_output,
+ flags=re.IGNORECASE)
+ if bssid_result:
+ bssid_result = bssid_result.group(0).split('\t')
+ scan_rssi[bssid]['data'].append(int(bssid_result[2]))
+ else:
+ scan_rssi[bssid]['data'].append(RSSI_ERROR_VAL)
+ # Compute mean RSSIs. Only average valid readings.
+ # Output RSSI_ERROR_VAL if no readings found.
+ for key, val in scan_rssi.items():
+ filtered_rssi_values = [x for x in val['data'] if not math.isnan(x)]
+ if filtered_rssi_values:
+ scan_rssi[key]['mean'] = statistics.mean(filtered_rssi_values)
+ if len(filtered_rssi_values) > 1:
+ scan_rssi[key]['stdev'] = statistics.stdev(
+ filtered_rssi_values)
+ else:
+ scan_rssi[key]['stdev'] = 0
+ else:
+ scan_rssi[key]['mean'] = RSSI_ERROR_VAL
+ scan_rssi[key]['stdev'] = RSSI_ERROR_VAL
+ return scan_rssi
+
+
+@nonblocking
+def get_scan_rssi_nb(dut, tracked_bssids, num_measurements=1):
+ return get_scan_rssi(dut, tracked_bssids, num_measurements)
+
+
+# Attenuator Utilities
+def atten_by_label(atten_list, path_label, atten_level):
+ """Attenuate signals according to their path label.
+
+ Args:
+ atten_list: list of attenuators to iterate over
+ path_label: path label on which to set desired attenuation
+ atten_level: attenuation desired on path
+ """
+ for atten in atten_list:
+ if path_label in atten.path:
+ atten.set_atten(atten_level)
+
+
+def get_atten_for_target_rssi(target_rssi, attenuators, dut, ping_server):
+ """Function to estimate attenuation to hit a target RSSI.
+
+ This function estimates a constant attenuation setting on all atennuation
+ ports to hit a target RSSI. The estimate is not meant to be exact or
+ guaranteed.
+
+ Args:
+ target_rssi: rssi of interest
+ attenuators: list of attenuator ports
+ dut: android device object assumed connected to a wifi network.
+ ping_server: ssh connection object to ping server
+ Returns:
+ target_atten: attenuation setting to achieve target_rssi
+ """
+ logging.info('Searching attenuation for RSSI = {}dB'.format(target_rssi))
+ # Set attenuator to 0 dB
+ for atten in attenuators:
+ atten.set_atten(0, strict=False)
+ # Start ping traffic
+ dut_ip = dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ # Measure starting RSSI
+ ping_future = get_ping_stats_nb(src_device=ping_server,
+ dest_address=dut_ip,
+ ping_duration=1.5,
+ ping_interval=0.02,
+ ping_size=64)
+ current_rssi = get_connected_rssi(dut,
+ num_measurements=4,
+ polling_frequency=0.25,
+ first_measurement_delay=0.5,
+ disconnect_warning=1,
+ ignore_samples=1)
+ current_rssi = current_rssi['signal_poll_rssi']['mean']
+ ping_future.result()
+ target_atten = 0
+ logging.debug("RSSI @ {0:.2f}dB attenuation = {1:.2f}".format(
+ target_atten, current_rssi))
+ within_range = 0
+ for idx in range(20):
+ atten_delta = max(min(current_rssi - target_rssi, 20), -20)
+ target_atten = int((target_atten + atten_delta) * 4) / 4
+ if target_atten < 0:
+ return 0
+ if target_atten > attenuators[0].get_max_atten():
+ return attenuators[0].get_max_atten()
+ for atten in attenuators:
+ atten.set_atten(target_atten, strict=False)
+ ping_future = get_ping_stats_nb(src_device=ping_server,
+ dest_address=dut_ip,
+ ping_duration=1.5,
+ ping_interval=0.02,
+ ping_size=64)
+ current_rssi = get_connected_rssi(dut,
+ num_measurements=4,
+ polling_frequency=0.25,
+ first_measurement_delay=0.5,
+ disconnect_warning=1,
+ ignore_samples=1)
+ current_rssi = current_rssi['signal_poll_rssi']['mean']
+ ping_future.result()
+ logging.info("RSSI @ {0:.2f}dB attenuation = {1:.2f}".format(
+ target_atten, current_rssi))
+ if abs(current_rssi - target_rssi) < 1:
+ if within_range:
+ logging.info(
+ 'Reached RSSI: {0:.2f}. Target RSSI: {1:.2f}.'
+ 'Attenuation: {2:.2f}, Iterations = {3:.2f}'.format(
+ current_rssi, target_rssi, target_atten, idx))
+ return target_atten
+ else:
+ within_range = True
+ else:
+ within_range = False
+ return target_atten
+
+
+def get_current_atten_dut_chain_map(attenuators, dut, ping_server):
+ """Function to detect mapping between attenuator ports and DUT chains.
+
+ This function detects the mapping between attenuator ports and DUT chains
+ in cases where DUT chains are connected to only one attenuator port. The
+ function assumes the DUT is already connected to a wifi network. The
+ function starts by measuring per chain RSSI at 0 attenuation, then
+ attenuates one port at a time looking for the chain that reports a lower
+ RSSI.
+
+ Args:
+ attenuators: list of attenuator ports
+ dut: android device object assumed connected to a wifi network.
+ ping_server: ssh connection object to ping server
+ Returns:
+ chain_map: list of dut chains, one entry per attenuator port
+ """
+ # Set attenuator to 0 dB
+ for atten in attenuators:
+ atten.set_atten(0, strict=False)
+ # Start ping traffic
+ dut_ip = dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ ping_future = get_ping_stats_nb(ping_server, dut_ip, 11, 0.02, 64)
+ # Measure starting RSSI
+ base_rssi = get_connected_rssi(dut, 4, 0.25, 1)
+ chain0_base_rssi = base_rssi['chain_0_rssi']['mean']
+ chain1_base_rssi = base_rssi['chain_1_rssi']['mean']
+ if chain0_base_rssi < -70 or chain1_base_rssi < -70:
+ logging.warning('RSSI might be too low to get reliable chain map.')
+ # Compile chain map by attenuating one path at a time and seeing which
+ # chain's RSSI degrades
+ chain_map = []
+ for test_atten in attenuators:
+ # Set one attenuator to 30 dB down
+ test_atten.set_atten(30, strict=False)
+ # Get new RSSI
+ test_rssi = get_connected_rssi(dut, 4, 0.25, 1)
+ # Assign attenuator to path that has lower RSSI
+ if chain0_base_rssi > -70 and chain0_base_rssi - test_rssi[
+ 'chain_0_rssi']['mean'] > 10:
+ chain_map.append('DUT-Chain-0')
+ elif chain1_base_rssi > -70 and chain1_base_rssi - test_rssi[
+ 'chain_1_rssi']['mean'] > 10:
+ chain_map.append('DUT-Chain-1')
+ else:
+ chain_map.append(None)
+ # Reset attenuator to 0
+ test_atten.set_atten(0, strict=False)
+ ping_future.result()
+ logging.debug('Chain Map: {}'.format(chain_map))
+ return chain_map
+
+
+def get_full_rf_connection_map(attenuators, dut, ping_server, networks):
+ """Function to detect per-network connections between attenuator and DUT.
+
+ This function detects the mapping between attenuator ports and DUT chains
+ on all networks in its arguments. The function connects the DUT to each
+ network then calls get_current_atten_dut_chain_map to get the connection
+ map on the current network. The function outputs the results in two formats
+ to enable easy access when users are interested in indexing by network or
+ attenuator port.
+
+ Args:
+ attenuators: list of attenuator ports
+ dut: android device object assumed connected to a wifi network.
+ ping_server: ssh connection object to ping server
+ networks: dict of network IDs and configs
+ Returns:
+ rf_map_by_network: dict of RF connections indexed by network.
+ rf_map_by_atten: list of RF connections indexed by attenuator
+ """
+ for atten in attenuators:
+ atten.set_atten(0, strict=False)
+
+ rf_map_by_network = collections.OrderedDict()
+ rf_map_by_atten = [[] for atten in attenuators]
+ for net_id, net_config in networks.items():
+ wutils.reset_wifi(dut)
+ wutils.wifi_connect(dut,
+ net_config,
+ num_of_tries=1,
+ assert_on_fail=False,
+ check_connectivity=False)
+ rf_map_by_network[net_id] = get_current_atten_dut_chain_map(
+ attenuators, dut, ping_server)
+ for idx, chain in enumerate(rf_map_by_network[net_id]):
+ if chain:
+ rf_map_by_atten[idx].append({
+ "network": net_id,
+ "dut_chain": chain
+ })
+ logging.debug("RF Map (by Network): {}".format(rf_map_by_network))
+ logging.debug("RF Map (by Atten): {}".format(rf_map_by_atten))
+
+ return rf_map_by_network, rf_map_by_atten
+
+
+# Miscellaneous Wifi Utilities
+def extract_sub_dict(full_dict, fields):
+ sub_dict = collections.OrderedDict(
+ (field, full_dict[field]) for field in fields)
+ return sub_dict
+
+
+def validate_network(dut, ssid):
+ """Check that DUT has a valid internet connection through expected SSID
+
+ Args:
+ dut: android device of interest
+ ssid: expected ssid
+ """
+ current_network = dut.droid.wifiGetConnectionInfo()
+ try:
+ connected = wutils.validate_connection(dut) is not None
+ except:
+ connected = False
+ if connected and current_network['SSID'] == ssid:
+ return True
+ else:
+ return False
+
+
+def get_server_address(ssh_connection, dut_ip, subnet_mask):
+ """Get server address on a specific subnet,
+
+ This function retrieves the LAN IP of a remote machine used in testing,
+ i.e., it returns the server's IP belonging to the same LAN as the DUT.
+
+ Args:
+ ssh_connection: object representing server for which we want an ip
+ dut_ip: string in ip address format, i.e., xxx.xxx.xxx.xxx, specifying
+ the DUT LAN IP we wish to connect to
+ subnet_mask: string representing subnet mask
+ """
+ subnet_mask = subnet_mask.split('.')
+ dut_subnet = [
+ int(dut) & int(subnet)
+ for dut, subnet in zip(dut_ip.split('.'), subnet_mask)
+ ]
+ ifconfig_out = ssh_connection.run('ifconfig').stdout
+ ip_list = re.findall('inet (?:addr:)?(\d+.\d+.\d+.\d+)', ifconfig_out)
+ for current_ip in ip_list:
+ current_subnet = [
+ int(ip) & int(subnet)
+ for ip, subnet in zip(current_ip.split('.'), subnet_mask)
+ ]
+ if current_subnet == dut_subnet:
+ return current_ip
+ logging.error('No IP address found in requested subnet')
+
+
+def get_dut_temperature(dut):
+ """Function to get dut temperature.
+
+ The function fetches and returns the reading from the temperature sensor
+ used for skin temperature and thermal throttling.
+
+ Args:
+ dut: AndroidDevice of interest
+ Returns:
+ temperature: device temperature. 0 if temperature could not be read
+ """
+ candidate_zones = [
+ 'skin-therm', 'sdm-therm-monitor', 'sdm-therm-adc', 'back_therm'
+ ]
+ for zone in candidate_zones:
+ try:
+ temperature = int(
+ dut.adb.shell(
+ 'cat /sys/class/thermal/tz-by-name/{}/temp'.format(zone)))
+ break
+ except ValueError:
+ temperature = 0
+ if temperature == 0:
+ logging.debug('Could not check DUT temperature.')
+ elif temperature > 100:
+ temperature = temperature / 1000
+ return temperature
+
+
+def wait_for_dut_cooldown(dut, target_temp=50, timeout=300):
+ """Function to wait for a DUT to cool down.
+
+ Args:
+ dut: AndroidDevice of interest
+ target_temp: target cooldown temperature
+ timeout: maxt time to wait for cooldown
+ """
+ start_time = time.time()
+ while time.time() - start_time < timeout:
+ temperature = get_dut_temperature(dut)
+ if temperature < target_temp:
+ break
+ time.sleep(SHORT_SLEEP)
+ elapsed_time = time.time() - start_time
+ logging.debug("DUT Final Temperature: {}C. Cooldown duration: {}".format(
+ temperature, elapsed_time))
+
+
+def health_check(dut, batt_thresh=5, temp_threshold=53, cooldown=1):
+ """Function to check health status of a DUT.
+
+ The function checks both battery levels and temperature to avoid DUT
+ powering off during the test.
+
+ Args:
+ dut: AndroidDevice of interest
+ batt_thresh: battery level threshold
+ temp_threshold: temperature threshold
+ cooldown: flag to wait for DUT to cool down when overheating
+ Returns:
+ health_check: boolean confirming device is healthy
+ """
+ health_check = True
+ battery_level = utils.get_battery_level(dut)
+ if battery_level < batt_thresh:
+ logging.warning("Battery level low ({}%)".format(battery_level))
+ health_check = False
+ else:
+ logging.debug("Battery level = {}%".format(battery_level))
+
+ temperature = get_dut_temperature(dut)
+ if temperature > temp_threshold:
+ if cooldown:
+ logging.warning(
+ "Waiting for DUT to cooldown. ({} C)".format(temperature))
+ wait_for_dut_cooldown(dut, target_temp=temp_threshold - 5)
+ else:
+ logging.warning("DUT Overheating ({} C)".format(temperature))
+ health_check = False
+ else:
+ logging.debug("DUT Temperature = {} C".format(temperature))
+ return health_check
+
+
+def get_sw_signature(dut):
+ """Function that checks the signature for wifi firmware and config files.
+
+ Returns:
+ bdf_signature: signature consisting of last three digits of bdf cksums
+ fw_signature: floating point firmware version, i.e., major.minor
+ """
+ bdf_output = dut.adb.shell('cksum /vendor/firmware/bdwlan*')
+ logging.debug('BDF Checksum output: {}'.format(bdf_output))
+ bdf_signature = sum(
+ [int(line.split(' ')[0]) for line in bdf_output.splitlines()]) % 1000
+
+ fw_output = dut.adb.shell('halutil -logger -get fw')
+ logging.debug('Firmware version output: {}'.format(fw_output))
+ fw_version = re.search(FW_REGEX, fw_output).group('firmware')
+ fw_signature = fw_version.split('.')[-3:-1]
+ fw_signature = float('.'.join(fw_signature))
+ serial_hash = int(hashlib.md5(dut.serial.encode()).hexdigest(), 16) % 1000
+ return {
+ 'bdf_signature': bdf_signature,
+ 'fw_signature': fw_signature,
+ 'serial_hash': serial_hash
+ }
+
+
+def push_bdf(dut, bdf_file):
+ """Function to push Wifi BDF files
+
+ This function checks for existing wifi bdf files and over writes them all,
+ for simplicity, with the bdf file provided in the arguments. The dut is
+ rebooted for the bdf file to take effect
+
+ Args:
+ dut: dut to push bdf file to
+ bdf_file: path to bdf_file to push
+ """
+ bdf_files_list = dut.adb.shell('ls /vendor/firmware/bdwlan*').splitlines()
+ for dst_file in bdf_files_list:
+ dut.push_system_file(bdf_file, dst_file)
+ dut.reboot()
+
+
+def push_firmware(dut, wlanmdsp_file, datamsc_file):
+ """Function to push Wifi firmware files
+
+ Args:
+ dut: dut to push bdf file to
+ wlanmdsp_file: path to wlanmdsp.mbn file
+ datamsc_file: path to Data.msc file
+ """
+ dut.push_system_file(wlanmdsp_file, '/vendor/firmware/wlanmdsp.mbn')
+ dut.push_system_file(datamsc_file, '/vendor/firmware/Data.msc')
+ dut.reboot()
+
+
+def _set_ini_fields(ini_file_path, ini_field_dict):
+ template_regex = r'^{}=[0-9,.x-]+'
+ with open(ini_file_path, 'r') as f:
+ ini_lines = f.read().splitlines()
+ for idx, line in enumerate(ini_lines):
+ for field_name, field_value in ini_field_dict.items():
+ line_regex = re.compile(template_regex.format(field_name))
+ if re.match(line_regex, line):
+ ini_lines[idx] = "{}={}".format(field_name, field_value)
+ print(ini_lines[idx])
+ with open(ini_file_path, 'w') as f:
+ f.write("\n".join(ini_lines) + "\n")
+
+
+def _edit_dut_ini(dut, ini_fields):
+ """Function to edit Wifi ini files."""
+ dut_ini_path = '/vendor/firmware/wlan/qca_cld/WCNSS_qcom_cfg.ini'
+ local_ini_path = os.path.expanduser('~/WCNSS_qcom_cfg.ini')
+ dut.pull_files(dut_ini_path, local_ini_path)
+
+ _set_ini_fields(local_ini_path, ini_fields)
+
+ dut.push_system_file(local_ini_path, dut_ini_path)
+ dut.reboot()
+
+
+def set_ini_single_chain_mode(dut, chain):
+ ini_fields = {
+ 'gEnable2x2': 0,
+ 'gSetTxChainmask1x1': chain + 1,
+ 'gSetRxChainmask1x1': chain + 1,
+ 'gDualMacFeatureDisable': 1,
+ 'gDot11Mode': 0
+ }
+ _edit_dut_ini(dut, ini_fields)
+
+
+def set_ini_two_chain_mode(dut):
+ ini_fields = {
+ 'gEnable2x2': 2,
+ 'gSetTxChainmask1x1': 1,
+ 'gSetRxChainmask1x1': 1,
+ 'gDualMacFeatureDisable': 6,
+ 'gDot11Mode': 0
+ }
+ _edit_dut_ini(dut, ini_fields)
+
+
+def set_ini_tx_mode(dut, mode):
+ TX_MODE_DICT = {
+ "Auto": 0,
+ "11n": 4,
+ "11ac": 9,
+ "11abg": 1,
+ "11b": 2,
+ "11g": 3,
+ "11g only": 5,
+ "11n only": 6,
+ "11b only": 7,
+ "11ac only": 8
+ }
+
+ ini_fields = {
+ 'gEnable2x2': 2,
+ 'gSetTxChainmask1x1': 1,
+ 'gSetRxChainmask1x1': 1,
+ 'gDualMacFeatureDisable': 6,
+ 'gDot11Mode': TX_MODE_DICT[mode]
+ }
+ _edit_dut_ini(dut, ini_fields)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py
new file mode 100644
index 0000000..b1565d2
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+from acts import utils
+from acts.libs.proc import job
+from acts.controllers.ap_lib import bridge_interface as bi
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts.controllers.ap_lib import hostapd_security
+from acts.controllers.ap_lib import hostapd_ap_preset
+
+# http://www.secdev.org/projects/scapy/
+# On ubuntu, sudo pip3 install scapy
+import scapy.all as scapy
+
+GET_FROM_PHONE = 'get_from_dut'
+GET_FROM_AP = 'get_from_ap'
+ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
+MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
+
+
+def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10):
+ """Function to change the DTIM setting in the phone.
+
+ Args:
+ ad: the target android device, AndroidDevice object
+ gEnableModulatedDTIM: Modulated DTIM, int
+ gMaxLIModulatedDTIM: Maximum modulated DTIM, int
+ """
+ # First trying to find the ini file with DTIM settings
+ ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini')
+ ini_file_local = ini_file_phone.split('/')[-1]
+
+ # Pull the file and change the DTIM to desired value
+ ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local))
+
+ with open(ini_file_local, 'r') as fin:
+ for line in fin:
+ if ENABLED_MODULATED_DTIM in line:
+ gE_old = line.strip('\n')
+ gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n')
+ if MAX_MODULATED_DTIM in line:
+ gM_old = line.strip('\n')
+ gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n')
+ fin.close()
+ if int(gEDTIM_old) == gEnableModulatedDTIM and int(
+ gMDTIM_old) == gMaxLIModulatedDTIM:
+ ad.log.info('Current DTIM is already the desired value,'
+ 'no need to reset it')
+ return 0
+
+ gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM)
+ gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM)
+
+ sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local)
+ sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local)
+ job.run(sed_gE)
+ job.run(sed_gM)
+
+ # Push the file to the phone
+ push_file_to_phone(ad, ini_file_local, ini_file_phone)
+ ad.log.info('DTIM changes checked in and rebooting...')
+ ad.reboot()
+ # Wait for auto-wifi feature to start
+ time.sleep(20)
+ ad.adb.shell('dumpsys battery set level 100')
+ ad.log.info('DTIM updated and device back from reboot')
+ return 1
+
+
+def push_file_to_phone(ad, file_local, file_phone):
+ """Function to push local file to android phone.
+
+ Args:
+ ad: the target android device
+ file_local: the locla file to push
+ file_phone: the file/directory on the phone to be pushed
+ """
+ ad.adb.root()
+ cmd_out = ad.adb.remount()
+ if 'Permission denied' in cmd_out:
+ ad.log.info('Need to disable verity first and reboot')
+ ad.adb.disable_verity()
+ time.sleep(1)
+ ad.reboot()
+ ad.log.info('Verity disabled and device back from reboot')
+ ad.adb.root()
+ ad.adb.remount()
+ time.sleep(1)
+ ad.adb.push('{} {}'.format(file_local, file_phone))
+
+
+def ap_setup(ap, network, bandwidth=80):
+ """Set up the whirlwind AP with provided network info.
+
+ Args:
+ ap: access_point object of the AP
+ network: dict with information of the network, including ssid, password
+ bssid, channel etc.
+ bandwidth: the operation bandwidth for the AP, default 80MHz
+ Returns:
+ brconfigs: the bridge interface configs
+ """
+ log = logging.getLogger()
+ bss_settings = []
+ ssid = network[wutils.WifiEnums.SSID_KEY]
+ if "password" in network.keys():
+ password = network["password"]
+ security = hostapd_security.Security(
+ security_mode="wpa", password=password)
+ else:
+ security = hostapd_security.Security(security_mode=None, password=None)
+ channel = network["channel"]
+ config = hostapd_ap_preset.create_ap_preset(
+ channel=channel,
+ ssid=ssid,
+ security=security,
+ bss_settings=bss_settings,
+ vht_bandwidth=bandwidth,
+ profile_name='whirlwind',
+ iface_wlan_2g=ap.wlan_2g,
+ iface_wlan_5g=ap.wlan_5g)
+ config_bridge = ap.generate_bridge_configs(channel)
+ brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1],
+ config_bridge[2])
+ ap.bridge.startup(brconfigs)
+ ap.start_ap(config)
+ log.info("AP started on channel {} with SSID {}".format(channel, ssid))
+ return brconfigs
+
+
+def run_iperf_client_nonblocking(ad, server_host, extra_args=""):
+ """Start iperf client on the device with nohup.
+
+ Return status as true if iperf client start successfully.
+ And data flow information as results.
+
+ Args:
+ ad: the android device under test
+ server_host: Address of the iperf server.
+ extra_args: A string representing extra arguments for iperf client,
+ e.g. "-i 1 -t 30".
+
+ """
+ log = logging.getLogger()
+ ad.adb.shell_nb("nohup >/dev/null 2>&1 sh -c 'iperf3 -c {} {} &'".format(
+ server_host, extra_args))
+ log.info("IPerf client started")
+
+
+def get_wifi_rssi(ad):
+ """Get the RSSI of the device.
+
+ Args:
+ ad: the android device under test
+ Returns:
+ RSSI: the rssi level of the device
+ """
+ RSSI = ad.droid.wifiGetConnectionInfo()['rssi']
+ return RSSI
+
+
+def get_phone_ip(ad):
+ """Get the WiFi IP address of the phone.
+
+ Args:
+ ad: the android device under test
+ Returns:
+ IP: IP address of the phone for WiFi, as a string
+ """
+ IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ return IP
+
+
+def get_phone_mac(ad):
+ """Get the WiFi MAC address of the phone.
+
+ Args:
+ ad: the android device under test
+ Returns:
+ mac: MAC address of the phone for WiFi, as a string
+ """
+ mac = ad.droid.wifiGetConnectionInfo()["mac_address"]
+
+ return mac
+
+
+def get_phone_ipv6(ad):
+ """Get the WiFi IPV6 address of the phone.
+
+ Args:
+ ad: the android device under test
+ Returns:
+ IPv6: IPv6 address of the phone for WiFi, as a string
+ """
+ IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6]
+
+ return IPv6
+
+
+def wait_for_dhcp(interface_name):
+ """Wait the DHCP address assigned to desired interface.
+
+ Getting DHCP address takes time and the wait time isn't constant. Utilizing
+ utils.timeout to keep trying until success
+
+ Args:
+ interface_name: desired interface name
+ Returns:
+ ip: ip address of the desired interface name
+ Raise:
+ TimeoutError: After timeout, if no DHCP assigned, raise
+ """
+ log = logging.getLogger()
+ reset_host_interface(interface_name)
+ start_time = time.time()
+ time_limit_seconds = 60
+ ip = '0.0.0.0'
+ while start_time + time_limit_seconds > time.time():
+ ip = scapy.get_if_addr(interface_name)
+ if ip == '0.0.0.0':
+ time.sleep(1)
+ else:
+ log.info(
+ 'DHCP address assigned to %s as %s' % (interface_name, ip))
+ return ip
+ raise TimeoutError('Timed out while getting if_addr after %s seconds.' %
+ time_limit_seconds)
+
+
+def reset_host_interface(intferface_name):
+ """Reset the host interface.
+
+ Args:
+ intferface_name: the desired interface to reset
+ """
+ log = logging.getLogger()
+ intf_down_cmd = 'ifconfig %s down' % intferface_name
+ intf_up_cmd = 'ifconfig %s up' % intferface_name
+ try:
+ job.run(intf_down_cmd)
+ time.sleep(10)
+ job.run(intf_up_cmd)
+ log.info('{} has been reset'.format(intferface_name))
+ except job.Error:
+ raise Exception('No such interface')
+
+
+def bringdown_host_interface(intferface_name):
+ """Reset the host interface.
+
+ Args:
+ intferface_name: the desired interface to reset
+ """
+ log = logging.getLogger()
+ intf_down_cmd = 'ifconfig %s down' % intferface_name
+ try:
+ job.run(intf_down_cmd)
+ time.sleep(2)
+ log.info('{} has been brought down'.format(intferface_name))
+ except job.Error:
+ raise Exception('No such interface')
+
+
+def create_pkt_config(test_class):
+ """Creates the config for generating multicast packets
+
+ Args:
+ test_class: object with all networking paramters
+
+ Returns:
+ Dictionary with the multicast packet config
+ """
+ addr_type = (scapy.IPV6_ADDR_LINKLOCAL
+ if test_class.ipv6_src_type == 'LINK_LOCAL' else
+ scapy.IPV6_ADDR_GLOBAL)
+
+ mac_dst = test_class.mac_dst
+ if GET_FROM_PHONE in test_class.mac_dst:
+ mac_dst = get_phone_mac(test_class.dut)
+
+ ipv4_dst = test_class.ipv4_dst
+ if GET_FROM_PHONE in test_class.ipv4_dst:
+ ipv4_dst = get_phone_ip(test_class.dut)
+
+ ipv6_dst = test_class.ipv6_dst
+ if GET_FROM_PHONE in test_class.ipv6_dst:
+ ipv6_dst = get_phone_ipv6(test_class.dut)
+
+ ipv4_gw = test_class.ipv4_gwt
+ if GET_FROM_AP in test_class.ipv4_gwt:
+ ipv4_gw = test_class.access_point.ssh_settings.hostname
+
+ pkt_gen_config = {
+ 'interf': test_class.pkt_sender.interface,
+ 'subnet_mask': test_class.sub_mask,
+ 'src_mac': test_class.mac_src,
+ 'dst_mac': mac_dst,
+ 'src_ipv4': test_class.ipv4_src,
+ 'dst_ipv4': ipv4_dst,
+ 'src_ipv6': test_class.ipv6_src,
+ 'src_ipv6_type': addr_type,
+ 'dst_ipv6': ipv6_dst,
+ 'gw_ipv4': ipv4_gw
+ }
+ return pkt_gen_config
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap.py
new file mode 100644
index 0000000..adbceed
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap.py
@@ -0,0 +1,1433 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import fcntl
+import os
+import selenium
+import splinter
+import time
+from acts import logger
+from acts.controllers import access_point
+from acts.controllers.ap_lib import bridge_interface
+from acts.controllers.ap_lib import hostapd_security
+from acts.controllers.ap_lib import hostapd_ap_preset
+
+BROWSER_WAIT_SHORT = 1
+BROWSER_WAIT_MED = 3
+BROWSER_WAIT_LONG = 30
+BROWSER_WAIT_EXTRA_LONG = 60
+
+
+def create(configs):
+ """Factory method for retail AP class.
+
+ Args:
+ configs: list of dicts containing ap settings. ap settings must contain
+ the following: brand, model, ip_address, username and password
+ """
+ SUPPORTED_APS = {
+ ("Netgear", "R7000"): "NetgearR7000AP",
+ ("Netgear", "R7000NA"): "NetgearR7000NAAP",
+ ("Netgear", "R7500"): "NetgearR7500AP",
+ ("Netgear", "R7800"): "NetgearR7800AP",
+ ("Netgear", "R8000"): "NetgearR8000AP",
+ ("Netgear", "R8500"): "NetgearR8500AP",
+ ("Netgear", "RAX"): "NetgearRAXAP",
+ ("Google", "Wifi"): "GoogleWifiAP"
+ }
+ objs = []
+ for config in configs:
+ try:
+ ap_class_name = SUPPORTED_APS[(config["brand"], config["model"])]
+ ap_class = globals()[ap_class_name]
+ except KeyError:
+ raise KeyError("Invalid retail AP brand and model combination.")
+ objs.append(ap_class(config))
+ return objs
+
+
+def detroy(objs):
+ for obj in objs:
+ obj.teardown()
+
+
+class BlockingBrowser(splinter.driver.webdriver.chrome.WebDriver):
+ """Class that implements a blocking browser session on top of selenium.
+
+ The class inherits from and builds upon splinter/selenium's webdriver class
+ and makes sure that only one such webdriver is active on a machine at any
+ single time. The class ensures single session operation using a lock file.
+ The class is to be used within context managers (e.g. with statements) to
+ ensure locks are always properly released.
+ """
+ def __init__(self, headless, timeout):
+ """Constructor for BlockingBrowser class.
+
+ Args:
+ headless: boolean to control visible/headless browser operation
+ timeout: maximum time allowed to launch browser
+ """
+ self.log = logger.create_tagged_trace_logger("ChromeDriver")
+ self.chrome_options = splinter.driver.webdriver.chrome.Options()
+ self.chrome_options.add_argument("--no-proxy-server")
+ self.chrome_options.add_argument("--no-sandbox")
+ self.chrome_options.add_argument("--allow-running-insecure-content")
+ self.chrome_options.add_argument("--ignore-certificate-errors")
+ self.chrome_capabilities = selenium.webdriver.common.desired_capabilities.DesiredCapabilities.CHROME.copy(
+ )
+ self.chrome_capabilities["acceptSslCerts"] = True
+ self.chrome_capabilities["acceptInsecureCerts"] = True
+ if headless:
+ self.chrome_options.add_argument("--headless")
+ self.chrome_options.add_argument("--disable-gpu")
+ self.lock_file_path = "/usr/local/bin/chromedriver"
+ self.timeout = timeout
+
+ def __enter__(self):
+ """Entry context manager for BlockingBrowser.
+
+ The enter context manager for BlockingBrowser attempts to lock the
+ browser file. If successful, it launches and returns a chromedriver
+ session. If an exception occurs while starting the browser, the lock
+ file is released.
+ """
+ self.lock_file = open(self.lock_file_path, "r")
+ start_time = time.time()
+ while time.time() < start_time + self.timeout:
+ try:
+ fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except BlockingIOError:
+ time.sleep(BROWSER_WAIT_SHORT)
+ continue
+ try:
+ self.driver = selenium.webdriver.Chrome(
+ options=self.chrome_options,
+ desired_capabilities=self.chrome_capabilities)
+ self.element_class = splinter.driver.webdriver.WebDriverElement
+ self._cookie_manager = splinter.driver.webdriver.cookie_manager.CookieManager(
+ self.driver)
+ super(splinter.driver.webdriver.chrome.WebDriver,
+ self).__init__(2)
+ return super(BlockingBrowser, self).__enter__()
+ except:
+ fcntl.flock(self.lock_file, fcntl.LOCK_UN)
+ self.lock_file.close()
+ raise RuntimeError("Error starting browser. "
+ "Releasing lock file.")
+ raise TimeoutError("Could not start chrome browser in time.")
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """Exit context manager for BlockingBrowser.
+
+ The exit context manager simply calls the parent class exit and
+ releases the lock file.
+ """
+ try:
+ super(BlockingBrowser, self).__exit__(exc_type, exc_value,
+ traceback)
+ except:
+ raise RuntimeError("Failed to quit browser. Releasing lock file.")
+ finally:
+ fcntl.flock(self.lock_file, fcntl.LOCK_UN)
+ self.lock_file.close()
+
+ def restart(self):
+ """Method to restart browser session without releasing lock file."""
+ self.quit()
+ self.__enter__()
+
+ def visit_persistent(self,
+ url,
+ page_load_timeout,
+ num_tries,
+ backup_url="about:blank",
+ check_for_element=None):
+ """Method to visit webpages and retry upon failure.
+
+ The function visits a web page and checks the the resulting URL matches
+ the intended URL, i.e. no redirects have happened
+
+ Args:
+ url: the intended url
+ page_load_timeout: timeout for page visits
+ num_tries: number of tries before url is declared unreachable
+ backup_url: url to visit if first url is not reachable. This can be
+ used to simply refresh the browser and try again or to re-login to
+ the AP
+ check_for_element: element id to check for existence on page
+ """
+ self.driver.set_page_load_timeout(page_load_timeout)
+ for idx in range(num_tries):
+ try:
+ self.visit(url)
+ except:
+ self.restart()
+
+ page_reached = self.url.split("/")[-1] == url.split("/")[-1]
+ if check_for_element:
+ time.sleep(BROWSER_WAIT_MED)
+ element = self.find_by_id(check_for_element)
+ if not element:
+ page_reached = 0
+ if page_reached:
+ break
+ else:
+ try:
+ self.visit(backup_url)
+ except:
+ self.restart()
+
+ if idx == num_tries - 1:
+ self.log.error("URL unreachable. Current URL: {}".format(
+ self.url))
+ raise RuntimeError("URL unreachable.")
+
+
+class WifiRetailAP(object):
+ """Base class implementation for retail ap.
+
+ Base class provides functions whose implementation is shared by all aps.
+ If some functions such as set_power not supported by ap, checks will raise
+ exceptions.
+ """
+ def __init__(self, ap_settings):
+ self.ap_settings = ap_settings.copy()
+ self.log = logger.create_tagged_trace_logger("AccessPoint|{}".format(
+ self._get_control_ip_address()))
+ # Lock AP
+ if self.ap_settings.get('lock_ap', 0):
+ self.lock_timeout = self.ap_settings.get('lock_timeout', 3600)
+ self._lock_ap()
+
+ def teardown(self):
+ """Function to perform destroy operations."""
+ self._unlock_ap()
+
+ def reset(self):
+ """Function that resets AP.
+
+ Function implementation is AP dependent and intended to perform any
+ necessary reset operations as part of controller destroy.
+ """
+ pass
+
+ def read_ap_settings(self):
+ """Function that reads current ap settings.
+
+ Function implementation is AP dependent and thus base class raises exception
+ if function not implemented in child class.
+ """
+ raise NotImplementedError
+
+ def validate_ap_settings(self):
+ """Function to validate ap settings.
+
+ This function compares the actual ap settings read from the web GUI
+ with the assumed settings saved in the AP object. When called after AP
+ configuration, this method helps ensure that our configuration was
+ successful.
+ Note: Calling this function updates the stored ap_settings
+
+ Raises:
+ ValueError: If read AP settings do not match stored settings.
+ """
+ assumed_ap_settings = self.ap_settings.copy()
+ actual_ap_settings = self.read_ap_settings()
+ if assumed_ap_settings != actual_ap_settings:
+ self.log.warning(
+ "Discrepancy in AP settings. Some settings may have been overwritten."
+ )
+
+ def configure_ap(self, **config_flags):
+ """Function that configures ap based on values of ap_settings.
+
+ Function implementation is AP dependent and thus base class raises exception
+ if function not implemented in child class.
+
+ Args:
+ config_flags: optional configuration flags
+ """
+ raise NotImplementedError
+
+ def set_region(self, region):
+ """Function that sets AP region.
+
+ This function sets the region for the AP. Note that this may overwrite
+ channel and bandwidth settings in cases where the new region does not
+ support the current wireless configuration.
+
+ Args:
+ region: string indicating AP region
+ """
+ self.log.warning("Updating region may overwrite wireless settings.")
+ setting_to_update = {"region": region}
+ self.update_ap_settings(setting_to_update)
+
+ def set_radio_on_off(self, network, status):
+ """Function that turns the radio on or off.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ status: boolean indicating on or off (0: off, 1: on)
+ """
+ setting_to_update = {"status_{}".format(network): int(status)}
+ self.update_ap_settings(setting_to_update)
+
+ def set_ssid(self, network, ssid):
+ """Function that sets network SSID.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ ssid: string containing ssid
+ """
+ setting_to_update = {"ssid_{}".format(network): str(ssid)}
+ self.update_ap_settings(setting_to_update)
+
+ def set_channel(self, network, channel):
+ """Function that sets network channel.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ channel: string or int containing channel
+ """
+ setting_to_update = {"channel_{}".format(network): str(channel)}
+ self.update_ap_settings(setting_to_update)
+
+ def set_bandwidth(self, network, bandwidth):
+ """Function that sets network bandwidth/mode.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ bandwidth: string containing mode, e.g. 11g, VHT20, VHT40, VHT80.
+ """
+ setting_to_update = {"bandwidth_{}".format(network): str(bandwidth)}
+ self.update_ap_settings(setting_to_update)
+
+ def set_power(self, network, power):
+ """Function that sets network transmit power.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ power: string containing power level, e.g., 25%, 100%
+ """
+ setting_to_update = {"power_{}".format(network): str(power)}
+ self.update_ap_settings(setting_to_update)
+
+ def set_security(self, network, security_type, *password):
+ """Function that sets network security setting and password.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ security: string containing security setting, e.g., WPA2-PSK
+ password: optional argument containing password
+ """
+ if (len(password) == 1) and (type(password[0]) == str):
+ setting_to_update = {
+ "security_type_{}".format(network): str(security_type),
+ "password_{}".format(network): str(password[0])
+ }
+ else:
+ setting_to_update = {
+ "security_type_{}".format(network): str(security_type)
+ }
+ self.update_ap_settings(setting_to_update)
+
+ def set_rate(self):
+ """Function that configures rate used by AP.
+
+ Function implementation is not supported by most APs and thus base
+ class raises exception if function not implemented in child class.
+ """
+ raise NotImplementedError
+
+ def update_ap_settings(self, dict_settings={}, **named_settings):
+ """Function to update settings of existing AP.
+
+ Function copies arguments into ap_settings and calls configure_retail_ap
+ to apply them.
+
+ Args:
+ *dict_settings accepts single dictionary of settings to update
+ **named_settings accepts named settings to update
+ Note: dict and named_settings cannot contain the same settings.
+ """
+ settings_to_update = dict(dict_settings, **named_settings)
+ if len(settings_to_update) != len(dict_settings) + len(named_settings):
+ raise KeyError("The following keys were passed twice: {}".format(
+ (set(dict_settings.keys()).intersection(
+ set(named_settings.keys())))))
+ if not set(settings_to_update.keys()).issubset(
+ set(self.ap_settings.keys())):
+ raise KeyError(
+ "The following settings are invalid for this AP: {}".format(
+ set(settings_to_update.keys()).difference(
+ set(self.ap_settings.keys()))))
+
+ updates_requested = False
+ status_toggle_flag = False
+ for setting, value in settings_to_update.items():
+ if self.ap_settings[setting] != value:
+ self.ap_settings[setting] = value
+ if "status" in setting:
+ status_toggle_flag = True
+ updates_requested = True
+
+ if updates_requested:
+ self.configure_ap(status_toggled=status_toggle_flag)
+
+ def band_lookup_by_channel(self, channel):
+ """Function that gives band name by channel number.
+
+ Args:
+ channel: channel number to lookup
+ Returns:
+ band: name of band which this channel belongs to on this ap
+ """
+ for key, value in self.channel_band_map.items():
+ if channel in value:
+ return key
+ raise ValueError("Invalid channel passed in argument.")
+
+ def _get_control_ip_address(self):
+ """Function to get AP's Control Interface IP address."""
+ if "ssh_config" in self.ap_settings.keys():
+ return self.ap_settings["ssh_config"]["host"]
+ else:
+ return self.ap_settings["ip_address"]
+
+ def _lock_ap(self):
+ """Function to lock the ap while tests are running."""
+ self.lock_file_path = "/tmp/{}_{}_{}.lock".format(
+ self.ap_settings['brand'], self.ap_settings['model'],
+ self._get_control_ip_address())
+ if not os.path.exists(self.lock_file_path):
+ with open(self.lock_file_path, 'w'):
+ pass
+ self.lock_file = open(self.lock_file_path, "r")
+ start_time = time.time()
+ self.log.info('Trying to acquire AP lock.')
+ while time.time() < start_time + self.lock_timeout:
+ try:
+ fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except BlockingIOError:
+ time.sleep(BROWSER_WAIT_SHORT)
+ continue
+ self.log.info('AP lock acquired.')
+ return
+ raise RuntimeError("Could not lock AP in time.")
+
+ def _unlock_ap(self):
+ """Function to unlock the AP when tests are done."""
+ self.log.info('Releasing AP lock.')
+ if hasattr(self, "lock_file"):
+ fcntl.flock(self.lock_file, fcntl.LOCK_UN)
+ self.lock_file.close()
+
+
+class NetgearR7000AP(WifiRetailAP):
+ """Class that implements Netgear R7000 AP."""
+ def __init__(self, ap_settings):
+ super().__init__(ap_settings)
+ self.init_gui_data()
+ # Read and update AP settings
+ self.read_ap_settings()
+ if not set(ap_settings.items()).issubset(self.ap_settings.items()):
+ self.update_ap_settings(ap_settings)
+
+ def init_gui_data(self):
+ """Function to initialize data used while interacting with web GUI"""
+ self.config_page = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_wireless_dual_band_r10.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_nologin = (
+ "{protocol}://{ip_address}:{port}/"
+ "WLG_wireless_dual_band_r10.htm").format(
+ protocol=self.ap_settings["protocol"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_advanced = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_adv_dual_band2.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.networks = ["2G", "5G_1"]
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [
+ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
+ 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+ ]
+ }
+ self.region_map = {
+ "1": "Africa",
+ "2": "Asia",
+ "3": "Australia",
+ "4": "Canada",
+ "5": "Europe",
+ "6": "Israel",
+ "7": "Japan",
+ "8": "Korea",
+ "9": "Mexico",
+ "10": "South America",
+ "11": "United States",
+ "12": "Middle East(Algeria/Syria/Yemen)",
+ "14": "Russia",
+ "16": "China",
+ "17": "India",
+ "18": "Malaysia",
+ "19": "Middle East(Iran/Labanon/Qatar)",
+ "20": "Middle East(Turkey/Egypt/Tunisia/Kuwait)",
+ "21": "Middle East(Saudi Arabia)",
+ "22": "Middle East(United Arab Emirates)",
+ "23": "Singapore",
+ "24": "Taiwan"
+ }
+ self.config_page_fields = {
+ "region": "WRegion",
+ ("2G", "status"): "enable_ap",
+ ("5G_1", "status"): "enable_ap_an",
+ ("2G", "ssid"): "ssid",
+ ("5G_1", "ssid"): "ssid_an",
+ ("2G", "channel"): "w_channel",
+ ("5G_1", "channel"): "w_channel_an",
+ ("2G", "bandwidth"): "opmode",
+ ("5G_1", "bandwidth"): "opmode_an",
+ ("2G", "power"): "enable_tpc",
+ ("5G_1", "power"): "enable_tpc_an",
+ ("2G", "security_type"): "security_type",
+ ("5G_1", "security_type"): "security_type_an",
+ ("2G", "password"): "passphrase",
+ ("5G_1", "password"): "passphrase_an"
+ }
+ self.bw_mode_values = {
+ "g and b": "11g",
+ "145Mbps": "VHT20",
+ "300Mbps": "VHT40",
+ "HT80": "VHT80"
+ }
+ self.power_mode_values = {
+ "1": "100%",
+ "2": "75%",
+ "3": "50%",
+ "4": "25%"
+ }
+ self.bw_mode_text = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 289 Mbps",
+ "VHT40": "Up to 600 Mbps",
+ "VHT80": "Up to 1300 Mbps"
+ }
+
+ def read_ap_settings(self):
+ """Function to read ap settings."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ # Visit URL
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+
+ for key, value in self.config_page_fields.items():
+ if "status" in key:
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
+ config_item = browser.find_by_name(value)
+ self.ap_settings["{}_{}".format(key[1], key[0])] = int(
+ config_item.first.checked)
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_MED, 10)
+ else:
+ config_item = browser.find_by_name(value)
+ if "bandwidth" in key:
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = self.bw_mode_values[
+ config_item.first.value]
+ elif "power" in key:
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = self.power_mode_values[
+ config_item.first.value]
+ elif "region" in key:
+ self.ap_settings["region"] = self.region_map[
+ config_item.first.value]
+ elif "security_type" in key:
+ for item in config_item:
+ if item.checked:
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = item.value
+ else:
+ config_item = browser.find_by_name(value)
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.first.value
+ return self.ap_settings.copy()
+
+ def configure_ap(self, **config_flags):
+ """Function to configure ap wireless settings."""
+ # Turn radios on or off
+ if config_flags["status_toggled"]:
+ self.configure_radio_on_off()
+ # Configure radios
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ # Visit URL
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_nologin,
+ BROWSER_WAIT_MED, 10, self.config_page)
+
+ # Update region, and power/bandwidth for each network
+ config_item = browser.find_by_name(
+ self.config_page_fields["region"]).first
+ config_item.select_by_text(self.ap_settings["region"])
+ for key, value in self.config_page_fields.items():
+ if "power" in key:
+ config_item = browser.find_by_name(value).first
+ config_item.select_by_text(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ elif "bandwidth" in key:
+ config_item = browser.find_by_name(value).first
+ try:
+ config_item.select_by_text(
+ self.bw_mode_text[self.ap_settings["{}_{}".format(
+ key[1], key[0])]])
+ except AttributeError:
+ self.log.warning(
+ "Cannot select bandwidth. Keeping AP default.")
+
+ # Update security settings (passwords updated only if applicable)
+ for key, value in self.config_page_fields.items():
+ if "security_type" in key:
+ browser.choose(
+ value, self.ap_settings["{}_{}".format(key[1],
+ key[0])])
+ if self.ap_settings["{}_{}".format(key[1],
+ key[0])] == "WPA2-PSK":
+ config_item = browser.find_by_name(
+ self.config_page_fields[(key[0],
+ "password")]).first
+ config_item.fill(self.ap_settings["{}_{}".format(
+ "password", key[0])])
+
+ # Update SSID and channel for each network
+ # NOTE: Update ordering done as such as workaround for R8000
+ # wherein channel and SSID get overwritten when some other
+ # variables are changed. However, region does have to be set before
+ # channel in all cases.
+ for key, value in self.config_page_fields.items():
+ if "ssid" in key:
+ config_item = browser.find_by_name(value).first
+ config_item.fill(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ elif "channel" in key:
+ config_item = browser.find_by_name(value).first
+ try:
+ config_item.select(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ time.sleep(BROWSER_WAIT_SHORT)
+ except AttributeError:
+ self.log.warning(
+ "Cannot select channel. Keeping AP default.")
+ try:
+ alert = browser.get_alert()
+ alert.accept()
+ except:
+ pass
+
+ time.sleep(BROWSER_WAIT_SHORT)
+ browser.find_by_name("Apply").first.click()
+ time.sleep(BROWSER_WAIT_SHORT)
+ try:
+ alert = browser.get_alert()
+ alert.accept()
+ time.sleep(BROWSER_WAIT_SHORT)
+ except:
+ time.sleep(BROWSER_WAIT_SHORT)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
+ 10)
+
+ def configure_radio_on_off(self):
+ """Helper configuration function to turn radios on/off."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ # Visit URL
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
+
+ # Turn radios on or off
+ for key, value in self.config_page_fields.items():
+ if "status" in key:
+ config_item = browser.find_by_name(value).first
+ if self.ap_settings["{}_{}".format(key[1], key[0])]:
+ config_item.check()
+ else:
+ config_item.uncheck()
+
+ time.sleep(BROWSER_WAIT_SHORT)
+ browser.find_by_name("Apply").first.click()
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
+ 10)
+
+
+class NetgearR7000NAAP(NetgearR7000AP):
+ """Class that implements Netgear R7000 NA AP."""
+ def init_gui_data(self):
+ """Function to initialize data used while interacting with web GUI"""
+ super.init_gui_data()
+ self.region_map["11"] = "North America"
+
+
+class NetgearR7500AP(WifiRetailAP):
+ """Class that implements Netgear R7500 AP."""
+ def __init__(self, ap_settings):
+ super().__init__(ap_settings)
+ self.init_gui_data()
+ # Read and update AP settings
+ self.read_ap_settings()
+ if not set(ap_settings.items()).issubset(self.ap_settings.items()):
+ self.update_ap_settings(ap_settings)
+
+ def init_gui_data(self):
+ """Function to initialize data used while interacting with web GUI"""
+ self.config_page = ("{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/index.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_advanced = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/adv_index.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.networks = ["2G", "5G_1"]
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [
+ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
+ 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+ ]
+ }
+ self.config_page_fields = {
+ "region": "WRegion",
+ ("2G", "status"): "enable_ap",
+ ("5G_1", "status"): "enable_ap_an",
+ ("2G", "ssid"): "ssid",
+ ("5G_1", "ssid"): "ssid_an",
+ ("2G", "channel"): "w_channel",
+ ("5G_1", "channel"): "w_channel_an",
+ ("2G", "bandwidth"): "opmode",
+ ("5G_1", "bandwidth"): "opmode_an",
+ ("2G", "security_type"): "security_type",
+ ("5G_1", "security_type"): "security_type_an",
+ ("2G", "password"): "passphrase",
+ ("5G_1", "password"): "passphrase_an"
+ }
+ self.region_map = {
+ "0": "Africa",
+ "1": "Asia",
+ "2": "Australia",
+ "3": "Canada",
+ "4": "Europe",
+ "5": "Israel",
+ "6": "Japan",
+ "7": "Korea",
+ "8": "Mexico",
+ "9": "South America",
+ "10": "United States",
+ "11": "China",
+ "12": "India",
+ "13": "Malaysia",
+ "14": "Middle East(Algeria/Syria/Yemen)",
+ "15": "Middle East(Iran/Labanon/Qatar)",
+ "16": "Middle East(Turkey/Egypt/Tunisia/Kuwait)",
+ "17": "Middle East(Saudi Arabia)",
+ "18": "Middle East(United Arab Emirates)",
+ "19": "Russia",
+ "20": "Singapore",
+ "21": "Taiwan"
+ }
+ self.bw_mode_text_2g = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 289 Mbps",
+ "VHT40": "Up to 600 Mbps"
+ }
+ self.bw_mode_text_5g = {
+ "VHT20": "Up to 347 Mbps",
+ "VHT40": "Up to 800 Mbps",
+ "VHT80": "Up to 1733 Mbps"
+ }
+ self.bw_mode_values = {
+ "1": "11g",
+ "2": "VHT20",
+ "3": "VHT40",
+ "7": "VHT20",
+ "8": "VHT40",
+ "9": "VHT80"
+ }
+
+ def read_ap_settings(self):
+ """Function to read ap wireless settings."""
+ # Get radio status (on/off)
+ self.read_radio_on_off()
+ # Get radio configuration. Note that if both radios are off, the below
+ # code will result in an error
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_MED,
+ 10,
+ check_for_element="wireless")
+ wireless_button = browser.find_by_id("wireless").first
+ wireless_button.click()
+ time.sleep(BROWSER_WAIT_MED)
+
+ with browser.get_iframe("formframe") as iframe:
+ for key, value in self.config_page_fields.items():
+ if "bandwidth" in key:
+ config_item = iframe.find_by_name(value).first
+ self.ap_settings["{}_{}".format(
+ key[1],
+ key[0])] = self.bw_mode_values[config_item.value]
+ elif "region" in key:
+ config_item = iframe.find_by_name(value).first
+ self.ap_settings["region"] = self.region_map[
+ config_item.value]
+ elif "password" in key:
+ try:
+ config_item = iframe.find_by_name(value).first
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.value
+ self.ap_settings["{}_{}".format(
+ "security_type", key[0])] = "WPA2-PSK"
+ except:
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = "defaultpassword"
+ self.ap_settings["{}_{}".format(
+ "security_type", key[0])] = "Disable"
+ elif ("channel" in key) or ("ssid" in key):
+ config_item = iframe.find_by_name(value).first
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.value
+ else:
+ pass
+ return self.ap_settings.copy()
+
+ def configure_ap(self, **config_flags):
+ """Function to configure ap wireless settings."""
+ # Turn radios on or off
+ if config_flags["status_toggled"]:
+ self.configure_radio_on_off()
+ # Configure radios
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_MED,
+ 10,
+ check_for_element="wireless")
+ wireless_button = browser.find_by_id("wireless").first
+ wireless_button.click()
+ time.sleep(BROWSER_WAIT_MED)
+
+ with browser.get_iframe("formframe") as iframe:
+ # Update AP region. Must be done before channel setting
+ config_item = iframe.find_by_name(
+ self.config_page_fields["region"]).first
+ config_item.select_by_text(self.ap_settings["region"])
+ # Update wireless settings for each network
+ for key, value in self.config_page_fields.items():
+ if "ssid" in key:
+ config_item = iframe.find_by_name(value).first
+ config_item.fill(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ elif "channel" in key:
+ channel_string = "0" * (int(self.ap_settings[
+ "{}_{}".format(key[1], key[0])]) < 10) + str(
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])]) + "(DFS)" * (48 < int(
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])]) < 149)
+ config_item = iframe.find_by_name(value).first
+ try:
+ config_item.select_by_text(channel_string)
+ except AttributeError:
+ self.log.warning(
+ "Cannot select channel. Keeping AP default.")
+ elif key == ("2G", "bandwidth"):
+ config_item = iframe.find_by_name(value).first
+ try:
+ config_item.select_by_text(
+ str(self.bw_mode_text_2g[self.ap_settings[
+ "{}_{}".format(key[1], key[0])]]))
+ except AttributeError:
+ self.log.warning(
+ "Cannot select bandwidth. Keeping AP default.")
+ elif key == ("5G_1", "bandwidth"):
+ config_item = iframe.find_by_name(value).first
+ try:
+ config_item.select_by_text(
+ str(self.bw_mode_text_5g[self.ap_settings[
+ "{}_{}".format(key[1], key[0])]]))
+ except AttributeError:
+ self.log.warning(
+ "Cannot select bandwidth. Keeping AP default.")
+ # Update passwords for WPA2-PSK protected networks
+ # (Must be done after security type is selected)
+ for key, value in self.config_page_fields.items():
+ if "security_type" in key:
+ iframe.choose(
+ value,
+ self.ap_settings["{}_{}".format(key[1], key[0])])
+ if self.ap_settings["{}_{}".format(
+ key[1], key[0])] == "WPA2-PSK":
+ config_item = iframe.find_by_name(
+ self.config_page_fields[(key[0],
+ "password")]).first
+ config_item.fill(self.ap_settings["{}_{}".format(
+ "password", key[0])])
+
+ apply_button = iframe.find_by_name("Apply")
+ apply_button[0].click()
+ time.sleep(BROWSER_WAIT_SHORT)
+ try:
+ alert = browser.get_alert()
+ alert.accept()
+ except:
+ pass
+ time.sleep(BROWSER_WAIT_SHORT)
+ try:
+ alert = browser.get_alert()
+ alert.accept()
+ except:
+ pass
+ time.sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
+ 10)
+
+ def configure_radio_on_off(self):
+ """Helper configuration function to turn radios on/off."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED,
+ 10,
+ check_for_element="advanced_bt")
+ advanced_button = browser.find_by_id("advanced_bt").first
+ advanced_button.click()
+ time.sleep(BROWSER_WAIT_MED)
+ wireless_button = browser.find_by_id("wladv").first
+ wireless_button.click()
+ time.sleep(BROWSER_WAIT_MED)
+
+ with browser.get_iframe("formframe") as iframe:
+ # Turn radios on or off
+ for key, value in self.config_page_fields.items():
+ if "status" in key:
+ config_item = iframe.find_by_name(value).first
+ if self.ap_settings["{}_{}".format(key[1], key[0])]:
+ config_item.check()
+ else:
+ config_item.uncheck()
+
+ time.sleep(BROWSER_WAIT_SHORT)
+ browser.find_by_name("Apply").first.click()
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_EXTRA_LONG, 10)
+
+ def read_radio_on_off(self):
+ """Helper configuration function to read radio status."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 900) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED,
+ 10,
+ check_for_element="advanced_bt")
+ advanced_button = browser.find_by_id("advanced_bt").first
+ advanced_button.click()
+ time.sleep(BROWSER_WAIT_SHORT)
+ wireless_button = browser.find_by_id("wladv").first
+ wireless_button.click()
+ time.sleep(BROWSER_WAIT_MED)
+
+ with browser.get_iframe("formframe") as iframe:
+ # Turn radios on or off
+ for key, value in self.config_page_fields.items():
+ if "status" in key:
+ config_item = iframe.find_by_name(value).first
+ self.ap_settings["{}_{}".format(key[1], key[0])] = int(
+ config_item.checked)
+
+
+class NetgearR7800AP(NetgearR7500AP):
+ """Class that implements Netgear R7800 AP.
+
+ Since most of the class' implementation is shared with the R7500, this
+ class inherits from NetgearR7500AP and simply redefines config parameters
+ """
+ def __init__(self, ap_settings):
+ super().__init__(ap_settings)
+ self.init_gui_data()
+ # Overwrite minor differences from R7500 AP
+ self.bw_mode_text_2g["VHT20"] = "Up to 347 Mbps"
+ # Read and update AP settings
+ self.read_ap_settings()
+ if not set(ap_settings.items()).issubset(self.ap_settings.items()):
+ self.update_ap_settings(ap_settings)
+
+
+class NetgearR8000AP(NetgearR7000AP):
+ """Class that implements Netgear R8000 AP.
+
+ Since most of the class' implementation is shared with the R7000, this
+ class inherits from NetgearR7000AP and simply redefines config parameters
+ """
+ def init_gui_data(self):
+ super().init_gui_data()
+ # Overwrite minor differences from R7000 AP
+ self.config_page = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_wireless_dual_band_r8000.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_nologin = (
+ "{protocol}://{ip_address}:{port}/"
+ "WLG_wireless_dual_band_r8000.htm").format(
+ protocol=self.ap_settings["protocol"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_advanced = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_adv_dual_band2_r8000.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.networks = ["2G", "5G_1", "5G_2"]
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [36, 40, 44, 48],
+ "5G_2": [149, 153, 157, 161, 165]
+ }
+ self.config_page_fields = {
+ "region": "WRegion",
+ ("2G", "status"): "enable_ap",
+ ("5G_1", "status"): "enable_ap_an",
+ ("5G_2", "status"): "enable_ap_an_2",
+ ("2G", "ssid"): "ssid",
+ ("5G_1", "ssid"): "ssid_an",
+ ("5G_2", "ssid"): "ssid_an_2",
+ ("2G", "channel"): "w_channel",
+ ("5G_1", "channel"): "w_channel_an",
+ ("5G_2", "channel"): "w_channel_an_2",
+ ("2G", "bandwidth"): "opmode",
+ ("5G_1", "bandwidth"): "opmode_an",
+ ("5G_2", "bandwidth"): "opmode_an_2",
+ ("2G", "security_type"): "security_type",
+ ("5G_1", "security_type"): "security_type_an",
+ ("5G_2", "security_type"): "security_type_an_2",
+ ("2G", "password"): "passphrase",
+ ("5G_1", "password"): "passphrase_an",
+ ("5G_2", "password"): "passphrase_an_2"
+ }
+
+
+class NetgearR8500AP(NetgearR7000AP):
+ """Class that implements Netgear R8500 AP.
+
+ Since most of the class' implementation is shared with the R7000, this
+ class inherits from NetgearR7000AP and simply redefines config parameters
+ """
+ def __init__(self, ap_settings):
+ super().__init__(ap_settings)
+ self.init_gui_data()
+ # Overwrite minor differences from R8000 AP
+ self.config_page = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_wireless_tri_band.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_nologin = (
+ "{protocol}://{ip_address}:{port}/"
+ "WLG_wireless_tri_band.htm").format(
+ protocol=self.ap_settings["protocol"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_advanced = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_adv_tri_band2.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.networks = ["2G", "5G_1", "5G_2"]
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [36, 40, 44, 48],
+ "5G_2": [149, 153, 157, 161, 165]
+ }
+ self.config_page_fields = {
+ "region": "WRegion",
+ ("2G", "status"): "enable_ap",
+ ("5G_1", "status"): "enable_ap_an",
+ ("5G_2", "status"): "enable_ap_an_2",
+ ("2G", "ssid"): "ssid",
+ ("5G_1", "ssid"): "ssid_an",
+ ("5G_2", "ssid"): "ssid_an_2",
+ ("2G", "channel"): "w_channel",
+ ("5G_1", "channel"): "w_channel_an",
+ ("5G_2", "channel"): "w_channel_an_2",
+ ("2G", "bandwidth"): "opmode",
+ ("5G_1", "bandwidth"): "opmode_an",
+ ("5G_2", "bandwidth"): "opmode_an_2",
+ ("2G", "security_type"): "security_type",
+ ("5G_1", "security_type"): "security_type_an",
+ ("5G_2", "security_type"): "security_type_an_2",
+ ("2G", "password"): "passphrase",
+ ("5G_1", "password"): "passphrase_an",
+ ("5G_2", "password"): "passphrase_an_2"
+ }
+ self.bw_mode_text = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 433 Mbps",
+ "VHT40": "Up to 1000 Mbps",
+ "VHT80": "Up to 2165 Mbps"
+ }
+ # Read and update AP settings
+ self.read_ap_settings()
+ if not set(ap_settings.items()).issubset(self.ap_settings.items()):
+ self.update_ap_settings(ap_settings)
+
+
+class NetgearRAXAP(NetgearR7000AP):
+ """Class that implements Netgear RAX AP.
+
+ Since most of the class' implementation is shared with the R7000, this
+ class inherits from NetgearR7000AP and simply redefines config parameters
+ """
+ def init_gui_data(self):
+ super().init_gui_data()
+ # Overwrite minor differences from R7000 AP
+ self.config_page = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_wireless_dual_band_r10.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_nologin = (
+ "{protocol}://{ip_address}:{port}/"
+ "WLG_wireless_dual_band_r10.htm").format(
+ protocol=self.ap_settings["protocol"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.config_page_advanced = (
+ "{protocol}://{username}:{password}@"
+ "{ip_address}:{port}/WLG_adv_dual_band2.htm").format(
+ protocol=self.ap_settings["protocol"],
+ username=self.ap_settings["admin_username"],
+ password=self.ap_settings["admin_password"],
+ ip_address=self.ap_settings["ip_address"],
+ port=self.ap_settings["port"])
+ self.networks = ["2G", "5G_1", "5G_2"]
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [
+ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
+ 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+ ]
+ }
+
+ self.bw_mode_values = {
+ "g and b": "11g",
+ "145Mbps": "VHT20",
+ "300Mbps": "VHT40",
+ "HT80": "VHT80",
+ "HT160": "VHT160"
+ }
+ self.bw_mode_text = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 600 Mbps",
+ "VHT40": "Up to 1200 Mbps",
+ "VHT80": "Up to 2400 Mbps",
+ "VHT160": "Up to 4800 Mbps"
+ }
+
+
+class GoogleWifiAP(WifiRetailAP):
+ """ Class that implements Google Wifi AP.
+
+ This class is a work in progress
+ """
+ def __init__(self, ap_settings):
+ super().__init__(ap_settings)
+ # Initialize AP
+ if self.ap_settings["status_2G"] and self.ap_settings["status_5G_1"]:
+ raise ValueError("Error initializing Google Wifi AP. "
+ "Only one interface can be enabled at a time.")
+ self.channel_band_map = {
+ "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+ "5G_1": [36, 40, 44, 48, 149, 153, 157, 161, 165]
+ }
+ self.BW_MODE_MAP = {
+ "legacy": 20,
+ "VHT20": 20,
+ "VHT40": 40,
+ "VHT80": 80
+ }
+ self.default_settings = {
+ "region": "United States",
+ "brand": "Google",
+ "model": "Wifi",
+ "hostapd_profile": "whirlwind",
+ "status_2G": 0,
+ "status_5G_1": 0,
+ "ssid_2G": "GoogleWifi_2G",
+ "ssid_5G_1": "GoogleWifi_5G",
+ "channel_2G": 11,
+ "channel_5G_1": 149,
+ "bandwidth_2G": "VHT20",
+ "bandwidth_5G_1": "VHT20",
+ "power_2G": "auto",
+ "power_5G_1": "auto",
+ "mode_2G": None,
+ "num_streams_2G": None,
+ "rate_2G": "auto",
+ "short_gi_2G": 0,
+ "mode_5G_1": None,
+ "num_streams_5G_1": None,
+ "rate_5G_1": "auto",
+ "short_gi_5G_1": 0,
+ "security_type_2G": "Open",
+ "security_type_5G_1": "Open",
+ "subnet_2G": "192.168.1.0/24",
+ "subnet_5G_1": "192.168.9.0/24",
+ "password_2G": "password",
+ "password_5G_1": "password"
+ }
+
+ for setting in self.default_settings.keys():
+ if setting not in self.ap_settings:
+ self.log.debug(
+ "{0} not found during init. Setting {0} = {1}".format(
+ setting, self.default_settings[setting]))
+ self.ap_settings[setting] = self.default_settings[setting]
+ init_settings = self.ap_settings.copy()
+ init_settings["ap_subnet"] = {
+ "2g": self.ap_settings["subnet_2G"],
+ "5g": self.ap_settings["subnet_5G_1"]
+ }
+ self.access_point = access_point.AccessPoint(init_settings)
+ self.configure_ap()
+
+ def read_ap_settings(self):
+ """Function that reads current ap settings."""
+ return self.ap_settings.copy()
+
+ def update_ap_settings(self, dict_settings={}, **named_settings):
+ """Function to update settings of existing AP.
+
+ Function copies arguments into ap_settings and calls configure_ap
+ to apply them.
+
+ Args:
+ dict_settings: single dictionary of settings to update
+ **named_settings: named settings to update
+ Note: dict and named_settings cannot contain the same settings.
+ """
+ settings_to_update = dict(dict_settings, **named_settings)
+ if len(settings_to_update) != len(dict_settings) + len(named_settings):
+ raise KeyError("The following keys were passed twice: {}".format(
+ (set(dict_settings.keys()).intersection(
+ set(named_settings.keys())))))
+ if not set(settings_to_update.keys()).issubset(
+ set(self.ap_settings.keys())):
+ raise KeyError(
+ "The following settings are invalid for this AP: {}".format(
+ set(settings_to_update.keys()).difference(
+ set(self.ap_settings.keys()))))
+
+ updating_2G = any(["2G" in x for x in settings_to_update.keys()])
+ updating_5G_1 = any(["5G_1" in x for x in settings_to_update.keys()])
+ if updating_2G and updating_5G_1:
+ raise ValueError(
+ "Error updating Google WiFi AP. "
+ "One interface can be activated and updated at a time")
+ elif updating_2G:
+ # If updating an interface and not explicitly setting its status,
+ # it is assumed that the interface is to be ENABLED and updated
+ if "status_2G" not in settings_to_update:
+ settings_to_update["status_2G"] = 1
+ settings_to_update["status_5G_1"] = 0
+ elif updating_5G_1:
+ if "status_5G_1" not in settings_to_update:
+ settings_to_update["status_2G"] = 0
+ settings_to_update["status_5G_1"] = 1
+
+ updates_requested = False
+ for setting, value in settings_to_update.items():
+ if self.ap_settings[setting] != value:
+ self.ap_settings[setting] = value
+ updates_requested = True
+
+ if updates_requested:
+ self.configure_ap()
+
+ def configure_ap(self):
+ """Function to configure Google Wifi."""
+ self.log.info("Stopping Google Wifi interfaces.")
+ self.access_point.stop_all_aps()
+
+ if self.ap_settings["status_2G"] == 1:
+ network = "2G"
+ self.log.info("Bringing up 2.4 GHz network.")
+ elif self.ap_settings["status_5G_1"] == 1:
+ network = "5G_1"
+ self.log.info("Bringing up 5 GHz network.")
+ else:
+ return
+
+ bss_settings = []
+ ssid = self.ap_settings["ssid_{}".format(network)]
+ security_mode = self.ap_settings["security_type_{}".format(
+ network)].lower()
+ if "wpa" in security_mode:
+ password = self.ap_settings["password_{}".format(network)]
+ security = hostapd_security.Security(security_mode=security_mode,
+ password=password)
+ else:
+ security = hostapd_security.Security(security_mode=None,
+ password=None)
+ channel = int(self.ap_settings["channel_{}".format(network)])
+ bandwidth = self.BW_MODE_MAP[self.ap_settings["bandwidth_{}".format(
+ network)]]
+ config = hostapd_ap_preset.create_ap_preset(
+ channel=channel,
+ ssid=ssid,
+ security=security,
+ bss_settings=bss_settings,
+ vht_bandwidth=bandwidth,
+ profile_name=self.ap_settings["hostapd_profile"],
+ iface_wlan_2g=self.access_point.wlan_2g,
+ iface_wlan_5g=self.access_point.wlan_5g)
+ config_bridge = self.access_point.generate_bridge_configs(channel)
+ brconfigs = bridge_interface.BridgeInterfaceConfigs(
+ config_bridge[0], "lan0", config_bridge[2])
+ self.access_point.bridge.startup(brconfigs)
+ self.access_point.start_ap(config)
+ self.set_power(network, self.ap_settings["power_{}".format(network)])
+ self.set_rate(
+ network,
+ mode=self.ap_settings["mode_{}".format(network)],
+ num_streams=self.ap_settings["num_streams_{}".format(network)],
+ rate=self.ap_settings["rate_{}".format(network)],
+ short_gi=self.ap_settings["short_gi_{}".format(network)])
+ self.log.info("AP started on channel {} with SSID {}".format(
+ channel, ssid))
+
+ def set_power(self, network, power):
+ """Function that sets network transmit power.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ power: power level in dBm
+ """
+ if power == "auto":
+ power_string = "auto"
+ else:
+ if not float(power).is_integer():
+ self.log.info(
+ "Power in dBm must be an integer. Setting to {}".format(
+ int(power)))
+ power = int(power)
+ power_string = "fixed {}".format(int(power) * 100)
+
+ if "2G" in network:
+ interface = self.access_point.wlan_2g
+ self.ap_settings["power_2G"] = power
+ elif "5G_1" in network:
+ interface = self.access_point.wlan_5g
+ self.ap_settings["power_5G_1"] = power
+ self.access_point.ssh.run("iw dev {} set txpower {}".format(
+ interface, power_string))
+
+ def set_rate(self,
+ network,
+ mode=None,
+ num_streams=None,
+ rate='auto',
+ short_gi=0):
+ """Function that sets rate.
+
+ Args:
+ network: string containing network identifier (2G, 5G_1, 5G_2)
+ mode: string indicating the WiFi standard to use
+ num_streams: number of MIMO streams. used only for VHT
+ rate: data rate of MCS index to use
+ short_gi: boolean controlling the use of short guard interval
+ """
+ if "2G" in network:
+ interface = self.access_point.wlan_2g
+ interface_short = "2.4"
+ self.ap_settings["mode_2G"] = mode
+ self.ap_settings["num_streams_2G"] = num_streams
+ self.ap_settings["rate_2G"] = rate
+ self.ap_settings["short_gi_2G"] = short_gi
+ elif "5G_1" in network:
+ interface = self.access_point.wlan_5g
+ interface_short = "5"
+ self.ap_settings["mode_5G_1"] = mode
+ self.ap_settings["num_streams_5G_1"] = num_streams
+ self.ap_settings["rate_5G_1"] = rate
+ self.ap_settings["short_gi_5G_1"] = short_gi
+
+ if rate == "auto":
+ cmd_string = "iw dev {0} set bitrates".format(interface)
+ elif "legacy" in mode.lower():
+ cmd_string = "iw dev {0} set bitrates legacy-{1} {2} ht-mcs-{1} vht-mcs-{1}".format(
+ interface, interface_short, rate)
+ elif "vht" in mode.lower():
+ cmd_string = "iw dev {0} set bitrates legacy-{1} ht-mcs-{1} vht-mcs-{1} {2}:{3}".format(
+ interface, interface_short, num_streams, rate)
+ if short_gi:
+ cmd_string = cmd_string + " sgi-{}".format(interface_short)
+ elif "ht" in mode.lower():
+ cmd_string = "iw dev {0} set bitrates legacy-{1} ht-mcs-{1} {2} vht-mcs-{1}".format(
+ interface, interface_short, rate)
+ if short_gi:
+ cmd_string = cmd_string + " sgi-{}".format(interface_short)
+ self.access_point.ssh.run(cmd_string)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py
new file mode 100755
index 0000000..73f5008
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py
@@ -0,0 +1,2609 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import re
+import shutil
+import time
+
+from collections import namedtuple
+from enum import IntEnum
+from queue import Empty
+
+from acts import asserts
+from acts import context
+from acts import signals
+from acts import utils
+from acts.controllers import attenuator
+from acts.controllers.ap_lib import hostapd_security
+from acts.controllers.ap_lib import hostapd_ap_preset
+from acts.controllers.ap_lib.hostapd_constants import BAND_2G
+from acts.controllers.ap_lib.hostapd_constants import BAND_5G
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.tel import tel_defines
+
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
+# Number of seconds to wait for events that are supposed to happen quickly.
+# Like onSuccess for start background scan and confirmation on wifi state
+# change.
+SHORT_TIMEOUT = 30
+ROAMING_TIMEOUT = 30
+WIFI_CONNECTION_TIMEOUT_DEFAULT = 30
+# Speed of light in m/s.
+SPEED_OF_LIGHT = 299792458
+
+DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
+
+ROAMING_ATTN = {
+ "AP1_on_AP2_off": [
+ 0,
+ 0,
+ 95,
+ 95
+ ],
+ "AP1_off_AP2_on": [
+ 95,
+ 95,
+ 0,
+ 0
+ ],
+ "default": [
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+
+
+class WifiEnums():
+
+ SSID_KEY = "SSID" # Used for Wifi & SoftAp
+ SSID_PATTERN_KEY = "ssidPattern"
+ NETID_KEY = "network_id"
+ BSSID_KEY = "BSSID" # Used for Wifi & SoftAp
+ BSSID_PATTERN_KEY = "bssidPattern"
+ PWD_KEY = "password" # Used for Wifi & SoftAp
+ frequency_key = "frequency"
+ HIDDEN_KEY = "hiddenSSID" # Used for Wifi & SoftAp
+ IS_APP_INTERACTION_REQUIRED = "isAppInteractionRequired"
+ IS_USER_INTERACTION_REQUIRED = "isUserInteractionRequired"
+ IS_SUGGESTION_METERED = "isMetered"
+ PRIORITY = "priority"
+ SECURITY = "security" # Used for Wifi & SoftAp
+
+ # Used for SoftAp
+ AP_BAND_KEY = "apBand"
+ AP_CHANNEL_KEY = "apChannel"
+ AP_MAXCLIENTS_KEY = "MaxNumberOfClients"
+ AP_SHUTDOWNTIMEOUT_KEY = "ShutdownTimeoutMillis"
+ AP_SHUTDOWNTIMEOUTENABLE_KEY = "AutoShutdownEnabled"
+ AP_CLIENTCONTROL_KEY = "ClientControlByUserEnabled"
+ AP_ALLOWEDLIST_KEY = "AllowedClientList"
+ AP_BLOCKEDLIST_KEY = "BlockedClientList"
+
+ WIFI_CONFIG_SOFTAP_BAND_2G = 1
+ WIFI_CONFIG_SOFTAP_BAND_5G = 2
+ WIFI_CONFIG_SOFTAP_BAND_2G_5G = 3
+ WIFI_CONFIG_SOFTAP_BAND_6G = 4
+ WIFI_CONFIG_SOFTAP_BAND_2G_6G = 5
+ WIFI_CONFIG_SOFTAP_BAND_5G_6G = 6
+ WIFI_CONFIG_SOFTAP_BAND_ANY = 7
+
+ # DO NOT USE IT for new test case! Replaced by WIFI_CONFIG_SOFTAP_BAND_
+ WIFI_CONFIG_APBAND_2G = WIFI_CONFIG_SOFTAP_BAND_2G
+ WIFI_CONFIG_APBAND_5G = WIFI_CONFIG_SOFTAP_BAND_5G
+ WIFI_CONFIG_APBAND_AUTO = WIFI_CONFIG_SOFTAP_BAND_2G_5G
+
+ WIFI_CONFIG_APBAND_2G_OLD = 0
+ WIFI_CONFIG_APBAND_5G_OLD = 1
+ WIFI_CONFIG_APBAND_AUTO_OLD = -1
+
+ WIFI_WPS_INFO_PBC = 0
+ WIFI_WPS_INFO_DISPLAY = 1
+ WIFI_WPS_INFO_KEYPAD = 2
+ WIFI_WPS_INFO_LABEL = 3
+ WIFI_WPS_INFO_INVALID = 4
+
+ class SoftApSecurityType():
+ OPEN = "NONE"
+ WPA2 = "WPA2_PSK"
+ WPA3_SAE_TRANSITION = "WPA3_SAE_TRANSITION"
+ WPA3_SAE = "WPA3_SAE"
+
+ class CountryCode():
+ CHINA = "CN"
+ JAPAN = "JP"
+ UK = "GB"
+ US = "US"
+ UNKNOWN = "UNKNOWN"
+
+ # Start of Macros for EAP
+ # EAP types
+ class Eap(IntEnum):
+ NONE = -1
+ PEAP = 0
+ TLS = 1
+ TTLS = 2
+ PWD = 3
+ SIM = 4
+ AKA = 5
+ AKA_PRIME = 6
+ UNAUTH_TLS = 7
+
+ # EAP Phase2 types
+ class EapPhase2(IntEnum):
+ NONE = 0
+ PAP = 1
+ MSCHAP = 2
+ MSCHAPV2 = 3
+ GTC = 4
+
+ class Enterprise:
+ # Enterprise Config Macros
+ EMPTY_VALUE = "NULL"
+ EAP = "eap"
+ PHASE2 = "phase2"
+ IDENTITY = "identity"
+ ANON_IDENTITY = "anonymous_identity"
+ PASSWORD = "password"
+ SUBJECT_MATCH = "subject_match"
+ ALTSUBJECT_MATCH = "altsubject_match"
+ DOM_SUFFIX_MATCH = "domain_suffix_match"
+ CLIENT_CERT = "client_cert"
+ CA_CERT = "ca_cert"
+ ENGINE = "engine"
+ ENGINE_ID = "engine_id"
+ PRIVATE_KEY_ID = "key_id"
+ REALM = "realm"
+ PLMN = "plmn"
+ FQDN = "FQDN"
+ FRIENDLY_NAME = "providerFriendlyName"
+ ROAMING_IDS = "roamingConsortiumIds"
+ OCSP = "ocsp"
+ # End of Macros for EAP
+
+ # Macros for wifi p2p.
+ WIFI_P2P_SERVICE_TYPE_ALL = 0
+ WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
+ WIFI_P2P_SERVICE_TYPE_UPNP = 2
+ WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
+
+ class ScanResult:
+ CHANNEL_WIDTH_20MHZ = 0
+ CHANNEL_WIDTH_40MHZ = 1
+ CHANNEL_WIDTH_80MHZ = 2
+ CHANNEL_WIDTH_160MHZ = 3
+ CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
+
+ # Macros for wifi rtt.
+ class RttType(IntEnum):
+ TYPE_ONE_SIDED = 1
+ TYPE_TWO_SIDED = 2
+
+ class RttPeerType(IntEnum):
+ PEER_TYPE_AP = 1
+ PEER_TYPE_STA = 2 # Requires NAN.
+ PEER_P2P_GO = 3
+ PEER_P2P_CLIENT = 4
+ PEER_NAN = 5
+
+ class RttPreamble(IntEnum):
+ PREAMBLE_LEGACY = 0x01
+ PREAMBLE_HT = 0x02
+ PREAMBLE_VHT = 0x04
+
+ class RttBW(IntEnum):
+ BW_5_SUPPORT = 0x01
+ BW_10_SUPPORT = 0x02
+ BW_20_SUPPORT = 0x04
+ BW_40_SUPPORT = 0x08
+ BW_80_SUPPORT = 0x10
+ BW_160_SUPPORT = 0x20
+
+ class Rtt(IntEnum):
+ STATUS_SUCCESS = 0
+ STATUS_FAILURE = 1
+ STATUS_FAIL_NO_RSP = 2
+ STATUS_FAIL_REJECTED = 3
+ STATUS_FAIL_NOT_SCHEDULED_YET = 4
+ STATUS_FAIL_TM_TIMEOUT = 5
+ STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6
+ STATUS_FAIL_NO_CAPABILITY = 7
+ STATUS_ABORTED = 8
+ STATUS_FAIL_INVALID_TS = 9
+ STATUS_FAIL_PROTOCOL = 10
+ STATUS_FAIL_SCHEDULE = 11
+ STATUS_FAIL_BUSY_TRY_LATER = 12
+ STATUS_INVALID_REQ = 13
+ STATUS_NO_WIFI = 14
+ STATUS_FAIL_FTM_PARAM_OVERRIDE = 15
+
+ REASON_UNSPECIFIED = -1
+ REASON_NOT_AVAILABLE = -2
+ REASON_INVALID_LISTENER = -3
+ REASON_INVALID_REQUEST = -4
+
+ class RttParam:
+ device_type = "deviceType"
+ request_type = "requestType"
+ BSSID = "bssid"
+ channel_width = "channelWidth"
+ frequency = "frequency"
+ center_freq0 = "centerFreq0"
+ center_freq1 = "centerFreq1"
+ number_burst = "numberBurst"
+ interval = "interval"
+ num_samples_per_burst = "numSamplesPerBurst"
+ num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
+ num_retries_per_FTMR = "numRetriesPerFTMR"
+ lci_request = "LCIRequest"
+ lcr_request = "LCRRequest"
+ burst_timeout = "burstTimeout"
+ preamble = "preamble"
+ bandwidth = "bandwidth"
+ margin = "margin"
+
+ RTT_MARGIN_OF_ERROR = {
+ RttBW.BW_80_SUPPORT: 2,
+ RttBW.BW_40_SUPPORT: 5,
+ RttBW.BW_20_SUPPORT: 5
+ }
+
+ # Macros as specified in the WifiScanner code.
+ WIFI_BAND_UNSPECIFIED = 0 # not specified
+ WIFI_BAND_24_GHZ = 1 # 2.4 GHz band
+ WIFI_BAND_5_GHZ = 2 # 5 GHz band without DFS channels
+ WIFI_BAND_5_GHZ_DFS_ONLY = 4 # 5 GHz band with DFS channels
+ WIFI_BAND_5_GHZ_WITH_DFS = 6 # 5 GHz band with DFS channels
+ WIFI_BAND_BOTH = 3 # both bands without DFS channels
+ WIFI_BAND_BOTH_WITH_DFS = 7 # both bands with DFS channels
+
+ REPORT_EVENT_AFTER_BUFFER_FULL = 0
+ REPORT_EVENT_AFTER_EACH_SCAN = 1
+ REPORT_EVENT_FULL_SCAN_RESULT = 2
+
+ SCAN_TYPE_LOW_LATENCY = 0
+ SCAN_TYPE_LOW_POWER = 1
+ SCAN_TYPE_HIGH_ACCURACY = 2
+
+ # US Wifi frequencies
+ ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
+ 2457, 2462]
+ DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
+ 5600, 5620, 5640, 5660, 5680, 5700, 5720]
+ NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
+ 5825]
+ ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
+
+ band_to_frequencies = {
+ WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
+ WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
+ WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
+ WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
+ WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
+ WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
+ }
+
+ # All Wifi frequencies to channels lookup.
+ freq_to_channel = {
+ 2412: 1,
+ 2417: 2,
+ 2422: 3,
+ 2427: 4,
+ 2432: 5,
+ 2437: 6,
+ 2442: 7,
+ 2447: 8,
+ 2452: 9,
+ 2457: 10,
+ 2462: 11,
+ 2467: 12,
+ 2472: 13,
+ 2484: 14,
+ 4915: 183,
+ 4920: 184,
+ 4925: 185,
+ 4935: 187,
+ 4940: 188,
+ 4945: 189,
+ 4960: 192,
+ 4980: 196,
+ 5035: 7,
+ 5040: 8,
+ 5045: 9,
+ 5055: 11,
+ 5060: 12,
+ 5080: 16,
+ 5170: 34,
+ 5180: 36,
+ 5190: 38,
+ 5200: 40,
+ 5210: 42,
+ 5220: 44,
+ 5230: 46,
+ 5240: 48,
+ 5260: 52,
+ 5280: 56,
+ 5300: 60,
+ 5320: 64,
+ 5500: 100,
+ 5520: 104,
+ 5540: 108,
+ 5560: 112,
+ 5580: 116,
+ 5600: 120,
+ 5620: 124,
+ 5640: 128,
+ 5660: 132,
+ 5680: 136,
+ 5700: 140,
+ 5745: 149,
+ 5765: 153,
+ 5785: 157,
+ 5795: 159,
+ 5805: 161,
+ 5825: 165,
+ }
+
+ # All Wifi channels to frequencies lookup.
+ channel_2G_to_freq = {
+ 1: 2412,
+ 2: 2417,
+ 3: 2422,
+ 4: 2427,
+ 5: 2432,
+ 6: 2437,
+ 7: 2442,
+ 8: 2447,
+ 9: 2452,
+ 10: 2457,
+ 11: 2462,
+ 12: 2467,
+ 13: 2472,
+ 14: 2484
+ }
+
+ channel_5G_to_freq = {
+ 183: 4915,
+ 184: 4920,
+ 185: 4925,
+ 187: 4935,
+ 188: 4940,
+ 189: 4945,
+ 192: 4960,
+ 196: 4980,
+ 7: 5035,
+ 8: 5040,
+ 9: 5045,
+ 11: 5055,
+ 12: 5060,
+ 16: 5080,
+ 34: 5170,
+ 36: 5180,
+ 38: 5190,
+ 40: 5200,
+ 42: 5210,
+ 44: 5220,
+ 46: 5230,
+ 48: 5240,
+ 52: 5260,
+ 56: 5280,
+ 60: 5300,
+ 64: 5320,
+ 100: 5500,
+ 104: 5520,
+ 108: 5540,
+ 112: 5560,
+ 116: 5580,
+ 120: 5600,
+ 124: 5620,
+ 128: 5640,
+ 132: 5660,
+ 136: 5680,
+ 140: 5700,
+ 149: 5745,
+ 151: 5755,
+ 153: 5765,
+ 155: 5775,
+ 157: 5785,
+ 159: 5795,
+ 161: 5805,
+ 165: 5825
+ }
+
+
+class WifiChannelBase:
+ ALL_2G_FREQUENCIES = []
+ DFS_5G_FREQUENCIES = []
+ NONE_DFS_5G_FREQUENCIES = []
+ ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
+ MIX_CHANNEL_SCAN = []
+
+ def band_to_freq(self, band):
+ _band_to_frequencies = {
+ WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
+ WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
+ WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
+ WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
+ WifiEnums.WIFI_BAND_BOTH:
+ self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
+ WifiEnums.WIFI_BAND_BOTH_WITH_DFS:
+ self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
+ }
+ return _band_to_frequencies[band]
+
+
+class WifiChannelUS(WifiChannelBase):
+ # US Wifi frequencies
+ ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
+ 2457, 2462]
+ NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
+ 5825]
+ MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300, 5500,
+ 5320, 5520, 5560, 5700, 5745, 5805]
+
+ def __init__(self, model=None):
+ self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
+ 5540, 5560, 5580, 5600, 5620, 5640,
+ 5660, 5680, 5700, 5720]
+ self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
+
+
+class WifiReferenceNetworks:
+ """ Class to parse and return networks of different band and
+ auth type from reference_networks
+ """
+ def __init__(self, obj):
+ self.reference_networks = obj
+ self.WIFI_2G = "2g"
+ self.WIFI_5G = "5g"
+
+ self.secure_networks_2g = []
+ self.secure_networks_5g = []
+ self.open_networks_2g = []
+ self.open_networks_5g = []
+ self._parse_networks()
+
+ def _parse_networks(self):
+ for network in self.reference_networks:
+ for key in network:
+ if key == self.WIFI_2G:
+ if "password" in network[key]:
+ self.secure_networks_2g.append(network[key])
+ else:
+ self.open_networks_2g.append(network[key])
+ else:
+ if "password" in network[key]:
+ self.secure_networks_5g.append(network[key])
+ else:
+ self.open_networks_5g.append(network[key])
+
+ def return_2g_secure_networks(self):
+ return self.secure_networks_2g
+
+ def return_5g_secure_networks(self):
+ return self.secure_networks_5g
+
+ def return_2g_open_networks(self):
+ return self.open_networks_2g
+
+ def return_5g_open_networks(self):
+ return self.open_networks_5g
+
+ def return_secure_networks(self):
+ return self.secure_networks_2g + self.secure_networks_5g
+
+ def return_open_networks(self):
+ return self.open_networks_2g + self.open_networks_5g
+
+
+def _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
+ """Wrapper function that handles the bahevior of assert_on_fail.
+
+ When assert_on_fail is True, let all test signals through, which can
+ terminate test cases directly. When assert_on_fail is False, the wrapper
+ raises no test signals and reports operation status by returning True or
+ False.
+
+ Args:
+ func: The function to wrap. This function reports operation status by
+ raising test signals.
+ assert_on_fail: A boolean that specifies if the output of the wrapper
+ is test signal based or return value based.
+ args: Positional args for func.
+ kwargs: Name args for func.
+
+ Returns:
+ If assert_on_fail is True, returns True/False to signal operation
+ status, otherwise return nothing.
+ """
+ try:
+ func(*args, **kwargs)
+ if not assert_on_fail:
+ return True
+ except signals.TestSignal:
+ if assert_on_fail:
+ raise
+ return False
+
+
+def assert_network_in_list(target, network_list):
+ """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
+ networks.
+
+ Args:
+ target: A dict representing a Wi-Fi network.
+ E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
+ network_list: A list of dicts, each representing a Wi-Fi network.
+ """
+ match_results = match_networks(target, network_list)
+ asserts.assert_true(
+ match_results, "Target network %s, does not exist in network list %s" %
+ (target, network_list))
+
+
+def match_networks(target_params, networks):
+ """Finds the WiFi networks that match a given set of parameters in a list
+ of WiFi networks.
+
+ To be considered a match, the network should contain every key-value pair
+ of target_params
+
+ Args:
+ target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
+ E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
+ networks: A list of dict objects representing WiFi networks.
+
+ Returns:
+ The networks that match the target parameters.
+ """
+ results = []
+ asserts.assert_true(target_params,
+ "Expected networks object 'target_params' is empty")
+ for n in networks:
+ add_network = 1
+ for k, v in target_params.items():
+ if k not in n:
+ add_network = 0
+ break
+ if n[k] != v:
+ add_network = 0
+ break
+ if add_network:
+ results.append(n)
+ return results
+
+
+def wait_for_wifi_state(ad, state, assert_on_fail=True):
+ """Waits for the device to transition to the specified wifi state
+
+ Args:
+ ad: An AndroidDevice object.
+ state: Wifi state to wait for.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ If assert_on_fail is False, function returns True if the device transitions
+ to the specified state, False otherwise. If assert_on_fail is True, no return value.
+ """
+ return _assert_on_fail_handler(
+ _wait_for_wifi_state, assert_on_fail, ad, state=state)
+
+
+def _wait_for_wifi_state(ad, state):
+ """Toggles the state of wifi.
+
+ TestFailure signals are raised when something goes wrong.
+
+ Args:
+ ad: An AndroidDevice object.
+ state: Wifi state to wait for.
+ """
+ if state == ad.droid.wifiCheckState():
+ # Check if the state is already achieved, so we don't wait for the
+ # state change event by mistake.
+ return
+ ad.droid.wifiStartTrackingStateChange()
+ fail_msg = "Device did not transition to Wi-Fi state to %s on %s." % (state,
+ ad.serial)
+ try:
+ ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
+ lambda x: x["data"]["enabled"] == state,
+ SHORT_TIMEOUT)
+ except Empty:
+ asserts.assert_equal(state, ad.droid.wifiCheckState(), fail_msg)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
+ """Toggles the state of wifi.
+
+ Args:
+ ad: An AndroidDevice object.
+ new_state: Wifi state to set to. If None, opposite of the current state.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ If assert_on_fail is False, function returns True if the toggle was
+ successful, False otherwise. If assert_on_fail is True, no return value.
+ """
+ return _assert_on_fail_handler(
+ _wifi_toggle_state, assert_on_fail, ad, new_state=new_state)
+
+
+def _wifi_toggle_state(ad, new_state=None):
+ """Toggles the state of wifi.
+
+ TestFailure signals are raised when something goes wrong.
+
+ Args:
+ ad: An AndroidDevice object.
+ new_state: The state to set Wi-Fi to. If None, opposite of the current
+ state will be set.
+ """
+ if new_state is None:
+ new_state = not ad.droid.wifiCheckState()
+ elif new_state == ad.droid.wifiCheckState():
+ # Check if the new_state is already achieved, so we don't wait for the
+ # state change event by mistake.
+ return
+ ad.droid.wifiStartTrackingStateChange()
+ ad.log.info("Setting Wi-Fi state to %s.", new_state)
+ ad.ed.clear_all_events()
+ # Setting wifi state.
+ ad.droid.wifiToggleState(new_state)
+ time.sleep(2)
+ fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
+ ad.serial)
+ try:
+ ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
+ lambda x: x["data"]["enabled"] == new_state,
+ SHORT_TIMEOUT)
+ except Empty:
+ asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def reset_wifi(ad):
+ """Clears all saved Wi-Fi networks on a device.
+
+ This will turn Wi-Fi on.
+
+ Args:
+ ad: An AndroidDevice object.
+
+ """
+ networks = ad.droid.wifiGetConfiguredNetworks()
+ if not networks:
+ return
+ for n in networks:
+ ad.droid.wifiForgetNetwork(n['networkId'])
+ try:
+ event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
+ SHORT_TIMEOUT)
+ except Empty:
+ logging.warning("Could not confirm the removal of network %s.", n)
+ # Check again to see if there's any network left.
+ asserts.assert_true(
+ not ad.droid.wifiGetConfiguredNetworks(),
+ "Failed to remove these configured Wi-Fi networks: %s" % networks)
+
+
+def toggle_airplane_mode_on_and_off(ad):
+ """Turn ON and OFF Airplane mode.
+
+ ad: An AndroidDevice object.
+ Returns: Assert if turning on/off Airplane mode fails.
+
+ """
+ ad.log.debug("Toggling Airplane mode ON.")
+ asserts.assert_true(
+ utils.force_airplane_mode(ad, True),
+ "Can not turn on airplane mode on: %s" % ad.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+ ad.log.debug("Toggling Airplane mode OFF.")
+ asserts.assert_true(
+ utils.force_airplane_mode(ad, False),
+ "Can not turn on airplane mode on: %s" % ad.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+
+
+def toggle_wifi_off_and_on(ad):
+ """Turn OFF and ON WiFi.
+
+ ad: An AndroidDevice object.
+ Returns: Assert if turning off/on WiFi fails.
+
+ """
+ ad.log.debug("Toggling wifi OFF.")
+ wifi_toggle_state(ad, False)
+ time.sleep(DEFAULT_TIMEOUT)
+ ad.log.debug("Toggling wifi ON.")
+ wifi_toggle_state(ad, True)
+ time.sleep(DEFAULT_TIMEOUT)
+
+
+def wifi_forget_network(ad, net_ssid):
+ """Remove configured Wifi network on an android device.
+
+ Args:
+ ad: android_device object for forget network.
+ net_ssid: ssid of network to be forget
+
+ """
+ networks = ad.droid.wifiGetConfiguredNetworks()
+ if not networks:
+ return
+ for n in networks:
+ if net_ssid in n[WifiEnums.SSID_KEY]:
+ ad.droid.wifiForgetNetwork(n['networkId'])
+ try:
+ event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
+ SHORT_TIMEOUT)
+ except Empty:
+ asserts.fail("Failed to remove network %s." % n)
+
+
+def wifi_test_device_init(ad):
+ """Initializes an android device for wifi testing.
+
+ 0. Make sure SL4A connection is established on the android device.
+ 1. Disable location service's WiFi scan.
+ 2. Turn WiFi on.
+ 3. Clear all saved networks.
+ 4. Set country code to US.
+ 5. Enable WiFi verbose logging.
+ 6. Sync device time with computer time.
+ 7. Turn off cellular data.
+ 8. Turn off ambient display.
+ """
+ utils.require_sl4a((ad, ))
+ ad.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
+ wifi_toggle_state(ad, True)
+ reset_wifi(ad)
+ ad.droid.wifiEnableVerboseLogging(1)
+ msg = "Failed to enable WiFi verbose logging."
+ asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
+ # We don't verify the following settings since they are not critical.
+ # Set wpa_supplicant log level to EXCESSIVE.
+ output = ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
+ "wlan0 log_level EXCESSIVE")
+ ad.log.info("wpa_supplicant log change status: %s", output)
+ utils.sync_device_time(ad)
+ ad.droid.telephonyToggleDataConnection(False)
+ set_wifi_country_code(ad, WifiEnums.CountryCode.US)
+ utils.set_ambient_display(ad, False)
+
+def set_wifi_country_code(ad, country_code):
+ """Sets the wifi country code on the device.
+
+ Args:
+ ad: An AndroidDevice object.
+ country_code: 2 letter ISO country code
+
+ Raises:
+ An RpcException if unable to set the country code.
+ """
+ try:
+ ad.adb.shell("cmd wifi force-country-code enabled %s" % country_code)
+ except ad.adb.AdbError as e:
+ ad.droid.wifiSetCountryCode(WifiEnums.CountryCode.US)
+
+
+def start_wifi_connection_scan(ad):
+ """Starts a wifi connection scan and wait for results to become available.
+
+ Args:
+ ad: An AndroidDevice object.
+ """
+ ad.ed.clear_all_events()
+ ad.droid.wifiStartScan()
+ try:
+ ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
+ except Empty:
+ asserts.fail("Wi-Fi results did not become available within 60s.")
+
+
+def start_wifi_connection_scan_and_return_status(ad):
+ """
+ Starts a wifi connection scan and wait for results to become available
+ or a scan failure to be reported.
+
+ Args:
+ ad: An AndroidDevice object.
+ Returns:
+ True: if scan succeeded & results are available
+ False: if scan failed
+ """
+ ad.ed.clear_all_events()
+ ad.droid.wifiStartScan()
+ try:
+ events = ad.ed.pop_events(
+ "WifiManagerScan(ResultsAvailable|Failure)", 60)
+ except Empty:
+ asserts.fail(
+ "Wi-Fi scan results/failure did not become available within 60s.")
+ # If there are multiple matches, we check for atleast one success.
+ for event in events:
+ if event["name"] == "WifiManagerScanResultsAvailable":
+ return True
+ elif event["name"] == "WifiManagerScanFailure":
+ ad.log.debug("Scan failure received")
+ return False
+
+
+def start_wifi_connection_scan_and_check_for_network(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & checks if the |network_ssid| is seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ Returns:
+ True: if network_ssid is found in scan results.
+ False: if network_ssid is not found in scan results.
+ """
+ for num_tries in range(max_tries):
+ if start_wifi_connection_scan_and_return_status(ad):
+ scan_results = ad.droid.wifiGetScanResults()
+ match_results = match_networks(
+ {WifiEnums.SSID_KEY: network_ssid}, scan_results)
+ if len(match_results) > 0:
+ return True
+ return False
+
+
+def start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & ensure the |network_ssid| is seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+ This method asserts on failure!
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ """
+ ad.log.info("Starting scans to ensure %s is present", network_ssid)
+ assert_msg = "Failed to find " + network_ssid + " in scan results" \
+ " after " + str(max_tries) + " tries"
+ asserts.assert_true(start_wifi_connection_scan_and_check_for_network(
+ ad, network_ssid, max_tries), assert_msg)
+
+
+def start_wifi_connection_scan_and_ensure_network_not_found(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & ensure the |network_ssid| is not seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+ This method asserts on failure!
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ """
+ ad.log.info("Starting scans to ensure %s is not present", network_ssid)
+ assert_msg = "Found " + network_ssid + " in scan results" \
+ " after " + str(max_tries) + " tries"
+ asserts.assert_false(start_wifi_connection_scan_and_check_for_network(
+ ad, network_ssid, max_tries), assert_msg)
+
+
+def start_wifi_background_scan(ad, scan_setting):
+ """Starts wifi background scan.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ scan_setting: A dict representing the settings of the scan.
+
+ Returns:
+ If scan was started successfully, event data of success event is returned.
+ """
+ idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
+ event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
+ SHORT_TIMEOUT)
+ return event['data']
+
+
+def start_wifi_tethering(ad, ssid, password, band=None, hidden=None):
+ """Starts wifi tethering on an android_device.
+
+ Args:
+ ad: android_device to start wifi tethering on.
+ ssid: The SSID the soft AP should broadcast.
+ password: The password the soft AP should use.
+ band: The band the soft AP should be set on. It should be either
+ WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
+ hidden: boolean to indicate if the AP needs to be hidden or not.
+
+ Returns:
+ No return value. Error checks in this function will raise test failure signals
+ """
+ config = {WifiEnums.SSID_KEY: ssid}
+ if password:
+ config[WifiEnums.PWD_KEY] = password
+ if band:
+ config[WifiEnums.AP_BAND_KEY] = band
+ if hidden:
+ config[WifiEnums.HIDDEN_KEY] = hidden
+ asserts.assert_true(
+ ad.droid.wifiSetWifiApConfiguration(config),
+ "Failed to update WifiAp Configuration")
+ ad.droid.wifiStartTrackingTetherStateChange()
+ ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
+ try:
+ ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
+ ad.ed.wait_for_event("TetherStateChanged",
+ lambda x: x["data"]["ACTIVE_TETHER"], 30)
+ ad.log.debug("Tethering started successfully.")
+ except Empty:
+ msg = "Failed to receive confirmation of wifi tethering starting"
+ asserts.fail(msg)
+ finally:
+ ad.droid.wifiStopTrackingTetherStateChange()
+
+
+def save_wifi_soft_ap_config(ad, wifi_config, band=None, hidden=None,
+ security=None, password=None,
+ channel=None, max_clients=None,
+ shutdown_timeout_enable=None,
+ shutdown_timeout_millis=None,
+ client_control_enable=None,
+ allowedList=None, blockedList=None):
+ """ Save a soft ap configuration and verified
+ Args:
+ ad: android_device to set soft ap configuration.
+ wifi_config: a soft ap configuration object, at least include SSID.
+ band: specifies the band for the soft ap.
+ hidden: specifies the soft ap need to broadcast its SSID or not.
+ security: specifies the security type for the soft ap.
+ password: specifies the password for the soft ap.
+ channel: specifies the channel for the soft ap.
+ max_clients: specifies the maximum connected client number.
+ shutdown_timeout_enable: specifies the auto shut down enable or not.
+ shutdown_timeout_millis: specifies the shut down timeout value.
+ client_control_enable: specifies the client control enable or not.
+ allowedList: specifies allowed clients list.
+ blockedList: specifies blocked clients list.
+ """
+ if security and password:
+ wifi_config[WifiEnums.SECURITY] = security
+ wifi_config[WifiEnums.PWD_KEY] = password
+ if band:
+ wifi_config[WifiEnums.AP_BAND_KEY] = band
+ if hidden:
+ wifi_config[WifiEnums.HIDDEN_KEY] = hidden
+ if channel and band:
+ wifi_config[WifiEnums.AP_BAND_KEY] = band
+ wifi_config[WifiEnums.AP_CHANNEL_KEY] = channel
+ if max_clients:
+ wifi_config[WifiEnums.AP_MAXCLIENTS_KEY] = max_clients
+ if shutdown_timeout_enable:
+ wifi_config[
+ WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] = shutdown_timeout_enable
+ if shutdown_timeout_millis:
+ wifi_config[
+ WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] = shutdown_timeout_millis
+ if client_control_enable:
+ wifi_config[WifiEnums.AP_CLIENTCONTROL_KEY] = client_control_enable
+ if allowedList:
+ wifi_config[WifiEnums.AP_ALLOWEDLIST_KEY] = allowedList
+ if blockedList:
+ wifi_config[WifiEnums.AP_BLOCKEDLIST_KEY] = blockedList
+
+ if WifiEnums.AP_CHANNEL_KEY in wifi_config and wifi_config[
+ WifiEnums.AP_CHANNEL_KEY] == 0:
+ del wifi_config[WifiEnums.AP_CHANNEL_KEY]
+
+ if WifiEnums.SECURITY in wifi_config and wifi_config[
+ WifiEnums.SECURITY] == WifiEnums.SoftApSecurityType.OPEN:
+ del wifi_config[WifiEnums.SECURITY]
+ del wifi_config[WifiEnums.PWD_KEY]
+
+ asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(wifi_config),
+ "Failed to set WifiAp Configuration")
+
+ wifi_ap = ad.droid.wifiGetApConfiguration()
+ asserts.assert_true(
+ wifi_ap[WifiEnums.SSID_KEY] == wifi_config[WifiEnums.SSID_KEY],
+ "Hotspot SSID doesn't match")
+ if WifiEnums.SECURITY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.SECURITY] == wifi_config[WifiEnums.SECURITY],
+ "Hotspot Security doesn't match")
+ if WifiEnums.PWD_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.PWD_KEY] == wifi_config[WifiEnums.PWD_KEY],
+ "Hotspot Password doesn't match")
+
+ if WifiEnums.HIDDEN_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.HIDDEN_KEY] == wifi_config[WifiEnums.HIDDEN_KEY],
+ "Hotspot hidden setting doesn't match")
+
+ if WifiEnums.AP_BAND_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_BAND_KEY] == wifi_config[WifiEnums.AP_BAND_KEY],
+ "Hotspot Band doesn't match")
+ if WifiEnums.AP_CHANNEL_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_CHANNEL_KEY] == wifi_config[
+ WifiEnums.AP_CHANNEL_KEY], "Hotspot Channel doesn't match")
+ if WifiEnums.AP_MAXCLIENTS_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_MAXCLIENTS_KEY] == wifi_config[
+ WifiEnums.AP_MAXCLIENTS_KEY], "Hotspot Max Clients doesn't match")
+ if WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] == wifi_config[
+ WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY],
+ "Hotspot ShutDown feature flag doesn't match")
+ if WifiEnums.AP_SHUTDOWNTIMEOUT_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] == wifi_config[
+ WifiEnums.AP_SHUTDOWNTIMEOUT_KEY],
+ "Hotspot ShutDown timeout setting doesn't match")
+ if WifiEnums.AP_CLIENTCONTROL_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_CLIENTCONTROL_KEY] == wifi_config[
+ WifiEnums.AP_CLIENTCONTROL_KEY],
+ "Hotspot Client control flag doesn't match")
+ if WifiEnums.AP_ALLOWEDLIST_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_ALLOWEDLIST_KEY] == wifi_config[
+ WifiEnums.AP_ALLOWEDLIST_KEY],
+ "Hotspot Allowed List doesn't match")
+ if WifiEnums.AP_BLOCKEDLIST_KEY in wifi_config:
+ asserts.assert_true(
+ wifi_ap[WifiEnums.AP_BLOCKEDLIST_KEY] == wifi_config[
+ WifiEnums.AP_BLOCKEDLIST_KEY],
+ "Hotspot Blocked List doesn't match")
+
+def start_wifi_tethering_saved_config(ad):
+ """ Turn on wifi hotspot with a config that is already saved """
+ ad.droid.wifiStartTrackingTetherStateChange()
+ ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
+ try:
+ ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
+ ad.ed.wait_for_event("TetherStateChanged",
+ lambda x: x["data"]["ACTIVE_TETHER"], 30)
+ except:
+ asserts.fail("Didn't receive wifi tethering starting confirmation")
+ finally:
+ ad.droid.wifiStopTrackingTetherStateChange()
+
+
+def stop_wifi_tethering(ad):
+ """Stops wifi tethering on an android_device.
+ Args:
+ ad: android_device to stop wifi tethering on.
+ """
+ ad.droid.wifiStartTrackingTetherStateChange()
+ ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
+ try:
+ ad.ed.pop_event("WifiManagerApDisabled", 30)
+ ad.ed.wait_for_event("TetherStateChanged",
+ lambda x: not x["data"]["ACTIVE_TETHER"], 30)
+ except Empty:
+ msg = "Failed to receive confirmation of wifi tethering stopping"
+ asserts.fail(msg)
+ finally:
+ ad.droid.wifiStopTrackingTetherStateChange()
+
+
+def toggle_wifi_and_wait_for_reconnection(ad,
+ network,
+ num_of_tries=1,
+ assert_on_fail=True):
+ """Toggle wifi state and then wait for Android device to reconnect to
+ the provided wifi network.
+
+ This expects the device to be already connected to the provided network.
+
+ Logic steps are
+ 1. Ensure that we're connected to the network.
+ 2. Turn wifi off.
+ 3. Wait for 10 seconds.
+ 4. Turn wifi on.
+ 5. Wait for the "connected" event, then confirm the connected ssid is the
+ one requested.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to await connection. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ If assert_on_fail is False, function returns True if the toggle was
+ successful, False otherwise. If assert_on_fail is True, no return value.
+ """
+ return _assert_on_fail_handler(
+ _toggle_wifi_and_wait_for_reconnection,
+ assert_on_fail,
+ ad,
+ network,
+ num_of_tries=num_of_tries)
+
+
+def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=3):
+ """Toggle wifi state and then wait for Android device to reconnect to
+ the provided wifi network.
+
+ This expects the device to be already connected to the provided network.
+
+ Logic steps are
+ 1. Ensure that we're connected to the network.
+ 2. Turn wifi off.
+ 3. Wait for 10 seconds.
+ 4. Turn wifi on.
+ 5. Wait for the "connected" event, then confirm the connected ssid is the
+ one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to await connection. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ """
+ expected_ssid = network[WifiEnums.SSID_KEY]
+ # First ensure that we're already connected to the provided network.
+ verify_con = {WifiEnums.SSID_KEY: expected_ssid}
+ verify_wifi_connection_info(ad, verify_con)
+ # Now toggle wifi state and wait for the connection event.
+ wifi_toggle_state(ad, False)
+ time.sleep(10)
+ wifi_toggle_state(ad, True)
+ ad.droid.wifiStartTrackingStateChange()
+ try:
+ connect_result = None
+ for i in range(num_of_tries):
+ try:
+ connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
+ 30)
+ break
+ except Empty:
+ pass
+ asserts.assert_true(connect_result,
+ "Failed to connect to Wi-Fi network %s on %s" %
+ (network, ad.serial))
+ logging.debug("Connection result on %s: %s.", ad.serial,
+ connect_result)
+ actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+ asserts.assert_equal(actual_ssid, expected_ssid,
+ "Connected to the wrong network on %s."
+ "Expected %s, but got %s." %
+ (ad.serial, expected_ssid, actual_ssid))
+ logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
+ ad.serial)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def wait_for_connect(ad, expected_ssid=None, expected_id=None, tries=2,
+ assert_on_fail=True):
+ """Wait for a connect event.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: An Android device object.
+ expected_ssid: SSID of the network to connect to.
+ expected_id: Network Id of the network to connect to.
+ tries: An integer that is the number of times to try before failing.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ Returns a value only if assert_on_fail is false.
+ Returns True if the connection was successful, False otherwise.
+ """
+ return _assert_on_fail_handler(
+ _wait_for_connect, assert_on_fail, ad, expected_ssid, expected_id,
+ tries)
+
+
+def _wait_for_connect(ad, expected_ssid=None, expected_id=None, tries=2):
+ """Wait for a connect event.
+
+ Args:
+ ad: An Android device object.
+ expected_ssid: SSID of the network to connect to.
+ expected_id: Network Id of the network to connect to.
+ tries: An integer that is the number of times to try before failing.
+ """
+ ad.droid.wifiStartTrackingStateChange()
+ try:
+ connect_result = _wait_for_connect_event(
+ ad, ssid=expected_ssid, id=expected_id, tries=tries)
+ asserts.assert_true(connect_result,
+ "Failed to connect to Wi-Fi network %s" %
+ expected_ssid)
+ ad.log.debug("Wi-Fi connection result: %s.", connect_result)
+ actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+ if expected_ssid:
+ asserts.assert_equal(actual_ssid, expected_ssid,
+ "Connected to the wrong network")
+ actual_id = connect_result['data'][WifiEnums.NETID_KEY]
+ if expected_id:
+ asserts.assert_equal(actual_id, expected_id,
+ "Connected to the wrong network")
+ ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
+ except Empty:
+ asserts.fail("Failed to start connection process to %s" %
+ expected_ssid)
+ except Exception as error:
+ ad.log.error("Failed to connect to %s with error %s", expected_ssid,
+ error)
+ raise signals.TestFailure("Failed to connect to %s network" %
+ expected_ssid)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def _wait_for_connect_event(ad, ssid=None, id=None, tries=1):
+ """Wait for a connect event on queue and pop when available.
+
+ Args:
+ ad: An Android device object.
+ ssid: SSID of the network to connect to.
+ id: Network Id of the network to connect to.
+ tries: An integer that is the number of times to try before failing.
+
+ Returns:
+ A dict with details of the connection data, which looks like this:
+ {
+ 'time': 1485460337798,
+ 'name': 'WifiNetworkConnected',
+ 'data': {
+ 'rssi': -27,
+ 'is_24ghz': True,
+ 'mac_address': '02:00:00:00:00:00',
+ 'network_id': 1,
+ 'BSSID': '30:b5:c2:33:d3:fc',
+ 'ip_address': 117483712,
+ 'link_speed': 54,
+ 'supplicant_state': 'completed',
+ 'hidden_ssid': False,
+ 'SSID': 'wh_ap1_2g',
+ 'is_5ghz': False}
+ }
+
+ """
+ conn_result = None
+
+ # If ssid and network id is None, just wait for any connect event.
+ if id is None and ssid is None:
+ for i in range(tries):
+ try:
+ conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
+ break
+ except Empty:
+ pass
+ else:
+ # If ssid or network id is specified, wait for specific connect event.
+ for i in range(tries):
+ try:
+ conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
+ if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
+ break
+ elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
+ break
+ except Empty:
+ pass
+
+ return conn_result
+
+
+def wait_for_disconnect(ad, timeout=10):
+ """Wait for a disconnect event within the specified timeout.
+
+ Args:
+ ad: Android device object.
+ timeout: Timeout in seconds.
+
+ """
+ try:
+ ad.droid.wifiStartTrackingStateChange()
+ event = ad.ed.pop_event("WifiNetworkDisconnected", timeout)
+ except Empty:
+ raise signals.TestFailure("Device did not disconnect from the network")
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def ensure_no_disconnect(ad, duration=10):
+ """Ensure that there is no disconnect for the specified duration.
+
+ Args:
+ ad: Android device object.
+ duration: Duration in seconds.
+
+ """
+ try:
+ ad.droid.wifiStartTrackingStateChange()
+ event = ad.ed.pop_event("WifiNetworkDisconnected", duration)
+ raise signals.TestFailure("Device disconnected from the network")
+ except Empty:
+ pass
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def connect_to_wifi_network(ad, network, assert_on_fail=True,
+ check_connectivity=True, hidden=False):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ ad: AndroidDevice to use for connection
+ network: network info of the network to connect to
+ assert_on_fail: If true, errors from wifi_connect will raise
+ test failure signals.
+ hidden: Is the Wifi network hidden.
+ """
+ if hidden:
+ start_wifi_connection_scan_and_ensure_network_not_found(
+ ad, network[WifiEnums.SSID_KEY])
+ else:
+ start_wifi_connection_scan_and_ensure_network_found(
+ ad, network[WifiEnums.SSID_KEY])
+ wifi_connect(ad,
+ network,
+ num_of_tries=3,
+ assert_on_fail=assert_on_fail,
+ check_connectivity=check_connectivity)
+
+
+def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
+ """Connect to the given network using network id and verify SSID.
+
+ Args:
+ network_id: int Network Id of the network.
+ network_ssid: string SSID of the network.
+
+ Returns: True if connect using network id was successful;
+ False otherwise.
+
+ """
+ start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid)
+ wifi_connect_by_id(ad, network_id)
+ connect_data = ad.droid.wifiGetConnectionInfo()
+ connect_ssid = connect_data[WifiEnums.SSID_KEY]
+ ad.log.debug("Expected SSID = %s Connected SSID = %s" %
+ (network_ssid, connect_ssid))
+ if connect_ssid != network_ssid:
+ return False
+ return True
+
+
+def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True,
+ check_connectivity=True):
+ """Connect an Android device to a wifi network.
+
+ Initiate connection to a wifi network, wait for the "connected" event, then
+ confirm the connected ssid is the one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ Returns a value only if assert_on_fail is false.
+ Returns True if the connection was successful, False otherwise.
+ """
+ return _assert_on_fail_handler(
+ _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries,
+ check_connectivity=check_connectivity)
+
+
+def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True):
+ """Connect an Android device to a wifi network.
+
+ Initiate connection to a wifi network, wait for the "connected" event, then
+ confirm the connected ssid is the one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ """
+ asserts.assert_true(WifiEnums.SSID_KEY in network,
+ "Key '%s' must be present in network definition." %
+ WifiEnums.SSID_KEY)
+ ad.droid.wifiStartTrackingStateChange()
+ expected_ssid = network[WifiEnums.SSID_KEY]
+ ad.droid.wifiConnectByConfig(network)
+ ad.log.info("Starting connection process to %s", expected_ssid)
+ try:
+ event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
+ connect_result = _wait_for_connect_event(
+ ad, ssid=expected_ssid, tries=num_of_tries)
+ asserts.assert_true(connect_result,
+ "Failed to connect to Wi-Fi network %s on %s" %
+ (network, ad.serial))
+ ad.log.debug("Wi-Fi connection result: %s.", connect_result)
+ actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+ asserts.assert_equal(actual_ssid, expected_ssid,
+ "Connected to the wrong network on %s." %
+ ad.serial)
+ ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
+
+ if check_connectivity:
+ internet = validate_connection(ad, DEFAULT_PING_ADDR)
+ if not internet:
+ raise signals.TestFailure("Failed to connect to internet on %s" %
+ expected_ssid)
+ except Empty:
+ asserts.fail("Failed to start connection process to %s on %s" %
+ (network, ad.serial))
+ except Exception as error:
+ ad.log.error("Failed to connect to %s with error %s", expected_ssid,
+ error)
+ raise signals.TestFailure("Failed to connect to %s network" % network)
+
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
+ """Connect an Android device to a wifi network using network Id.
+
+ Start connection to the wifi network, with the given network Id, wait for
+ the "connected" event, then verify the connected network is the one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network_id: Integer specifying the network id of the network.
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ Returns a value only if assert_on_fail is false.
+ Returns True if the connection was successful, False otherwise.
+ """
+ _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
+ network_id, num_of_tries)
+
+
+def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
+ """Connect an Android device to a wifi network using it's network id.
+
+ Start connection to the wifi network, with the given network id, wait for
+ the "connected" event, then verify the connected network is the one requested.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network_id: Integer specifying the network id of the network.
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ """
+ ad.droid.wifiStartTrackingStateChange()
+ # Clear all previous events.
+ ad.ed.clear_all_events()
+ ad.droid.wifiConnectByNetworkId(network_id)
+ ad.log.info("Starting connection to network with id %d", network_id)
+ try:
+ event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
+ connect_result = _wait_for_connect_event(
+ ad, id=network_id, tries=num_of_tries)
+ asserts.assert_true(connect_result,
+ "Failed to connect to Wi-Fi network using network id")
+ ad.log.debug("Wi-Fi connection result: %s", connect_result)
+ actual_id = connect_result['data'][WifiEnums.NETID_KEY]
+ asserts.assert_equal(actual_id, network_id,
+ "Connected to the wrong network on %s."
+ "Expected network id = %d, but got %d." %
+ (ad.serial, network_id, actual_id))
+ expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+ ad.log.info("Connected to Wi-Fi network %s with %d network id.",
+ expected_ssid, network_id)
+
+ internet = validate_connection(ad, DEFAULT_PING_ADDR)
+ if not internet:
+ raise signals.TestFailure("Failed to connect to internet on %s" %
+ expected_ssid)
+ except Empty:
+ asserts.fail("Failed to connect to network with id %d on %s" %
+ (network_id, ad.serial))
+ except Exception as error:
+ ad.log.error("Failed to connect to network with id %d with error %s",
+ network_id, error)
+ raise signals.TestFailure("Failed to connect to network with network"
+ " id %d" % network_id)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def wifi_connect_using_network_request(ad, network, network_specifier,
+ num_of_tries=3, assert_on_fail=True):
+ """Connect an Android device to a wifi network using network request.
+
+ Trigger a network request with the provided network specifier,
+ wait for the "onMatch" event, ensure that the scan results in "onMatch"
+ event contain the specified network, then simulate the user granting the
+ request with the specified network selected. Then wait for the "onAvailable"
+ network callback indicating successful connection to network.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network_specifier: A dictionary representing the network specifier to
+ use.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ Returns a value only if assert_on_fail is false.
+ Returns True if the connection was successful, False otherwise.
+ """
+ _assert_on_fail_handler(_wifi_connect_using_network_request, assert_on_fail,
+ ad, network, network_specifier, num_of_tries)
+
+
+def _wifi_connect_using_network_request(ad, network, network_specifier,
+ num_of_tries=3):
+ """Connect an Android device to a wifi network using network request.
+
+ Trigger a network request with the provided network specifier,
+ wait for the "onMatch" event, ensure that the scan results in "onMatch"
+ event contain the specified network, then simulate the user granting the
+ request with the specified network selected. Then wait for the "onAvailable"
+ network callback indicating successful connection to network.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network_specifier: A dictionary representing the network specifier to
+ use.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure.
+ """
+ ad.droid.wifiRequestNetworkWithSpecifier(network_specifier)
+ ad.log.info("Sent network request with %s", network_specifier)
+ # Need a delay here because UI interaction should only start once wifi
+ # starts processing the request.
+ time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC)
+ _wait_for_wifi_connect_after_network_request(ad, network, num_of_tries)
+
+
+def wait_for_wifi_connect_after_network_request(ad, network, num_of_tries=3,
+ assert_on_fail=True):
+ """
+ Simulate and verify the connection flow after initiating the network
+ request.
+
+ Wait for the "onMatch" event, ensure that the scan results in "onMatch"
+ event contain the specified network, then simulate the user granting the
+ request with the specified network selected. Then wait for the "onAvailable"
+ network callback indicating successful connection to network.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ Returns a value only if assert_on_fail is false.
+ Returns True if the connection was successful, False otherwise.
+ """
+ _assert_on_fail_handler(_wait_for_wifi_connect_after_network_request,
+ assert_on_fail, ad, network, num_of_tries)
+
+
+def _wait_for_wifi_connect_after_network_request(ad, network, num_of_tries=3):
+ """
+ Simulate and verify the connection flow after initiating the network
+ request.
+
+ Wait for the "onMatch" event, ensure that the scan results in "onMatch"
+ event contain the specified network, then simulate the user granting the
+ request with the specified network selected. Then wait for the "onAvailable"
+ network callback indicating successful connection to network.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ network: A dictionary representing the network to connect to. The
+ dictionary must have the key "SSID".
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure.
+ """
+ asserts.assert_true(WifiEnums.SSID_KEY in network,
+ "Key '%s' must be present in network definition." %
+ WifiEnums.SSID_KEY)
+ ad.droid.wifiStartTrackingStateChange()
+ expected_ssid = network[WifiEnums.SSID_KEY]
+ ad.droid.wifiRegisterNetworkRequestMatchCallback()
+ # Wait for the platform to scan and return a list of networks
+ # matching the request
+ try:
+ matched_network = None
+ for _ in [0, num_of_tries]:
+ on_match_event = ad.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH, 60)
+ asserts.assert_true(on_match_event,
+ "Network request on match not received.")
+ matched_scan_results = on_match_event["data"]
+ ad.log.debug("Network request on match results %s",
+ matched_scan_results)
+ matched_network = match_networks(
+ {WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]},
+ matched_scan_results)
+ if matched_network:
+ break;
+
+ asserts.assert_true(
+ matched_network, "Target network %s not found" % network)
+
+ ad.droid.wifiSendUserSelectionForNetworkRequestMatch(network)
+ ad.log.info("Sent user selection for network request %s",
+ expected_ssid)
+
+ # Wait for the platform to connect to the network.
+ on_available_event = ad.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_CB_ON_AVAILABLE, 60)
+ asserts.assert_true(on_available_event,
+ "Network request on available not received.")
+ connected_network = on_available_event["data"]
+ ad.log.info("Connected to network %s", connected_network)
+ asserts.assert_equal(connected_network[WifiEnums.SSID_KEY],
+ expected_ssid,
+ "Connected to the wrong network."
+ "Expected %s, but got %s." %
+ (network, connected_network))
+ except Empty:
+ asserts.fail("Failed to connect to %s" % expected_ssid)
+ except Exception as error:
+ ad.log.error("Failed to connect to %s with error %s",
+ (expected_ssid, error))
+ raise signals.TestFailure("Failed to connect to %s network" % network)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1,
+ assert_on_fail=True):
+ """Connect an Android device to a wifi network.
+
+ Initiate connection to a wifi network, wait for the "connected" event, then
+ confirm the connected ssid is the one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ passpoint_network: SSID of the Passpoint network to connect to.
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ If assert_on_fail is False, function returns network id, if the connect was
+ successful, False otherwise. If assert_on_fail is True, no return value.
+ """
+ _assert_on_fail_handler(_wifi_passpoint_connect, assert_on_fail, ad,
+ passpoint_network, num_of_tries = num_of_tries)
+
+
+def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
+ """Connect an Android device to a wifi network.
+
+ Initiate connection to a wifi network, wait for the "connected" event, then
+ confirm the connected ssid is the one requested.
+
+ This will directly fail a test if anything goes wrong.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ passpoint_network: SSID of the Passpoint network to connect to.
+ num_of_tries: An integer that is the number of times to try before
+ delaring failure. Default is 1.
+ """
+ ad.droid.wifiStartTrackingStateChange()
+ expected_ssid = passpoint_network
+ ad.log.info("Starting connection process to passpoint %s", expected_ssid)
+
+ try:
+ connect_result = _wait_for_connect_event(
+ ad, expected_ssid, num_of_tries)
+ asserts.assert_true(connect_result,
+ "Failed to connect to WiFi passpoint network %s on"
+ " %s" % (expected_ssid, ad.serial))
+ ad.log.info("Wi-Fi connection result: %s.", connect_result)
+ actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+ asserts.assert_equal(actual_ssid, expected_ssid,
+ "Connected to the wrong network on %s." % ad.serial)
+ ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
+
+ internet = validate_connection(ad, DEFAULT_PING_ADDR)
+ if not internet:
+ raise signals.TestFailure("Failed to connect to internet on %s" %
+ expected_ssid)
+ except Exception as error:
+ ad.log.error("Failed to connect to passpoint network %s with error %s",
+ expected_ssid, error)
+ raise signals.TestFailure("Failed to connect to %s passpoint network" %
+ expected_ssid)
+
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
+
+
+def delete_passpoint(ad, fqdn):
+ """Delete a required Passpoint configuration."""
+ try:
+ ad.droid.removePasspointConfig(fqdn)
+ return True
+ except Exception as error:
+ ad.log.error("Failed to remove passpoint configuration with FQDN=%s "
+ "and error=%s" , fqdn, error)
+ return False
+
+
+def start_wifi_single_scan(ad, scan_setting):
+ """Starts wifi single shot scan.
+
+ Args:
+ ad: android_device object to initiate connection on.
+ scan_setting: A dict representing the settings of the scan.
+
+ Returns:
+ If scan was started successfully, event data of success event is returned.
+ """
+ idx = ad.droid.wifiScannerStartScan(scan_setting)
+ event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
+ ad.log.debug("Got event %s", event)
+ return event['data']
+
+
+def track_connection(ad, network_ssid, check_connection_count):
+ """Track wifi connection to network changes for given number of counts
+
+ Args:
+ ad: android_device object for forget network.
+ network_ssid: network ssid to which connection would be tracked
+ check_connection_count: Integer for maximum number network connection
+ check.
+ Returns:
+ True if connection to given network happen, else return False.
+ """
+ ad.droid.wifiStartTrackingStateChange()
+ while check_connection_count > 0:
+ connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
+ ad.log.info("Connected to network %s", connect_network)
+ if (WifiEnums.SSID_KEY in connect_network['data'] and
+ connect_network['data'][WifiEnums.SSID_KEY] == network_ssid):
+ return True
+ check_connection_count -= 1
+ ad.droid.wifiStopTrackingStateChange()
+ return False
+
+
+def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
+ """Calculate the scan time required based on the band or channels in scan
+ setting
+
+ Args:
+ wifi_chs: Object of channels supported
+ scan_setting: scan setting used for start scan
+ stime_channel: scan time per channel
+
+ Returns:
+ scan_time: time required for completing a scan
+ scan_channels: channel used for scanning
+ """
+ scan_time = 0
+ scan_channels = []
+ if "band" in scan_setting and "channels" not in scan_setting:
+ scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
+ elif "channels" in scan_setting and "band" not in scan_setting:
+ scan_channels = scan_setting["channels"]
+ scan_time = len(scan_channels) * stime_channel
+ for channel in scan_channels:
+ if channel in WifiEnums.DFS_5G_FREQUENCIES:
+ scan_time += 132 #passive scan time on DFS
+ return scan_time, scan_channels
+
+
+def start_wifi_track_bssid(ad, track_setting):
+ """Start tracking Bssid for the given settings.
+
+ Args:
+ ad: android_device object.
+ track_setting: Setting for which the bssid tracking should be started
+
+ Returns:
+ If tracking started successfully, event data of success event is returned.
+ """
+ idx = ad.droid.wifiScannerStartTrackingBssids(
+ track_setting["bssidInfos"], track_setting["apLostThreshold"])
+ event = ad.ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
+ SHORT_TIMEOUT)
+ return event['data']
+
+
+def convert_pem_key_to_pkcs8(in_file, out_file):
+ """Converts the key file generated by us to the format required by
+ Android using openssl.
+
+ The input file must have the extension "pem". The output file must
+ have the extension "der".
+
+ Args:
+ in_file: The original key file.
+ out_file: The full path to the converted key file, including
+ filename.
+ """
+ asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
+ asserts.assert_true(
+ out_file.endswith(".der"), "Output file has to be .der.")
+ cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
+ " -topk8").format(in_file, out_file)
+ utils.exe_cmd(cmd)
+
+
+def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR, wait_time=15,
+ ping_gateway=True):
+ """Validate internet connection by pinging the address provided.
+
+ Args:
+ ad: android_device object.
+ ping_addr: address on internet for pinging.
+ wait_time: wait for some time before validating connection
+
+ Returns:
+ ping output if successful, NULL otherwise.
+ """
+ # wait_time to allow for DHCP to complete.
+ for i in range(wait_time):
+ if ad.droid.connectivityNetworkIsConnected():
+ break
+ time.sleep(1)
+ ping = False
+ try:
+ ping = ad.droid.httpPing(ping_addr)
+ ad.log.info("Http ping result: %s.", ping)
+ except:
+ pass
+ if not ping and ping_gateway:
+ ad.log.info("Http ping failed. Pinging default gateway")
+ gw = ad.droid.connectivityGetIPv4DefaultGateway()
+ result = ad.adb.shell("ping -c 6 {}".format(gw))
+ ad.log.info("Default gateway ping result: %s" % result)
+ ping = False if "100% packet loss" in result else True
+ return ping
+
+
+#TODO(angli): This can only verify if an actual value is exactly the same.
+# Would be nice to be able to verify an actual value is one of serveral.
+def verify_wifi_connection_info(ad, expected_con):
+ """Verifies that the information of the currently connected wifi network is
+ as expected.
+
+ Args:
+ expected_con: A dict representing expected key-value pairs for wifi
+ connection. e.g. {"SSID": "test_wifi"}
+ """
+ current_con = ad.droid.wifiGetConnectionInfo()
+ case_insensitive = ["BSSID", "supplicant_state"]
+ ad.log.debug("Current connection: %s", current_con)
+ for k, expected_v in expected_con.items():
+ # Do not verify authentication related fields.
+ if k == "password":
+ continue
+ msg = "Field %s does not exist in wifi connection info %s." % (
+ k, current_con)
+ if k not in current_con:
+ raise signals.TestFailure(msg)
+ actual_v = current_con[k]
+ if k in case_insensitive:
+ actual_v = actual_v.lower()
+ expected_v = expected_v.lower()
+ msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
+ actual_v)
+ if actual_v != expected_v:
+ raise signals.TestFailure(msg)
+
+
+def check_autoconnect_to_open_network(ad, conn_timeout=WIFI_CONNECTION_TIMEOUT_DEFAULT):
+ """Connects to any open WiFI AP
+ Args:
+ timeout value in sec to wait for UE to connect to a WiFi AP
+ Returns:
+ True if UE connects to WiFi AP (supplicant_state = completed)
+ False if UE fails to complete connection within WIFI_CONNECTION_TIMEOUT time.
+ """
+ if ad.droid.wifiCheckState():
+ return True
+ ad.droid.wifiToggleState()
+ wifi_connection_state = None
+ timeout = time.time() + conn_timeout
+ while wifi_connection_state != "completed":
+ wifi_connection_state = ad.droid.wifiGetConnectionInfo()[
+ 'supplicant_state']
+ if time.time() > timeout:
+ ad.log.warning("Failed to connect to WiFi AP")
+ return False
+ return True
+
+
+def expand_enterprise_config_by_phase2(config):
+ """Take an enterprise config and generate a list of configs, each with
+ a different phase2 auth type.
+
+ Args:
+ config: A dict representing enterprise config.
+
+ Returns
+ A list of enterprise configs.
+ """
+ results = []
+ phase2_types = WifiEnums.EapPhase2
+ if config[WifiEnums.Enterprise.EAP] == WifiEnums.Eap.PEAP:
+ # Skip unsupported phase2 types for PEAP.
+ phase2_types = [WifiEnums.EapPhase2.GTC, WifiEnums.EapPhase2.MSCHAPV2]
+ for phase2_type in phase2_types:
+ # Skip a special case for passpoint TTLS.
+ if (WifiEnums.Enterprise.FQDN in config and
+ phase2_type == WifiEnums.EapPhase2.GTC):
+ continue
+ c = dict(config)
+ c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
+ results.append(c)
+ return results
+
+
+def generate_eap_test_name(config, ad=None):
+ """ Generates a test case name based on an EAP configuration.
+
+ Args:
+ config: A dict representing an EAP credential.
+ ad object: Redundant but required as the same param is passed
+ to test_func in run_generated_tests
+
+ Returns:
+ A string representing the name of a generated EAP test case.
+ """
+ eap = WifiEnums.Eap
+ eap_phase2 = WifiEnums.EapPhase2
+ Ent = WifiEnums.Enterprise
+ name = "test_connect-"
+ eap_name = ""
+ for e in eap:
+ if e.value == config[Ent.EAP]:
+ eap_name = e.name
+ break
+ if "peap0" in config[WifiEnums.SSID_KEY].lower():
+ eap_name = "PEAP0"
+ if "peap1" in config[WifiEnums.SSID_KEY].lower():
+ eap_name = "PEAP1"
+ name += eap_name
+ if Ent.PHASE2 in config:
+ for e in eap_phase2:
+ if e.value == config[Ent.PHASE2]:
+ name += "-{}".format(e.name)
+ break
+ return name
+
+
+def group_attenuators(attenuators):
+ """Groups a list of attenuators into attenuator groups for backward
+ compatibility reasons.
+
+ Most legacy Wi-Fi setups have two attenuators each connected to a separate
+ AP. The new Wi-Fi setup has four attenuators, each connected to one channel
+ on an AP, so two of them are connected to one AP.
+
+ To make the existing scripts work in the new setup, when the script needs
+ to attenuate one AP, it needs to set attenuation on both attenuators
+ connected to the same AP.
+
+ This function groups attenuators properly so the scripts work in both
+ legacy and new Wi-Fi setups.
+
+ Args:
+ attenuators: A list of attenuator objects, either two or four in length.
+
+ Raises:
+ signals.TestFailure is raised if the attenuator list does not have two
+ or four objects.
+ """
+ attn0 = attenuator.AttenuatorGroup("AP0")
+ attn1 = attenuator.AttenuatorGroup("AP1")
+ # Legacy testbed setup has two attenuation channels.
+ num_of_attns = len(attenuators)
+ if num_of_attns == 2:
+ attn0.add(attenuators[0])
+ attn1.add(attenuators[1])
+ elif num_of_attns == 4:
+ attn0.add(attenuators[0])
+ attn0.add(attenuators[1])
+ attn1.add(attenuators[2])
+ attn1.add(attenuators[3])
+ else:
+ asserts.fail(("Either two or four attenuators are required for this "
+ "test, but found %s") % num_of_attns)
+ return [attn0, attn1]
+
+
+def set_attns(attenuator, attn_val_name, roaming_attn=ROAMING_ATTN):
+ """Sets attenuation values on attenuators used in this test.
+
+ Args:
+ attenuator: The attenuator object.
+ attn_val_name: Name of the attenuation value pair to use.
+ roaming_attn: Dictionary specifying the attenuation params.
+ """
+ logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
+ try:
+ attenuator[0].set_atten(roaming_attn[attn_val_name][0])
+ attenuator[1].set_atten(roaming_attn[attn_val_name][1])
+ attenuator[2].set_atten(roaming_attn[attn_val_name][2])
+ attenuator[3].set_atten(roaming_attn[attn_val_name][3])
+ except:
+ logging.exception("Failed to set attenuation values %s.",
+ attn_val_name)
+ raise
+
+def set_attns_steps(attenuators,
+ atten_val_name,
+ roaming_attn=ROAMING_ATTN,
+ steps=10,
+ wait_time=12):
+ """Set attenuation values on attenuators used in this test. It will change
+ the attenuation values linearly from current value to target value step by
+ step.
+
+ Args:
+ attenuators: The list of attenuator objects that you want to change
+ their attenuation value.
+ atten_val_name: Name of the attenuation value pair to use.
+ roaming_attn: Dictionary specifying the attenuation params.
+ steps: Number of attenuator changes to reach the target value.
+ wait_time: Sleep time for each change of attenuator.
+ """
+ logging.info("Set attenuation values to %s in %d step(s)",
+ roaming_attn[atten_val_name], steps)
+ start_atten = [attenuator.get_atten() for attenuator in attenuators]
+ target_atten = roaming_attn[atten_val_name]
+ for current_step in range(steps):
+ progress = (current_step + 1) / steps
+ for i, attenuator in enumerate(attenuators):
+ amount_since_start = (target_atten[i] - start_atten[i]) * progress
+ attenuator.set_atten(round(start_atten[i] + amount_since_start))
+ time.sleep(wait_time)
+
+
+def trigger_roaming_and_validate(dut,
+ attenuator,
+ attn_val_name,
+ expected_con,
+ roaming_attn=ROAMING_ATTN):
+ """Sets attenuators to trigger roaming and validate the DUT connected
+ to the BSSID expected.
+
+ Args:
+ attenuator: The attenuator object.
+ attn_val_name: Name of the attenuation value pair to use.
+ expected_con: The network information of the expected network.
+ roaming_attn: Dictionary specifying the attenaution params.
+ """
+ expected_con = {
+ WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
+ WifiEnums.BSSID_KEY: expected_con["bssid"],
+ }
+ set_attns_steps(attenuator, attn_val_name, roaming_attn)
+
+ verify_wifi_connection_info(dut, expected_con)
+ expected_bssid = expected_con[WifiEnums.BSSID_KEY]
+ logging.info("Roamed to %s successfully", expected_bssid)
+ if not validate_connection(dut):
+ raise signals.TestFailure("Fail to connect to internet on %s" %
+ expected_bssid)
+
+def create_softap_config():
+ """Create a softap config with random ssid and password."""
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = utils.rand_ascii_str(8)
+ logging.info("softap setup: %s %s", ap_ssid, ap_password)
+ config = {
+ WifiEnums.SSID_KEY: ap_ssid,
+ WifiEnums.PWD_KEY: ap_password,
+ }
+ return config
+
+def start_softap_and_verify(ad, band):
+ """Bring-up softap and verify AP mode and in scan results.
+
+ Args:
+ band: The band to use for softAP.
+
+ Returns: dict, the softAP config.
+
+ """
+ # Register before start the test.
+ callbackId = ad.dut.droid.registerSoftApCallback()
+ # Check softap info value is default
+ frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
+ asserts.assert_true(frequency == 0, "Softap frequency is not reset")
+ asserts.assert_true(bandwdith == 0, "Softap bandwdith is not reset")
+
+ config = create_softap_config()
+ start_wifi_tethering(ad.dut,
+ config[WifiEnums.SSID_KEY],
+ config[WifiEnums.PWD_KEY], band=band)
+ asserts.assert_true(ad.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+ start_wifi_connection_scan_and_ensure_network_found(ad.dut_client,
+ config[WifiEnums.SSID_KEY])
+
+ # Check softap info can get from callback succeed and assert value should be
+ # valid.
+ frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
+ asserts.assert_true(frequency > 0, "Softap frequency is not valid")
+ asserts.assert_true(bandwdith > 0, "Softap bandwdith is not valid")
+ # Unregister callback
+ ad.dut.droid.unregisterSoftApCallback(callbackId)
+
+ return config
+
+def wait_for_expected_number_of_softap_clients(ad, callbackId,
+ expected_num_of_softap_clients):
+ """Wait for the number of softap clients to be updated as expected.
+ Args:
+ callbackId: Id of the callback associated with registering.
+ expected_num_of_softap_clients: expected number of softap clients.
+ """
+ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
+ callbackId) + wifi_constants.SOFTAP_NUMBER_CLIENTS_CHANGED
+ clientData = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)['data']
+ clientCount = clientData[wifi_constants.SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY]
+ clientMacAddresses = clientData[wifi_constants.SOFTAP_CLIENTS_MACS_CALLBACK_KEY]
+ asserts.assert_equal(clientCount, expected_num_of_softap_clients,
+ "The number of softap clients doesn't match the expected number")
+ asserts.assert_equal(len(clientMacAddresses), expected_num_of_softap_clients,
+ "The number of mac addresses doesn't match the expected number")
+ for macAddress in clientMacAddresses:
+ asserts.assert_true(checkMacAddress(macAddress), "An invalid mac address was returned")
+
+def checkMacAddress(input):
+ """Validate whether a string is a valid mac address or not.
+
+ Args:
+ input: The string to validate.
+
+ Returns: True/False, returns true for a valid mac address and false otherwise.
+ """
+ macValidationRegex = "[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$"
+ if re.match(macValidationRegex, input.lower()):
+ return True
+ return False
+
+def wait_for_expected_softap_state(ad, callbackId, expected_softap_state):
+ """Wait for the expected softap state change.
+ Args:
+ callbackId: Id of the callback associated with registering.
+ expected_softap_state: The expected softap state.
+ """
+ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
+ callbackId) + wifi_constants.SOFTAP_STATE_CHANGED
+ asserts.assert_equal(ad.ed.pop_event(eventStr,
+ SHORT_TIMEOUT)['data'][wifi_constants.
+ SOFTAP_STATE_CHANGE_CALLBACK_KEY],
+ expected_softap_state,
+ "Softap state doesn't match with expected state")
+
+def get_current_number_of_softap_clients(ad, callbackId):
+ """pop up all of softap client updated event from queue.
+ Args:
+ callbackId: Id of the callback associated with registering.
+
+ Returns:
+ If exist aleast callback, returns last updated number_of_softap_clients.
+ Returns None when no any match callback event in queue.
+ """
+ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
+ callbackId) + wifi_constants.SOFTAP_NUMBER_CLIENTS_CHANGED
+ events = ad.ed.pop_all(eventStr)
+ for event in events:
+ num_of_clients = event['data'][wifi_constants.
+ SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY]
+ if len(events) == 0:
+ return None
+ return num_of_clients
+
+def get_current_softap_info(ad, callbackId, least_one):
+ """pop up all of softap info changed event from queue.
+ Args:
+ callbackId: Id of the callback associated with registering.
+ least_one: Wait for the info callback event before pop all.
+ Returns:
+ Returns last updated information of softap.
+ """
+ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
+ callbackId) + wifi_constants.SOFTAP_INFO_CHANGED
+ ad.log.info("softap info dump from eventStr %s",
+ eventStr)
+ frequency = 0
+ bandwidth = 0
+ if (least_one):
+ event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)
+ frequency = event['data'][wifi_constants.
+ SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
+ bandwidth = event['data'][wifi_constants.
+ SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
+ ad.log.info("softap info updated, frequency is %s, bandwidth is %s",
+ frequency, bandwidth)
+
+ events = ad.ed.pop_all(eventStr)
+ for event in events:
+ frequency = event['data'][wifi_constants.
+ SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
+ bandwidth = event['data'][wifi_constants.
+ SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
+ ad.log.info("softap info, frequency is %s, bandwidth is %s",
+ frequency, bandwidth)
+ return frequency, bandwidth
+
+
+
+def get_ssrdumps(ad, test_name=""):
+ """Pulls dumps in the ssrdump dir
+ Args:
+ ad: android device object.
+ test_name: test case name
+ """
+ logs = ad.get_file_names("/data/vendor/ssrdump/")
+ if logs:
+ ad.log.info("Pulling ssrdumps %s", logs)
+ log_path = os.path.join(ad.log_path, test_name,
+ "SSRDUMP_%s" % ad.serial)
+ os.makedirs(log_path, exist_ok=True)
+ ad.pull_files(logs, log_path)
+ ad.adb.shell("find /data/vendor/ssrdump/ -type f -delete")
+
+def start_pcap(pcap, wifi_band, test_name):
+ """Start packet capture in monitor mode.
+
+ Args:
+ pcap: packet capture object
+ wifi_band: '2g' or '5g' or 'dual'
+ test_name: test name to be used for pcap file name
+
+ Returns:
+ Dictionary with wifi band as key and the tuple
+ (pcap Process object, log directory) as the value
+ """
+ log_dir = os.path.join(
+ context.get_current_context().get_full_output_path(), 'PacketCapture')
+ os.makedirs(log_dir, exist_ok=True)
+ if wifi_band == 'dual':
+ bands = [BAND_2G, BAND_5G]
+ else:
+ bands = [wifi_band]
+ procs = {}
+ for band in bands:
+ proc = pcap.start_packet_capture(band, log_dir, test_name)
+ procs[band] = (proc, os.path.join(log_dir, test_name))
+ return procs
+
+
+def stop_pcap(pcap, procs, test_status=None):
+ """Stop packet capture in monitor mode.
+
+ Since, the pcap logs in monitor mode can be very large, we will
+ delete them if they are not required. 'test_status' if True, will delete
+ the pcap files. If False, we will keep them.
+
+ Args:
+ pcap: packet capture object
+ procs: dictionary returned by start_pcap
+ test_status: status of the test case
+ """
+ for proc, fname in procs.values():
+ pcap.stop_packet_capture(proc)
+
+ if test_status:
+ shutil.rmtree(os.path.dirname(fname))
+
+def verify_mac_not_found_in_pcap(ad, mac, packets):
+ """Verify that a mac address is not found in the captured packets.
+
+ Args:
+ ad: android device object
+ mac: string representation of the mac address
+ packets: packets obtained by rdpcap(pcap_fname)
+ """
+ for pkt in packets:
+ logging.debug("Packet Summary = %s", pkt.summary())
+ if mac in pkt.summary():
+ asserts.fail("Device %s caught Factory MAC: %s in packet sniffer."
+ "Packet = %s" % (ad.serial, mac, pkt.show()))
+
+def verify_mac_is_found_in_pcap(ad, mac, packets):
+ """Verify that a mac address is found in the captured packets.
+
+ Args:
+ ad: android device object
+ mac: string representation of the mac address
+ packets: packets obtained by rdpcap(pcap_fname)
+ """
+ for pkt in packets:
+ if mac in pkt.summary():
+ return
+ asserts.fail("Did not find MAC = %s in packet sniffer."
+ "for device %s" % (mac, ad.serial))
+
+def start_cnss_diags(ads):
+ for ad in ads:
+ start_cnss_diag(ad)
+
+
+def start_cnss_diag(ad):
+ """Start cnss_diag to record extra wifi logs
+
+ Args:
+ ad: android device object.
+ """
+ if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
+ prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
+ else:
+ prop = wifi_constants.CNSS_DIAG_PROP
+ if ad.adb.getprop(prop) != 'true':
+ ad.adb.shell("find /data/vendor/wifi/cnss_diag/wlan_logs/ -type f -delete")
+ ad.adb.shell("setprop %s true" % prop, ignore_status=True)
+
+
+def stop_cnss_diags(ads):
+ for ad in ads:
+ stop_cnss_diag(ad)
+
+
+def stop_cnss_diag(ad):
+ """Stops cnss_diag
+
+ Args:
+ ad: android device object.
+ """
+ if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
+ prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
+ else:
+ prop = wifi_constants.CNSS_DIAG_PROP
+ ad.adb.shell("setprop %s false" % prop, ignore_status=True)
+
+
+def get_cnss_diag_log(ad, test_name=""):
+ """Pulls the cnss_diag logs in the wlan_logs dir
+ Args:
+ ad: android device object.
+ test_name: test case name
+ """
+ logs = ad.get_file_names("/data/vendor/wifi/cnss_diag/wlan_logs/")
+ if logs:
+ ad.log.info("Pulling cnss_diag logs %s", logs)
+ log_path = os.path.join(ad.device_log_path, "CNSS_DIAG_%s" % ad.serial)
+ os.makedirs(log_path, exist_ok=True)
+ ad.pull_files(logs, log_path)
+
+
+LinkProbeResult = namedtuple('LinkProbeResult', (
+ 'is_success', 'stdout', 'elapsed_time', 'failure_reason'))
+
+
+def send_link_probe(ad):
+ """Sends a link probe to the currently connected AP, and returns whether the
+ probe succeeded or not.
+
+ Args:
+ ad: android device object
+ Returns:
+ LinkProbeResult namedtuple
+ """
+ stdout = ad.adb.shell('cmd wifi send-link-probe')
+ asserts.assert_false('Error' in stdout or 'Exception' in stdout,
+ 'Exception while sending link probe: ' + stdout)
+
+ is_success = False
+ elapsed_time = None
+ failure_reason = None
+ if 'succeeded' in stdout:
+ is_success = True
+ elapsed_time = next(
+ (int(token) for token in stdout.split() if token.isdigit()), None)
+ elif 'failed with reason' in stdout:
+ failure_reason = next(
+ (int(token) for token in stdout.split() if token.isdigit()), None)
+ else:
+ asserts.fail('Unexpected link probe result: ' + stdout)
+
+ return LinkProbeResult(
+ is_success=is_success, stdout=stdout,
+ elapsed_time=elapsed_time, failure_reason=failure_reason)
+
+
+def send_link_probes(ad, num_probes, delay_sec):
+ """Sends a sequence of link probes to the currently connected AP, and
+ returns whether the probes succeeded or not.
+
+ Args:
+ ad: android device object
+ num_probes: number of probes to perform
+ delay_sec: delay time between probes, in seconds
+ Returns:
+ List[LinkProbeResult] one LinkProbeResults for each probe
+ """
+ logging.info('Sending link probes')
+ results = []
+ for _ in range(num_probes):
+ # send_link_probe() will also fail the test if it sees an exception
+ # in the stdout of the adb shell command
+ result = send_link_probe(ad)
+ logging.info('link probe results: ' + str(result))
+ results.append(result)
+ time.sleep(delay_sec)
+
+ return results
+
+
+def ap_setup(test, index, ap, network, bandwidth=80, channel=6):
+ """Set up the AP with provided network info.
+
+ Args:
+ test: the calling test class object.
+ index: int, index of the AP.
+ ap: access_point object of the AP.
+ network: dict with information of the network, including ssid,
+ password and bssid.
+ bandwidth: the operation bandwidth for the AP, default 80MHz.
+ channel: the channel number for the AP.
+ Returns:
+ brconfigs: the bridge interface configs
+ """
+ bss_settings = []
+ ssid = network[WifiEnums.SSID_KEY]
+ test.access_points[index].close()
+ time.sleep(5)
+
+ # Configure AP as required.
+ if "password" in network.keys():
+ password = network["password"]
+ security = hostapd_security.Security(
+ security_mode="wpa", password=password)
+ else:
+ security = hostapd_security.Security(security_mode=None, password=None)
+ config = hostapd_ap_preset.create_ap_preset(
+ channel=channel,
+ ssid=ssid,
+ security=security,
+ bss_settings=bss_settings,
+ vht_bandwidth=bandwidth,
+ profile_name='whirlwind',
+ iface_wlan_2g=ap.wlan_2g,
+ iface_wlan_5g=ap.wlan_5g)
+ ap.start_ap(config)
+ logging.info("AP started on channel {} with SSID {}".format(channel, ssid))
+
+
+def turn_ap_off(test, AP):
+ """Bring down hostapd on the Access Point.
+ Args:
+ test: The test class object.
+ AP: int, indicating which AP to turn OFF.
+ """
+ hostapd_2g = test.access_points[AP-1]._aps['wlan0'].hostapd
+ if hostapd_2g.is_alive():
+ hostapd_2g.stop()
+ logging.debug('Turned WLAN0 AP%d off' % AP)
+ hostapd_5g = test.access_points[AP-1]._aps['wlan1'].hostapd
+ if hostapd_5g.is_alive():
+ hostapd_5g.stop()
+ logging.debug('Turned WLAN1 AP%d off' % AP)
+
+
+def turn_ap_on(test, AP):
+ """Bring up hostapd on the Access Point.
+ Args:
+ test: The test class object.
+ AP: int, indicating which AP to turn ON.
+ """
+ hostapd_2g = test.access_points[AP-1]._aps['wlan0'].hostapd
+ if not hostapd_2g.is_alive():
+ hostapd_2g.start(hostapd_2g.config)
+ logging.debug('Turned WLAN0 AP%d on' % AP)
+ hostapd_5g = test.access_points[AP-1]._aps['wlan1'].hostapd
+ if not hostapd_5g.is_alive():
+ hostapd_5g.start(hostapd_5g.config)
+ logging.debug('Turned WLAN1 AP%d on' % AP)
+
+
+def turn_location_off_and_scan_toggle_off(ad):
+ """Turns off wifi location scans."""
+ utils.set_location_service(ad, False)
+ ad.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+
+def set_softap_channel(dut, ap_iface='wlan1', cs_count=10, channel=2462):
+ """ Set SoftAP mode channel
+
+ Args:
+ dut: android device object
+ ap_iface: interface of SoftAP mode.
+ cs_count: how many beacon frames before switch channel, default = 10
+ channel: a wifi channel.
+ """
+ chan_switch_cmd = 'hostapd_cli -i {} chan_switch {} {}'
+ chan_switch_cmd_show = chan_switch_cmd.format(ap_iface,cs_count,channel)
+ dut.log.info('adb shell {}'.format(chan_switch_cmd_show))
+ chan_switch_result = dut.adb.shell(chan_switch_cmd.format(ap_iface,
+ cs_count,
+ channel))
+ if chan_switch_result == 'OK':
+ dut.log.info('switch hotspot channel to {}'.format(channel))
+ return chan_switch_result
+
+ asserts.fail("Failed to switch hotspot channel")
+
diff --git a/acts_tests/acts_contrib/test_utils_tests b/acts_tests/acts_contrib/test_utils_tests
deleted file mode 120000
index 0615a35..0000000
--- a/acts_tests/acts_contrib/test_utils_tests
+++ /dev/null
@@ -1 +0,0 @@
-../../acts/framework/tests/test_utils
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils_tests/__init__.py b/acts_tests/acts_contrib/test_utils_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/acts_import_test_utils_test.py b/acts_tests/acts_contrib/test_utils_tests/acts_import_test_utils_test.py
new file mode 100755
index 0000000..1fcb699
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/acts_import_test_utils_test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+
+class ActsImportTestUtilsTest(unittest.TestCase):
+ """This test class has unit tests for the implementation of everything
+ under acts_contrib.test_utils.*
+ """
+
+ def test_import_successful(self):
+ """ Test to return true if all imports were successful.
+
+ This test will fail if any import was unsuccessful.
+ """
+ try:
+ from acts import utils
+
+ from acts_contrib.test_utils.bt import BleEnum
+ from acts_contrib.test_utils.bt import BluetoothBaseTest
+ from acts_contrib.test_utils.bt import BluetoothCarHfpBaseTest
+ from acts_contrib.test_utils.bt import BtEnum
+ from acts_contrib.test_utils.bt import GattConnectedBaseTest
+ from acts_contrib.test_utils.bt import GattEnum
+ from acts_contrib.test_utils.bt import bt_contacts_utils
+ from acts_contrib.test_utils.bt import bt_gatt_utils
+ from acts_contrib.test_utils.bt import bt_test_utils
+ from acts_contrib.test_utils.bt import native_bt_test_utils
+
+ from acts_contrib.test_utils.car import car_bt_utils
+ from acts_contrib.test_utils.car import car_media_utils
+ from acts_contrib.test_utils.car import car_telecom_utils
+ from acts_contrib.test_utils.car import tel_telecom_utils
+
+ from acts_contrib.test_utils.net import connectivity_const
+ from acts_contrib.test_utils.net import connectivity_const
+
+ from acts_contrib.test_utils.tel import TelephonyBaseTest
+ from acts_contrib.test_utils.tel import tel_atten_utils
+ from acts_contrib.test_utils.tel import tel_data_utils
+ from acts_contrib.test_utils.tel import tel_defines
+ from acts_contrib.test_utils.tel import tel_lookup_tables
+ from acts_contrib.test_utils.tel import tel_subscription_utils
+ from acts_contrib.test_utils.tel import tel_test_utils
+ from acts_contrib.test_utils.tel import tel_video_utils
+ from acts_contrib.test_utils.tel import tel_voice_utils
+
+ from acts_contrib.test_utils.wifi import wifi_constants
+ from acts_contrib.test_utils.wifi import wifi_test_utils
+
+ except Exception:
+ self.fail('Unable to import all supported test_utils')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/audio_analysis_integrationtest.py b/acts_tests/acts_contrib/test_utils_tests/audio_analysis_integrationtest.py
new file mode 100644
index 0000000..a00a431
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/audio_analysis_integrationtest.py
@@ -0,0 +1,365 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Note: This test has been labelled as an integration test due to its use of
+# real data, and the five to six second execution time.
+import logging
+import numpy
+import os
+import unittest
+
+# TODO(markdr): Remove this after soundfile is added to setup.py
+import sys
+import mock
+sys.modules['soundfile'] = mock.Mock()
+
+import acts_contrib.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+import acts_contrib.test_utils.audio_analysis_lib.audio_data as audio_data
+
+
+class SpectralAnalysisTest(unittest.TestCase):
+ def setUp(self):
+ """Uses the same seed to generate noise for each test."""
+ numpy.random.seed(0)
+
+ def dummy_peak_detection(self, array, window_size):
+ """Detects peaks in an array in simple way.
+
+ A point (i, array[i]) is a peak if array[i] is the maximum among
+ array[i - half_window_size] to array[i + half_window_size].
+ If array[i - half_window_size] to array[i + half_window_size] are all
+ equal, then there is no peak in this window.
+
+ Args:
+ array: The input array to detect peaks in. Array is a list of
+ absolute values of the magnitude of transformed coefficient.
+ window_size: The window to detect peaks.
+
+ Returns:
+ A list of tuples:
+ [(peak_index_1, peak_value_1), (peak_index_2, peak_value_2),
+ ...]
+ where the tuples are sorted by peak values.
+
+ """
+ half_window_size = window_size / 2
+ length = len(array)
+
+ def mid_is_peak(array, mid, left, right):
+ """Checks if value at mid is the largest among left to right.
+
+ Args:
+ array: A list of numbers.
+ mid: The mid index.
+ left: The left index.
+ rigth: The right index.
+
+ Returns:
+ True if array[index] is the maximum among numbers in array
+ between index [left, right] inclusively.
+
+ """
+ value_mid = array[int(mid)]
+ for index in range(int(left), int(right) + 1):
+ if index == mid:
+ continue
+ if array[index] >= value_mid:
+ return False
+ return True
+
+ results = []
+ for mid in range(length):
+ left = max(0, mid - half_window_size)
+ right = min(length - 1, mid + half_window_size)
+ if mid_is_peak(array, mid, left, right):
+ results.append((mid, array[int(mid)]))
+
+ # Sort the peaks by values.
+ return sorted(results, key=lambda x: x[1], reverse=True)
+
+ def test_peak_detection(self):
+ array = [0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 5, 3, 2, 1, 1, 1, 1, 1]
+ result = audio_analysis.peak_detection(array, 4)
+ golden_answer = [(12, 5), (4, 4)]
+ self.assertEqual(result, golden_answer)
+
+ def test_peak_detection_large(self):
+ array = numpy.random.uniform(0, 1, 1000000)
+ window_size = 100
+ logging.debug('Test large array using dummy peak detection')
+ dummy_answer = self.dummy_peak_detection(array, window_size)
+ logging.debug('Test large array using improved peak detection')
+ improved_answer = audio_analysis.peak_detection(array, window_size)
+ logging.debug('Compare the result')
+ self.assertEqual(dummy_answer, improved_answer)
+
+ def test_spectral_analysis(self):
+ rate = 48000
+ length_in_secs = 0.5
+ freq_1 = 490.0
+ freq_2 = 60.0
+ coeff_1 = 1
+ coeff_2 = 0.3
+ samples = length_in_secs * rate
+ noise = numpy.random.standard_normal(int(samples)) * 0.005
+ x = numpy.linspace(0.0, (samples - 1) * 1.0 / rate, samples)
+ y = (coeff_1 * numpy.sin(freq_1 * 2.0 * numpy.pi * x) + coeff_2 *
+ numpy.sin(freq_2 * 2.0 * numpy.pi * x)) + noise
+ results = audio_analysis.spectral_analysis(y, rate)
+ # Results should contains
+ # [(490, 1*k), (60, 0.3*k), (0, 0.1*k)] where 490Hz is the dominant
+ # frequency with coefficient 1, 60Hz is the second dominant frequency
+ # with coefficient 0.3, 0Hz is from Gaussian noise with coefficient
+ # around 0.1. The k constant is resulted from window function.
+ logging.debug('Results: %s', results)
+ self.assertTrue(abs(results[0][0] - freq_1) < 1)
+ self.assertTrue(abs(results[1][0] - freq_2) < 1)
+ self.assertTrue(
+ abs(results[0][1] / results[1][1] - coeff_1 / coeff_2) < 0.01)
+
+ def test_spectral_snalysis_real_data(self):
+ """This unittest checks the spectral analysis works on real data."""
+ file_path = os.path.join(
+ os.path.dirname(__file__), '../../../acts/framework/tests/test_data', '1k_2k.raw')
+ binary = open(file_path, 'rb').read()
+ data = audio_data.AudioRawData(binary, 2, 'S32_LE')
+ saturate_value = audio_data.get_maximum_value_from_sample_format(
+ 'S32_LE')
+ golden_frequency = [1000, 2000]
+ for channel in [0, 1]:
+ normalized_signal = audio_analysis.normalize_signal(
+ data.channel_data[channel], saturate_value)
+ spectral = audio_analysis.spectral_analysis(normalized_signal,
+ 48000, 0.02)
+ logging.debug('channel %s: %s', channel, spectral)
+ self.assertTrue(
+ abs(spectral[0][0] - golden_frequency[channel]) < 5,
+ 'Dominant frequency is not correct')
+
+ def test_not_meaningful_data(self):
+ """Checks that sepectral analysis handles un-meaningful data."""
+ rate = 48000
+ length_in_secs = 0.5
+ samples = length_in_secs * rate
+ noise_amplitude = audio_analysis.MEANINGFUL_RMS_THRESHOLD * 0.5
+ noise = numpy.random.standard_normal(int(samples)) * noise_amplitude
+ results = audio_analysis.spectral_analysis(noise, rate)
+ self.assertEqual([(0, 0)], results)
+
+ def testEmptyData(self):
+ """Checks that sepectral analysis rejects empty data."""
+ with self.assertRaises(audio_analysis.EmptyDataError):
+ results = audio_analysis.spectral_analysis([], 100)
+
+
+class NormalizeTest(unittest.TestCase):
+ def test_normalize(self):
+ y = [1, 2, 3, 4, 5]
+ normalized_y = audio_analysis.normalize_signal(y, 10)
+ expected = numpy.array([0.1, 0.2, 0.3, 0.4, 0.5])
+ for i in range(len(y)):
+ self.assertEqual(expected[i], normalized_y[i])
+
+
+class AnomalyTest(unittest.TestCase):
+ def setUp(self):
+ """Creates a test signal of sine wave."""
+ # Use the same seed for each test case.
+ numpy.random.seed(0)
+
+ self.block_size = 120
+ self.rate = 48000
+ self.freq = 440
+ length_in_secs = 0.25
+ self.samples = length_in_secs * self.rate
+ x = numpy.linspace(0.0, (self.samples - 1) * 1.0 / self.rate,
+ self.samples)
+ self.y = numpy.sin(self.freq * 2.0 * numpy.pi * x)
+
+ def add_noise(self):
+ """Add noise to the test signal."""
+ noise_amplitude = 0.3
+ noise = numpy.random.standard_normal(len(self.y)) * noise_amplitude
+ self.y = self.y + noise
+
+ def insert_anomaly(self):
+ """Inserts an anomaly to the test signal.
+
+ The anomaly self.anomaly_samples should be created before calling this
+ method.
+
+ """
+ self.anomaly_start_secs = 0.1
+ self.y = numpy.insert(self.y,
+ int(self.anomaly_start_secs * self.rate),
+ self.anomaly_samples)
+
+ def generate_skip_anomaly(self):
+ """Skips a section of test signal."""
+ self.anomaly_start_secs = 0.1
+ self.anomaly_duration_secs = 0.005
+ anomaly_append_secs = self.anomaly_start_secs + self.anomaly_duration_secs
+ anomaly_start_index = self.anomaly_start_secs * self.rate
+ anomaly_append_index = anomaly_append_secs * self.rate
+ self.y = numpy.append(self.y[:int(anomaly_start_index)],
+ self.y[int(anomaly_append_index):])
+
+ def create_constant_anomaly(self, amplitude):
+ """Creates an anomaly of constant samples.
+
+ Args:
+ amplitude: The amplitude of the constant samples.
+
+ """
+ self.anomaly_duration_secs = 0.005
+ self.anomaly_samples = ([amplitude] *
+ int(self.anomaly_duration_secs * self.rate))
+
+ def run_analysis(self):
+ """Runs the anomaly detection."""
+ self.results = audio_analysis.anomaly_detection(
+ self.y, self.rate, self.freq, self.block_size)
+ logging.debug('Results: %s', self.results)
+
+ def check_no_anomaly(self):
+ """Verifies that there is no anomaly in detection result."""
+ self.run_analysis()
+ self.assertFalse(self.results)
+
+ def check_anomaly(self):
+ """Verifies that there is anomaly in detection result.
+
+ The detection result should contain anomaly time stamps that are
+ close to where anomaly was inserted. There can be multiple anomalies
+ since the detection depends on the block size.
+
+ """
+ self.run_analysis()
+ self.assertTrue(self.results)
+ # Anomaly can be detected as long as the detection window of block size
+ # overlaps with anomaly.
+ expected_detected_range_secs = (
+ self.anomaly_start_secs - float(self.block_size) / self.rate,
+ self.anomaly_start_secs + self.anomaly_duration_secs)
+ for detected_secs in self.results:
+ self.assertTrue(detected_secs <= expected_detected_range_secs[1])
+ self.assertTrue(detected_secs >= expected_detected_range_secs[0])
+
+ def test_good_signal(self):
+ """Sine wave signal with no noise or anomaly."""
+ self.check_no_anomaly()
+
+ def test_good_signal_noise(self):
+ """Sine wave signal with noise."""
+ self.add_noise()
+ self.check_no_anomaly()
+
+ def test_zero_anomaly(self):
+ """Sine wave signal with no noise but with anomaly.
+
+ This test case simulates underrun in digital data where there will be
+ one block of samples with 0 amplitude.
+
+ """
+ self.create_constant_anomaly(0)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def test_zero_anomaly_noise(self):
+ """Sine wave signal with noise and anomaly.
+
+ This test case simulates underrun in analog data where there will be
+ one block of samples with amplitudes close to 0.
+
+ """
+ self.create_constant_anomaly(0)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def test_low_constant_anomaly(self):
+ """Sine wave signal with low constant anomaly.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(0.05)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def test_low_constant_anomaly_noise(self):
+ """Sine wave signal with low constant anomaly and noise.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(0.05)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def test_high_constant_anomaly(self):
+ """Sine wave signal with high constant anomaly.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(2)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def test_high_constant_anomaly_noise(self):
+ """Sine wave signal with high constant anomaly and noise.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(2)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def test_skipped_anomaly(self):
+ """Sine wave signal with skipped anomaly.
+
+ The anomaly simulates the symptom where a block is skipped.
+
+ """
+ self.generate_skip_anomaly()
+ self.check_anomaly()
+
+ def test_skipped_anomaly_noise(self):
+ """Sine wave signal with skipped anomaly with noise.
+
+ The anomaly simulates the symptom where a block is skipped.
+
+ """
+ self.generate_skip_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def test_empty_data(self):
+ """Checks that anomaly detection rejects empty data."""
+ self.y = []
+ with self.assertRaises(audio_analysis.EmptyDataError):
+ self.check_anomaly()
+
+
+if __name__ == '__main__':
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/audio_quality_measurement_integrationtest.py b/acts_tests/acts_contrib/test_utils_tests/audio_quality_measurement_integrationtest.py
new file mode 100644
index 0000000..396e5b8
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/audio_quality_measurement_integrationtest.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Note: This test has been labelled as an integration test due to its use of
+# real data, and the 12+ second execution time. It also generates sine waves
+# during the test, rather than using data that has been pre-calculated.
+
+import math
+import numpy
+import unittest
+
+# TODO(markdr): Remove this after soundfile is added to setup.py
+import sys
+import mock
+sys.modules['soundfile'] = mock.Mock()
+
+import acts_contrib.test_utils.audio_analysis_lib.audio_quality_measurement as audio_quality_measurement
+
+
+class NoiseLevelTest(unittest.TestCase):
+ def setUp(self):
+ """Uses the same seed to generate noise for each test."""
+ numpy.random.seed(0)
+
+ def test_noise_level(self):
+ # Generates the standard sin wave with standard_noise portion of noise.
+ rate = 48000
+ length_in_secs = 2
+ frequency = 440
+ amplitude = 1
+ standard_noise = 0.05
+
+ wave = []
+ for index in range(0, rate * length_in_secs):
+ phase = 2.0 * math.pi * frequency * float(index) / float(rate)
+ sine_wave = math.sin(phase)
+ noise = standard_noise * numpy.random.standard_normal()
+ wave.append(float(amplitude) * (sine_wave + noise))
+
+ # Calculates the average value after applying teager operator.
+ teager_value_of_wave, length = 0, len(wave)
+ for i in range(1, length - 1):
+ ith_teager_value = abs(wave[i] * wave[i] - wave[i - 1] * wave[i +
+ 1])
+ ith_teager_value *= max(1, abs(wave[i]))
+ teager_value_of_wave += ith_teager_value
+ teager_value_of_wave /= float(length * (amplitude**2))
+
+ noise = audio_quality_measurement.noise_level(
+ amplitude, frequency, rate, teager_value_of_wave)
+
+ self.assertTrue(abs(noise - standard_noise) < 0.01)
+
+
+class ErrorTest(unittest.TestCase):
+ def test_error(self):
+ value1 = [0.2, 0.4, 0.1, 0.01, 0.01, 0.01]
+ value2 = [0.3, 0.3, 0.08, 0.0095, 0.0098, 0.0099]
+ error = [0.5, 0.25, 0.2, 0.05, 0.02, 0.01]
+ for i in range(len(value1)):
+ ret = audio_quality_measurement.error(value1[i], value2[i])
+ self.assertTrue(abs(ret - error[i]) < 0.001)
+
+
+class QualityMeasurementTest(unittest.TestCase):
+ def setUp(self):
+ """Creates a test signal of sine wave."""
+ numpy.random.seed(0)
+
+ self.rate = 48000
+ self.freq = 440
+ self.amplitude = 1
+ length_in_secs = 2
+ self.samples = length_in_secs * self.rate
+ self.y = []
+ for index in range(self.samples):
+ phase = 2.0 * math.pi * self.freq * float(index) / float(self.rate)
+ sine_wave = math.sin(phase)
+ self.y.append(float(self.amplitude) * sine_wave)
+
+ def add_noise(self):
+ """Adds noise to the test signal."""
+ noise_amplitude = 0.01 * self.amplitude
+ for index in range(self.samples):
+ noise = noise_amplitude * numpy.random.standard_normal()
+ self.y[index] += noise
+
+ def generate_delay(self):
+ """Generates some delays during playing."""
+ self.delay_start_time = [0.200, 0.375, 0.513, 0.814, 1.000, 1.300]
+ self.delay_end_time = [0.201, 0.377, 0.516, 0.824, 1.100, 1.600]
+
+ for i in range(len(self.delay_start_time)):
+ start_index = int(self.delay_start_time[i] * self.rate)
+ end_index = int(self.delay_end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] = 0
+
+ def generate_artifacts_before_playback(self):
+ """Generates artifacts before playing."""
+ silence_before_playback_end_time = 0.2
+ end_index = int(silence_before_playback_end_time * self.rate)
+ for i in range(0, end_index):
+ self.y[i] = 0
+ noise_start_index = int(0.1 * self.rate)
+ noise_end_index = int(0.1005 * self.rate)
+ for i in range(noise_start_index, noise_end_index):
+ self.y[i] = 3 * self.amplitude
+
+ def generate_artifacts_after_playback(self):
+ """Generates artifacts after playing."""
+ silence_after_playback_start_time = int(1.9 * self.rate)
+ noise_start_index = int(1.95 * self.rate)
+ noise_end_index = int((1.95 + 0.02) * self.rate)
+
+ for i in range(silence_after_playback_start_time, self.samples):
+ self.y[i] = 0
+ for i in range(noise_start_index, noise_end_index):
+ self.y[i] = self.amplitude
+
+ def generate_burst_during_playback(self):
+ """Generates bursts during playing."""
+ self.burst_start_time = [0.300, 0.475, 0.613, 0.814, 1.300]
+ self.burst_end_time = [0.301, 0.476, 0.614, 0.815, 1.301]
+
+ for i in range(len(self.burst_start_time)):
+ start_index = int(self.burst_start_time[i] * self.rate)
+ end_index = int(self.burst_end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] = self.amplitude * (3 + numpy.random.uniform(-1, 1))
+
+ def generate_volume_changing(self):
+ """Generates volume changing during playing."""
+ start_time = [0.300, 1.400]
+ end_time = [0.600, 1.700]
+ for i in range(len(start_time)):
+ start_index = int(start_time[i] * self.rate)
+ end_index = int(end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] *= 1.4
+ self.volume_changing = [+1, -1, +1, -1]
+ self.volume_changing_time = [0.3, 0.6, 1.4, 1.7]
+
+ def test_good_signal(self):
+ """Sine wave signal with no noise or artifacts."""
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def test_good_signal_with_noise(self):
+ """Sine wave signal with noise."""
+ self.add_noise()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(0.009 < result['equivalent_noise_level'] < 0.011)
+
+ def test_delay(self):
+ """Sine wave with delay during playing."""
+ self.generate_delay()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(
+ len(result['volume_changes']) == 2 * len(self.delay_start_time))
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ self.assertTrue(
+ len(result['artifacts']['delay_during_playback']) ==
+ len(self.delay_start_time))
+ for i in range(len(result['artifacts']['delay_during_playback'])):
+ delta = abs(result['artifacts']['delay_during_playback'][i][0] -
+ self.delay_start_time[i])
+ self.assertTrue(delta < 0.001)
+ duration = self.delay_end_time[i] - self.delay_start_time[i]
+ delta = abs(result['artifacts']['delay_during_playback'][i][1] -
+ duration)
+ self.assertTrue(delta < 0.001)
+
+ def test_artifacts_before_playback(self):
+ """Sine wave with artifacts before playback."""
+ self.generate_artifacts_before_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 1)
+ delta = abs(result['artifacts']['noise_before_playback'][0][0] - 0.1)
+ self.assertTrue(delta < 0.01)
+ delta = abs(result['artifacts']['noise_before_playback'][0][1] - 0.005)
+ self.assertTrue(delta < 0.004)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def test_artifacts_after_playback(self):
+ """Sine wave with artifacts after playback."""
+ self.generate_artifacts_after_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 1)
+ delta = abs(result['artifacts']['noise_after_playback'][0][0] - 1.95)
+ self.assertTrue(delta < 0.01)
+ delta = abs(result['artifacts']['noise_after_playback'][0][1] - 0.02)
+ self.assertTrue(delta < 0.001)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def test_burst_during_playback(self):
+ """Sine wave with burst during playback."""
+ self.generate_burst_during_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 5)
+ self.assertTrue(len(result['volume_changes']) == 10)
+ self.assertTrue(result['equivalent_noise_level'] > 0.02)
+ for i in range(len(result['artifacts']['burst_during_playback'])):
+ delta = abs(self.burst_start_time[i] - result['artifacts'][
+ 'burst_during_playback'][i])
+ self.assertTrue(delta < 0.002)
+
+ def test_volume_changing(self):
+ """Sine wave with volume changing during playback."""
+ self.generate_volume_changing()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+ self.assertTrue(
+ len(result['volume_changes']) == len(self.volume_changing))
+ for i in range(len(self.volume_changing)):
+ self.assertTrue(
+ abs(self.volume_changing_time[i] - result['volume_changes'][i][
+ 0]) < 0.01)
+ self.assertTrue(
+ self.volume_changing[i] == result['volume_changes'][i][1])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/instrumentation/__init__.py b/acts_tests/acts_contrib/test_utils_tests/instrumentation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/instrumentation/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/__init__.py b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/__init__.py b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/instrumentation_command_builder_test.py b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/instrumentation_command_builder_test.py
new file mode 100755
index 0000000..584c8bf
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/instrumentation/device/command/instrumentation_command_builder_test.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder
+from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder
+
+
+class InstrumentationCommandBuilderTest(unittest.TestCase):
+
+ def test__runner_and_manifest_package_definition(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_manifest_package('package')
+ builder.set_runner('runner')
+ call = builder.build()
+ self.assertIn('package/runner', call)
+
+ def test__manifest_package_must_be_defined(self):
+ builder = InstrumentationCommandBuilder()
+
+ with self.assertRaisesRegex(Exception, '.*package cannot be none.*'):
+ builder.build()
+
+ def test__runner_must_be_defined(self):
+ builder = InstrumentationCommandBuilder()
+
+ with self.assertRaisesRegex(Exception, '.*runner cannot be none.*'):
+ builder.build()
+
+ def test__output_as_proto(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+ builder.set_proto_path()
+
+ call = builder.build()
+ self.assertIn('-f', call)
+
+ def test__proto_flag_with_set_proto_path(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+ builder.set_proto_path('/some/proto/path')
+
+ call = builder.build()
+ self.assertIn('-f', call)
+ self.assertIn('/some/proto/path', call)
+
+ def test__set_output_as_text_clears_proto_options(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+ builder.set_proto_path('/some/proto/path')
+ builder.set_output_as_text()
+
+ call = builder.build()
+ self.assertNotIn('-f', call)
+ self.assertNotIn('/some/proto/path', call)
+
+ def test__set_nohup(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+ builder.set_nohup()
+
+ call = builder.build()
+ self.assertEqual(
+ call, 'nohup am instrument some.manifest.package/runner >> '
+ '$EXTERNAL_STORAGE/instrumentation_output.txt 2>&1')
+
+ def test__key_value_param_definition(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+
+ builder.add_key_value_param('my_key_1', 'my_value_1')
+ builder.add_key_value_param('my_key_2', 'my_value_2')
+
+ call = builder.build()
+ self.assertIn('-e my_key_1 my_value_1', call)
+ self.assertIn('-e my_key_2 my_value_2', call)
+
+ def test__flags(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+
+ builder.add_flag('--flag1')
+ builder.add_flag('--flag2')
+
+ call = builder.build()
+ self.assertIn('--flag1', call)
+ self.assertIn('--flag2', call)
+
+ def test__remove_flags(self):
+ builder = InstrumentationCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+
+ builder.add_flag('--flag1')
+ builder.add_flag('--flag2')
+ builder.remove_flag('--flag1')
+
+ call = builder.build()
+ self.assertNotIn('--flag1', call)
+ self.assertIn('--flag2', call)
+
+
+class InstrumentationTestCommandBuilderTest(unittest.TestCase):
+ """Test class for
+ acts_contrib/test_utils/instrumentation/instrumentation_call_builder.py
+ """
+
+ def test__test_packages_can_not_be_added_if_classes_were_added_first(self):
+ builder = InstrumentationTestCommandBuilder()
+ builder.add_test_class('some.tests.Class')
+
+ with self.assertRaisesRegex(Exception, '.*only a list of classes.*'):
+ builder.add_test_package('some.tests.package')
+
+ def test__test_classes_can_not_be_added_if_packages_were_added_first(self):
+ builder = InstrumentationTestCommandBuilder()
+ builder.add_test_package('some.tests.package')
+
+ with self.assertRaisesRegex(Exception, '.*only a list of classes.*'):
+ builder.add_test_class('some.tests.Class')
+
+ def test__test_classes_and_test_methods_can_be_combined(self):
+ builder = InstrumentationTestCommandBuilder()
+ builder.set_runner('runner')
+ builder.set_manifest_package('some.manifest.package')
+ builder.add_test_class('some.tests.Class1')
+ builder.add_test_method('some.tests.Class2', 'favoriteTestMethod')
+
+ call = builder.build()
+ self.assertIn('some.tests.Class1', call)
+ self.assertIn('some.tests.Class2', call)
+ self.assertIn('favoriteTestMethod', call)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/__init__.py b/acts_tests/acts_contrib/test_utils_tests/power/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/__init__.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/consume_parameter_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/consume_parameter_test.py
new file mode 100644
index 0000000..f1b899e
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/consume_parameter_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mobly.config_parser as mobly_config_parser
+import mock_bokeh
+from unittest import mock
+
+
+class ConsumeParameterTest(unittest.TestCase):
+ """ Unit tests for testing the consumption of test name parameters
+ for instances of PowerCellularLabBaseTest
+ """
+ @classmethod
+ def setUpClass(self):
+ from acts_contrib.test_utils.power.cellular.cellular_power_base_test import PowerCellularLabBaseTest as PCBT
+ self.PCBT = PCBT
+ PCBT.log = mock.Mock()
+ PCBT.log_path = ''
+
+ def setUp(self):
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PCBT(test_run_config)
+ self.test = test
+
+ def test_consume_parameter_typical_case(self):
+ """ Tests the typical case: The parameter is available
+ for consumption and it has enough values
+ """
+ parameters = ['param1', 1, 2, 'param2', 3, 'param3', 'value']
+ expected = ['param2', 3]
+ self.test.unpack_userparams(parameters=parameters)
+ try:
+ result = self.test.consume_parameter('param2', 1)
+ self.assertTrue(
+ result == expected,
+ 'Consume parameter did not return the expected result')
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def test_consume_parameter_returns_empty_when_parameter_unavailabe(self):
+ """ Tests the case where the requested parameter is unavailable
+ for consumption. In this case, a ValueError should be raised
+ """
+ parameters = ['param1', 1, 2]
+ expected = []
+ self.test.unpack_userparams(parameters=parameters)
+ try:
+ result = self.test.consume_parameter('param2', 1)
+ self.assertTrue(
+ result == expected,
+ 'Consume parameter should return empty list for an invalid key'
+ )
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def test_consume_parameter_throws_when_requesting_too_many_parameters(
+ self):
+ """ Tests the case where the requested parameter is available
+ for consumption, but too many values are requested
+ """
+ parameters = ['param1', 1, 2]
+ self.test.unpack_userparams(parameters=parameters)
+ with self.assertRaises(ValueError):
+ self.test.consume_parameter('param1', 3)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/ensure_valid_calibration_table_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/ensure_valid_calibration_table_test.py
new file mode 100644
index 0000000..b15e892
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/ensure_valid_calibration_table_test.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mobly.config_parser as mobly_config_parser
+import mock_bokeh
+from unittest import mock
+
+
+class EnsureValidCalibrationTableTest(unittest.TestCase):
+ """ Unit tests for exercising the logic of ensure_valid_calibration_table
+ for instances of PowerCellularLabBaseTest
+ """
+
+ VALID_CALIBRATION_TABLE = {'1': {'2': {'3': 123, '4': 3.14}}, '2': 45.67}
+
+ INVALID_CALIBRATION_TABLE = invalid = {'1': {'a': 'invalid'}, '2': 1234}
+
+ @classmethod
+ def setUpClass(self):
+ from acts_contrib.test_utils.power.cellular.cellular_power_base_test import PowerCellularLabBaseTest as PCBT
+ self.PCBT = PCBT
+ PCBT.log = mock.Mock()
+ PCBT.log_path = ''
+
+
+ def setUp(self):
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PCBT(test_run_config)
+ self.test = test
+
+
+ def _assert_no_exception(self, func, *args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except Exception as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def _assert_calibration_table_passes(self, table):
+ self._assert_no_exception(self.test.ensure_valid_calibration_table, table)
+
+ def _assert_calibration_table_fails(self, table):
+ with self.assertRaises(TypeError):
+ self.test.ensure_valid_calibration_table(table)
+
+ def test_ensure_valid_calibration_table_passes_with_empty_table(self):
+ """ Ensure that empty calibration tables are invalid """
+ self._assert_calibration_table_passes({})
+
+ def test_ensure_valid_calibration_table_passes_with_valid_table(self):
+ """ Ensure that valid calibration tables throw no error """
+ self._assert_calibration_table_passes(self.VALID_CALIBRATION_TABLE)
+
+ def test_ensure_valid_calibration_table_fails_with_invalid_data(self):
+ """ Ensure that calibration tables with invalid entries throw an error """
+ self._assert_calibration_table_fails(self.INVALID_CALIBRATION_TABLE)
+
+ def test_ensure_valid_calibration_table_fails_with_none(self):
+ """ Ensure an exception is thrown if no calibration table is given """
+ self._assert_calibration_table_fails(None)
+
+ def test_ensure_valid_calibration_table_fails_with_invalid_type(self):
+ """ Ensure an exception is thrown if no calibration table is given """
+ self._assert_calibration_table_fails([])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/init_simulation_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/init_simulation_test.py
new file mode 100644
index 0000000..ef89239
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/init_simulation_test.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mobly.config_parser as mobly_config_parser
+import mock_bokeh
+from acts.controllers.cellular_lib.LteSimulation import LteSimulation
+from acts.controllers.cellular_lib.UmtsSimulation import UmtsSimulation
+from unittest import mock
+
+
+class InitSimulationTest(unittest.TestCase):
+ """ Unit tests for ensuring the simulation is correctly
+ initialized for instances of PowerCellularLabBaseTest
+ """
+ @classmethod
+ def setUpClass(self):
+ from acts_contrib.test_utils.power.cellular.cellular_power_base_test import PowerCellularLabBaseTest as PCBT
+ self.PCBT = PCBT
+ PCBT.log = mock.Mock()
+ PCBT.log_path = ''
+
+ def setUp(self):
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PCBT(test_run_config)
+ self.test = test
+
+ def test_init_simulation_reuses_simulation_if_same_type(self):
+ """ Ensure that a new simulation is not instantiated if
+ the type is the same as the last simulation
+ """
+ mock_lte_sim = mock.Mock(spec=LteSimulation)
+ self.test.unpack_userparams(simulation=mock_lte_sim)
+ try:
+ self.test.init_simulation(self.PCBT.PARAM_SIM_TYPE_LTE)
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+ self.assertTrue(self.test.simulation is mock_lte_sim,
+ 'A new simulation was instantiated')
+
+ def test_init_simulation_does_not_reuse_simulation_if_different_type(self):
+ """ Ensure that a new simulation is instantiated if
+ the type is different from the last simulation
+ """
+ self.test.unpack_userparams(simulation=mock.Mock(spec=LteSimulation),
+ test_params=mock.Mock())
+ try:
+ with mock.patch.object(UmtsSimulation,
+ '__init__',
+ return_value=None) as mock_init:
+ self.test.init_simulation(self.PCBT.PARAM_SIM_TYPE_UMTS)
+ except Exception as e:
+ self.fail('Error thrown: {}'.format(e))
+ self.assertTrue(mock_init.called,
+ 'A new simulation was not instantiated')
+
+ def test_init_simulation_throws_error_with_invalid_simulation_type(self):
+ """ Ensure that a new simulation is not instantiated if
+ the type is invalid
+ """
+ self.test.unpack_userparams(simulation=mock.Mock(spec=LteSimulation))
+ with self.assertRaises(ValueError):
+ self.test.init_simulation('Invalid simulation type')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/initialize_simulator_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/initialize_simulator_test.py
new file mode 100644
index 0000000..e495a2a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/initialize_simulator_test.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mobly.config_parser as mobly_config_parser
+import mock_bokeh
+from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsu
+from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw
+from unittest import mock
+
+
+class InitializeSimulatorTest(unittest.TestCase):
+ """ Unit tests for ensuring the simulator is correctly
+ initialized for instances of PowerCellularLabBaseTest
+ """
+ @classmethod
+ def setUpClass(self):
+ from acts_contrib.test_utils.power.cellular.cellular_power_base_test import PowerCellularLabBaseTest as PCBT
+ self.PCBT = PCBT
+ PCBT.log = mock.Mock()
+ PCBT.log_path = ''
+
+ def setUp(self):
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PCBT(test_run_config)
+ self.test = test
+
+ def test_initialize_simulator_md8475_A(self):
+ """ Ensure that an instance of MD8475CellularSimulator
+ is returned when requesting md8475_version A
+ """
+ self.test.unpack_userparams(md8475_version='A', md8475a_ip_address='12345')
+ try:
+ with mock.patch.object(anritsu.MD8475CellularSimulator,
+ '__init__',
+ return_value=None):
+ result = self.test.initialize_simulator()
+ self.assertTrue(
+ isinstance(result, anritsu.MD8475CellularSimulator),
+ 'Incorrect simulator type returned for md8475_version A')
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def test_initialize_simulator_md8475_B(self):
+ """ Ensure that an instance of MD8475BCellularSimulator
+ is returned when requesting md8475_version B
+ """
+ self.test.unpack_userparams(md8475_version='B', md8475a_ip_address='12345')
+ try:
+ with mock.patch.object(anritsu.MD8475BCellularSimulator,
+ '__init__',
+ return_value=None):
+ result = self.test.initialize_simulator()
+ self.assertTrue(
+ isinstance(result, anritsu.MD8475BCellularSimulator),
+ 'Incorrect simulator type returned for md8475_version B')
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def test_initialize_simulator_cmw500(self):
+ """ Ensure that an instance of CMW500CellularSimulator
+ is returned when requesting cmw500
+ """
+ self.test.unpack_userparams(md8475_version=None,
+ md8475a_ip_address=None,
+ cmw500_ip='12345',
+ cmw500_port='12345')
+ try:
+ with mock.patch.object(cmw.CMW500CellularSimulator,
+ '__init__',
+ return_value=None):
+ result = self.test.initialize_simulator()
+ self.assertTrue(
+ isinstance(result, cmw.CMW500CellularSimulator),
+ 'Incorrect simulator type returned for cmw500')
+ except ValueError as e:
+ self.fail('Error thrown: {}'.format(e))
+
+ def test_initialize_simulator_throws_with_missing_configs(self):
+ """ Ensure that an error is raised when initialize_simulator
+ is called with missing configs
+ """
+ self.test.unpack_userparams(md8475_version=None,
+ md8475a_ip_address=None,
+ cmw500_ip='12345',
+ cmw500_port=None)
+ with self.assertRaises(RuntimeError), mock.patch.object(
+ cmw.CMW500CellularSimulator, '__init__', return_value=None):
+ self.test.initialize_simulator()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/mock_bokeh.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/mock_bokeh.py
new file mode 100644
index 0000000..7364386
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/mock_bokeh.py
@@ -0,0 +1,8 @@
+import sys
+from mock import Mock
+
+sys.modules['bokeh'] = Mock()
+sys.modules['bokeh.layouts'] = Mock()
+sys.modules['bokeh.models'] = Mock()
+sys.modules['bokeh.models.widgets'] = Mock()
+sys.modules['bokeh.plotting'] = Mock()
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/power_tel_traffic_e2e_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/power_tel_traffic_e2e_test.py
new file mode 100644
index 0000000..d0c9c0a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/power_tel_traffic_e2e_test.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mock_bokeh
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import mobly.config_parser as mobly_config_parser
+from acts.controllers.cellular_lib.LteSimulation import LteSimulation
+from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw
+from unittest import mock
+
+magic_patch = lambda patched: mock.patch(patched, mock.MagicMock())
+
+
+class PowerTelTrafficE2eTest(unittest.TestCase):
+ """ E2E sanity test for the power cellular traffic tests """
+ @classmethod
+ def setUpClass(cls):
+ cls.PTTT = ctpt.PowerTelTrafficTest
+ cls.PTTT.log = mock.Mock()
+ cls.PTTT.log_path = ''
+
+ @magic_patch('json.load')
+ @magic_patch('builtins.open')
+ @magic_patch('os.chmod')
+ @magic_patch('os.system')
+ @magic_patch('time.sleep')
+ @magic_patch(
+ 'acts_contrib.test_utils.power.cellular.cellular_power_base_test.telutils')
+ @magic_patch('acts_contrib.test_utils.power.PowerBaseTest.wutils')
+ @magic_patch(
+ 'acts.metrics.loggers.blackbox.BlackboxMetricLogger.for_test_case')
+ @magic_patch(
+ 'acts_contrib.test_utils.power.loggers.power_metric_logger.PowerMetricLogger.for_test_case'
+ )
+ def test_e2e(self, *args):
+
+ # Configure the test
+ test_to_mock = 'test_lte_traffic_direction_dlul_blimit_0_0'
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PTTT(test_run_config)
+ mock_android = mock.Mock()
+ mock_android.model = 'coral'
+ test.unpack_userparams(
+ android_devices=[mock_android],
+ monsoons=[mock.Mock()],
+ iperf_servers=[mock.Mock(), mock.Mock()],
+ packet_senders=[mock.Mock(), mock.Mock()],
+ custom_files=[
+ 'pass_fail_threshold_coral.json', 'rockbottom_coral.sh'
+ ],
+ simulation=mock.Mock(spec=LteSimulation),
+ mon_freq=5000,
+ mon_duration=0,
+ mon_offset=0,
+ current_test_name=test_to_mock,
+ test_name=test_to_mock,
+ test_result=mock.Mock(),
+ bug_report={},
+ dut_rockbottom=mock.Mock(),
+ start_tel_traffic=mock.Mock(),
+ init_simulation=mock.Mock(),
+ initialize_simulator=mock.Mock(return_value=mock.Mock(
+ spec=cmw.CMW500CellularSimulator)),
+ collect_power_data=mock.Mock(),
+ get_iperf_results=mock.Mock(return_value={
+ 'ul': 0,
+ 'dl': 0
+ }),
+ pass_fail_check=mock.Mock())
+
+ # Emulate lifecycle
+ test.setup_class()
+ test.setup_test()
+ test.power_tel_traffic_test()
+ test.teardown_test()
+ test.teardown_class()
+
+ self.assertTrue(test.start_tel_traffic.called,
+ 'Start traffic was not called')
+ self.assertTrue(test.init_simulation.called,
+ 'Simulation was not initialized')
+ self.assertTrue(test.initialize_simulator.called,
+ 'Simulator was not initialized')
+ self.assertTrue(test.collect_power_data.called,
+ 'Power data was not collected')
+ self.assertTrue(test.get_iperf_results.called,
+ 'Did not get iperf results')
+ self.assertTrue(test.pass_fail_check.called,
+ 'Pass/Fail check was not performed')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/save_summary_to_file_test.py b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/save_summary_to_file_test.py
new file mode 100644
index 0000000..e9563af
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils_tests/power/tel/lab/save_summary_to_file_test.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import mobly.config_parser as mobly_config_parser
+import mock_bokeh
+from acts.controllers.cellular_lib.LteSimulation import LteSimulation
+from unittest import mock
+from unittest.mock import mock_open
+
+
+class SaveSummaryToFileTest(unittest.TestCase):
+ """ Unit tests for testing the save summary functionality for
+ instances of PowerCellularLabBaseTest
+ """
+
+ @classmethod
+ def setUpClass(self):
+ from acts_contrib.test_utils.power.cellular.cellular_power_base_test import PowerCellularLabBaseTest as PCBT
+ self.PCBT = PCBT
+ PCBT.log = mock.Mock()
+ PCBT.log_path = ''
+
+ def setUp(self):
+ self.tb_key = 'testbed_configs'
+ test_run_config = mobly_config_parser.TestRunConfig()
+ test_run_config.testbed_name = 'MockTestBed'
+ test_run_config.log_path = '/tmp'
+ test_run_config.summary_writer = mock.MagicMock()
+ test = self.PCBT(test_run_config)
+ self.test = test
+
+ def test_save_summary_to_file(self):
+ """ Ensure that a new file is written when saving
+ the test summary
+ """
+ self.test.unpack_userparams(simulation=mock.Mock(spec=LteSimulation))
+ m = mock_open()
+ with mock.patch('builtins.open', m, create=False):
+ self.test.save_summary_to_file()
+ self.assertTrue(m.called, 'Test summary was not written to output')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py b/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
index 26c459f..c044b4d 100644
--- a/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
+++ b/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
@@ -22,12 +22,12 @@
from acts.controllers.sl4a_lib import rpc_client
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import adv_fail
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
-from acts.test_utils.bt.bt_constants import java_integer
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import adv_fail
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
+from acts_contrib.test_utils.bt.bt_constants import java_integer
class BleAdvertiseVerificationError(Exception):
diff --git a/acts_tests/tests/google/ble/api/BleScanApiTest.py b/acts_tests/tests/google/ble/api/BleScanApiTest.py
index 06f2362..7e824f4 100644
--- a/acts_tests/tests/google/ble/api/BleScanApiTest.py
+++ b/acts_tests/tests/google/ble/api/BleScanApiTest.py
@@ -22,12 +22,12 @@
from acts.controllers.sl4a_lib import rpc_client
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_result_types
-from acts.test_utils.bt.bt_constants import ble_scan_settings_report_delay_milli_seconds
-from acts.test_utils.bt.bt_constants import ble_uuids
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_callback_types
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_result_types
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_report_delay_milli_seconds
+from acts_contrib.test_utils.bt.bt_constants import ble_uuids
class BleScanResultsError(Exception):
diff --git a/acts_tests/tests/google/ble/api/GattApiTest.py b/acts_tests/tests/google/ble/api/GattApiTest.py
index cc87979..f13809f 100644
--- a/acts_tests/tests/google/ble/api/GattApiTest.py
+++ b/acts_tests/tests/google/ble/api/GattApiTest.py
@@ -19,8 +19,8 @@
from acts.controllers.sl4a_lib import rpc_client
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
class GattApiTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/beacon_tests/BeaconSwarmTest.py b/acts_tests/tests/google/ble/beacon_tests/BeaconSwarmTest.py
index 0df9a7b..a084794 100644
--- a/acts_tests/tests/google/ble/beacon_tests/BeaconSwarmTest.py
+++ b/acts_tests/tests/google/ble/beacon_tests/BeaconSwarmTest.py
@@ -22,17 +22,17 @@
import threading
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
-from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
-from acts.test_utils.bt.bt_test_utils import adv_succ
-from acts.test_utils.bt.bt_test_utils import batch_scan_result
-from acts.test_utils.bt.bt_test_utils import scan_result
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts_contrib.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts_contrib.test_utils.bt.bt_test_utils import adv_succ
+from acts_contrib.test_utils.bt.bt_test_utils import batch_scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
class BeaconSwarmTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/bt5/AdvertisingSetTest.py b/acts_tests/tests/google/ble/bt5/AdvertisingSetTest.py
index de4192f..0245b8d 100644
--- a/acts_tests/tests/google/ble/bt5/AdvertisingSetTest.py
+++ b/acts_tests/tests/google/ble/bt5/AdvertisingSetTest.py
@@ -26,18 +26,18 @@
from acts.asserts import assert_equal
from acts.asserts import assert_true
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import advertising_set_started
-from acts.test_utils.bt.bt_constants import advertising_set_stopped
-from acts.test_utils.bt.bt_constants import advertising_set_enabled
-from acts.test_utils.bt.bt_constants import advertising_set_data_set
-from acts.test_utils.bt.bt_constants import advertising_set_scan_response_set
-from acts.test_utils.bt.bt_constants import advertising_set_parameters_update
-from acts.test_utils.bt.bt_constants import advertising_set_periodic_parameters_updated
-from acts.test_utils.bt.bt_constants import advertising_set_periodic_data_set
-from acts.test_utils.bt.bt_constants import advertising_set_periodic_enable
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_started
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_stopped
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_enabled
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_data_set
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_scan_response_set
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_parameters_update
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_periodic_parameters_updated
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_periodic_data_set
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_periodic_enable
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
from acts import signals
diff --git a/acts_tests/tests/google/ble/bt5/Bt5ScanTest.py b/acts_tests/tests/google/ble/bt5/Bt5ScanTest.py
index e2c9c83..496bbf9 100644
--- a/acts_tests/tests/google/ble/bt5/Bt5ScanTest.py
+++ b/acts_tests/tests/google/ble/bt5/Bt5ScanTest.py
@@ -25,17 +25,17 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_phys
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import batch_scan_result
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_constants import advertising_set_on_own_address_read
-from acts.test_utils.bt.bt_constants import advertising_set_started
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_phys
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import batch_scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_on_own_address_read
+from acts_contrib.test_utils.bt.bt_constants import advertising_set_started
from acts import signals
diff --git a/acts_tests/tests/google/ble/bt5/PhyTest.py b/acts_tests/tests/google/ble/bt5/PhyTest.py
index 0b1ecfa..69487b2 100644
--- a/acts_tests/tests/google/ble/bt5/PhyTest.py
+++ b/acts_tests/tests/google/ble/bt5/PhyTest.py
@@ -20,11 +20,11 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
-from acts.test_utils.bt.bt_constants import gatt_connection_priority
-from acts.test_utils.bt.bt_constants import gatt_event
-from acts.test_utils.bt.bt_constants import gatt_phy
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_connection_priority
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_phy
from acts import signals
CONNECTION_PRIORITY_HIGH = gatt_connection_priority['high']
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
index c38fc93..2e2be83 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
@@ -24,21 +24,21 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_n_advertisements
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
-from acts.test_utils.bt.bt_test_utils import teardown_n_advertisements
-from acts.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_callback_types
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import BtTestUtilsError
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_n_advertisements
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.bt_test_utils import teardown_n_advertisements
+from acts_contrib.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements
class ConcurrentBleAdvertisementDiscoveryTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
index 09c6cd3..7fd0d52 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
@@ -24,21 +24,21 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import setup_n_advertisements
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
-from acts.test_utils.bt.bt_test_utils import teardown_n_advertisements
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import BtTestUtilsError
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_callback_types
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import setup_n_advertisements
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.bt_test_utils import teardown_n_advertisements
class ConcurrentBleAdvertisingTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleScanningTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
index 512aed8..3905bdb 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
@@ -23,16 +23,16 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_failed
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_callback_types
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_failed
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
class ConcurrentBleScanningTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
index ec5e09c..6a5b861 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
@@ -23,19 +23,19 @@
import concurrent.futures
import threading
import time
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import bt_profile_constants
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_characteristic_value_format
-from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_service_types
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_gatt_utils import run_continuous_write_descriptor
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
-from acts.test_utils.bt.gatts_lib import GattServerLib
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_constants
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_gatt_utils import run_continuous_write_descriptor
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.gatts_lib import GattServerLib
from acts.test_decorators import test_tracker_info
service_uuid = '0000a00a-0000-1000-8000-00805f9b34fb'
diff --git a/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py b/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
index 353f507..70dcb89 100644
--- a/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
+++ b/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -24,21 +24,21 @@
from acts import utils
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
-from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
-from acts.test_utils.bt.bt_constants import default_le_data_length
-from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
-from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
-from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
-from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
-from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
+from acts_contrib.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
+from acts_contrib.test_utils.bt.bt_constants import default_le_data_length
+from acts_contrib.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts_contrib.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts_contrib.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts_contrib.test_utils.bt.bt_constants import le_connection_event_time_step_ms
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import kill_bluetooth_process
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.bt_test_utils import verify_server_and_client_connected
class BleCoc2ConnTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
index 166b848..9e95dcb 100644
--- a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
+++ b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
@@ -24,19 +24,19 @@
from acts import utils
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
-from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
-from acts.test_utils.bt.bt_constants import default_le_data_length
-from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
-from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
-from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
+from acts_contrib.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
+from acts_contrib.test_utils.bt.bt_constants import default_le_data_length
+from acts_contrib.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts_contrib.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import kill_bluetooth_process
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.bt_test_utils import verify_server_and_client_connected
class BleCocTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/examples/BleExamplesTest.py b/acts_tests/tests/google/ble/examples/BleExamplesTest.py
index 1ced2db..a9ec058 100644
--- a/acts_tests/tests/google/ble/examples/BleExamplesTest.py
+++ b/acts_tests/tests/google/ble/examples/BleExamplesTest.py
@@ -20,11 +20,11 @@
import pprint
from acts.controllers import android_device
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
class BleExamplesTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/examples/GattServerExampleTest.py b/acts_tests/tests/google/ble/examples/GattServerExampleTest.py
index e1f6476..7ffcebb 100644
--- a/acts_tests/tests/google/ble/examples/GattServerExampleTest.py
+++ b/acts_tests/tests/google/ble/examples/GattServerExampleTest.py
@@ -14,13 +14,13 @@
# License for the specific language governing permissions and limitations under
# the License.
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_service_types
-from acts.test_utils.bt.bt_constants import gatt_characteristic_value_format
-from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
-from acts.test_utils.bt.gatts_lib import GattServerLib
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.gatts_lib import GattServerLib
service_uuid = '0000a00a-0000-1000-8000-00805f9b34fb'
characteristic_uuid = 'aa7edd5a-4d1d-4f0e-883a-d145616a1630'
diff --git a/acts_tests/tests/google/ble/filtering/FilteringTest.py b/acts_tests/tests/google/ble/filtering/FilteringTest.py
index d1bdc39..0e898c8 100644
--- a/acts_tests/tests/google/ble/filtering/FilteringTest.py
+++ b/acts_tests/tests/google/ble/filtering/FilteringTest.py
@@ -20,18 +20,18 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
-from acts.test_utils.bt.bt_constants import java_integer
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import small_timeout
-from acts.test_utils.bt.bt_constants import adv_fail
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
+from acts_contrib.test_utils.bt.bt_constants import java_integer
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import small_timeout
+from acts_contrib.test_utils.bt.bt_constants import adv_fail
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_result
class FilteringTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/filtering/UniqueFilteringTest.py b/acts_tests/tests/google/ble/filtering/UniqueFilteringTest.py
index c2e837c..fa20a39 100644
--- a/acts_tests/tests/google/ble/filtering/UniqueFilteringTest.py
+++ b/acts_tests/tests/google/ble/filtering/UniqueFilteringTest.py
@@ -25,14 +25,14 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_constants import batch_scan_result
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import batch_scan_result
+from acts_contrib.test_utils.bt.bt_constants import scan_result
class UniqueFilteringTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/gatt/GattConnectTest.py b/acts_tests/tests/google/ble/gatt/GattConnectTest.py
index 52f3601..ebe5714 100644
--- a/acts_tests/tests/google/ble/gatt/GattConnectTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattConnectTest.py
@@ -22,31 +22,31 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_match_nums
-from acts.test_utils.bt.bt_constants import bt_profile_constants
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_service_types
-from acts.test_utils.bt.bt_constants import gatt_cb_err
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_constants import gatt_connection_state
-from acts.test_utils.bt.bt_constants import gatt_mtu_size
-from acts.test_utils.bt.bt_constants import gatt_phy_mask
-from acts.test_utils.bt.bt_constants import gatt_transport
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
-from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import wait_for_gatt_disconnect_event
-from acts.test_utils.bt.bt_gatt_utils import close_gatt_client
-from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
-from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import setup_multiple_services
-from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_match_nums
+from acts_contrib.test_utils.bt.bt_constants import bt_profile_constants
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_connection_state
+from acts_contrib.test_utils.bt.bt_constants import gatt_mtu_size
+from acts_contrib.test_utils.bt.bt_constants import gatt_phy_mask
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import wait_for_gatt_disconnect_event
+from acts_contrib.test_utils.bt.bt_gatt_utils import close_gatt_client
+from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_multiple_services
+from acts_contrib.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
PHYSICAL_DISCONNECT_TIMEOUT = 5
diff --git a/acts_tests/tests/google/ble/gatt/GattNotifyTest.py b/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
index 7d62d65..4b142a5 100644
--- a/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
@@ -18,13 +18,13 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_event
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
from math import ceil
diff --git a/acts_tests/tests/google/ble/gatt/GattReadTest.py b/acts_tests/tests/google/ble/gatt/GattReadTest.py
index e880757..216f470 100644
--- a/acts_tests/tests/google/ble/gatt/GattReadTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattReadTest.py
@@ -18,12 +18,12 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_event
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
from math import ceil
diff --git a/acts_tests/tests/google/ble/gatt/GattToolTest.py b/acts_tests/tests/google/ble/gatt/GattToolTest.py
index 8e7a9f0..116495c 100644
--- a/acts_tests/tests/google/ble/gatt/GattToolTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattToolTest.py
@@ -27,19 +27,19 @@
from queue import Empty
import time
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import gatt_cb_err
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_transport
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
-from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_err
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
class GattToolTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/gatt/GattWriteTest.py b/acts_tests/tests/google/ble/gatt/GattWriteTest.py
index d245e9e..87e043a 100644
--- a/acts_tests/tests/google/ble/gatt/GattWriteTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattWriteTest.py
@@ -18,16 +18,16 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_event
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_constants import gatt_connection_priority
-from acts.test_utils.bt.bt_constants import gatt_characteristic_attr_length
-from acts.test_utils.bt.bt_constants import gatt_mtu_size
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_connection_priority
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_attr_length
+from acts_contrib.test_utils.bt.bt_constants import gatt_mtu_size
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
class GattWriteTest(GattConnectedBaseTest):
diff --git a/acts_tests/tests/google/ble/scan/BleBackgroundScanTest.py b/acts_tests/tests/google/ble/scan/BleBackgroundScanTest.py
index 6839602..533d9fa 100644
--- a/acts_tests/tests/google/ble/scan/BleBackgroundScanTest.py
+++ b/acts_tests/tests/google/ble/scan/BleBackgroundScanTest.py
@@ -21,18 +21,18 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import bluetooth_off
-from acts.test_utils.bt.bt_test_utils import bluetooth_on
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_constants import bluetooth_le_off
-from acts.test_utils.bt.bt_constants import bluetooth_le_on
-from acts.test_utils.bt.bt_constants import bt_adapter_states
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_off
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_on
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_le_off
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_le_on
+from acts_contrib.test_utils.bt.bt_constants import bt_adapter_states
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import scan_result
import time
diff --git a/acts_tests/tests/google/ble/scan/BleOnLostOnFoundTest.py b/acts_tests/tests/google/ble/scan/BleOnLostOnFoundTest.py
index 01f7976..6926c6a 100644
--- a/acts_tests/tests/google/ble/scan/BleOnLostOnFoundTest.py
+++ b/acts_tests/tests/google/ble/scan/BleOnLostOnFoundTest.py
@@ -20,18 +20,18 @@
from queue import Empty
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
-from acts.test_utils.bt.BleEnum import ScanSettingsMatchMode
-from acts.test_utils.bt.BleEnum import ScanSettingsMatchNum
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_match_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_match_nums
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_callback_types
+from acts_contrib.test_utils.bt.BleEnum import ScanSettingsMatchMode
+from acts_contrib.test_utils.bt.BleEnum import ScanSettingsMatchNum
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_match_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_match_nums
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_result
class BleOnLostOnFoundTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/scan/BleOpportunisticScanTest.py b/acts_tests/tests/google/ble/scan/BleOpportunisticScanTest.py
index 9e59128..570dd1e 100644
--- a/acts_tests/tests/google/ble/scan/BleOpportunisticScanTest.py
+++ b/acts_tests/tests/google/ble/scan/BleOpportunisticScanTest.py
@@ -25,15 +25,15 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import batch_scan_result
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import batch_scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_result
class BleOpportunisticScanTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py b/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
index 07ae898..a19e80c 100644
--- a/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
+++ b/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
@@ -25,15 +25,15 @@
from queue import Empty
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import bt_default_timeout
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import bt_default_timeout
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
class BleScanScreenStateTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/system_tests/BleStressTest.py b/acts_tests/tests/google/ble/system_tests/BleStressTest.py
index afbb67a..721ef6c 100644
--- a/acts_tests/tests/google/ble/system_tests/BleStressTest.py
+++ b/acts_tests/tests/google/ble/system_tests/BleStressTest.py
@@ -23,15 +23,15 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
-from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import BtTestUtilsError
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts_contrib.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import scan_result
class BleStressTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/ble/system_tests/GattLongevityTest.py b/acts_tests/tests/google/ble/system_tests/GattLongevityTest.py
index bb13646..88b1774 100644
--- a/acts_tests/tests/google/ble/system_tests/GattLongevityTest.py
+++ b/acts_tests/tests/google/ble/system_tests/GattLongevityTest.py
@@ -18,16 +18,16 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_event
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_constants import gatt_connection_priority
-from acts.test_utils.bt.bt_constants import gatt_characteristic_attr_length
-from acts.test_utils.bt.GattEnum import MtuSize
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_event
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_constants import gatt_connection_priority
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_attr_length
+from acts_contrib.test_utils.bt.GattEnum import MtuSize
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
class GattLongevityTest(GattConnectedBaseTest):
diff --git a/acts_tests/tests/google/bt/AkXB10PairingTest.py b/acts_tests/tests/google/bt/AkXB10PairingTest.py
index 10e7335..99e7529 100644
--- a/acts_tests/tests/google/bt/AkXB10PairingTest.py
+++ b/acts_tests/tests/google/bt/AkXB10PairingTest.py
@@ -20,7 +20,7 @@
import time
from acts.controllers.relay_lib.relay import SynchronizeRelays
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
log = logging
diff --git a/acts_tests/tests/google/bt/BtAirplaneModeTest.py b/acts_tests/tests/google/bt/BtAirplaneModeTest.py
index e2dd7eb..aeafc7a 100644
--- a/acts_tests/tests/google/bt/BtAirplaneModeTest.py
+++ b/acts_tests/tests/google/bt/BtAirplaneModeTest.py
@@ -19,9 +19,9 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from queue import Empty
import time
diff --git a/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py b/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
index 3194d76..ade4c0a 100644
--- a/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
+++ b/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
@@ -22,14 +22,14 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import bt_scan_mode_types
-from acts.test_utils.bt.bt_test_utils import check_device_supported_profiles
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import set_device_name
-from acts.test_utils.bt.bt_test_utils import set_bt_scan_mode
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts_contrib.test_utils.bt.bt_test_utils import check_device_supported_profiles
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import set_device_name
+from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
class BtBasicFunctionalityTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/BtFactoryResetTest.py b/acts_tests/tests/google/bt/BtFactoryResetTest.py
index f33b210..49ea0e0 100644
--- a/acts_tests/tests/google/bt/BtFactoryResetTest.py
+++ b/acts_tests/tests/google/bt/BtFactoryResetTest.py
@@ -17,8 +17,8 @@
Test script to test BluetoothAdapter's resetFactory method.
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
class BtFactoryResetTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/BtKillProcessTest.py b/acts_tests/tests/google/bt/BtKillProcessTest.py
index ed53e20..b5adc77 100644
--- a/acts_tests/tests/google/bt/BtKillProcessTest.py
+++ b/acts_tests/tests/google/bt/BtKillProcessTest.py
@@ -21,7 +21,7 @@
import re
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
class BtKillProcessTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/BtMetricsTest.py b/acts_tests/tests/google/bt/BtMetricsTest.py
index 37d4150..42a6da3 100644
--- a/acts_tests/tests/google/bt/BtMetricsTest.py
+++ b/acts_tests/tests/google/bt/BtMetricsTest.py
@@ -16,11 +16,11 @@
from google import protobuf
from acts import asserts
-from acts.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.protos import bluetooth_pb2
+from acts_contrib.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.protos import bluetooth_pb2
from acts.utils import get_current_epoch_time, sync_device_time
diff --git a/acts_tests/tests/google/bt/RfcommTest.py b/acts_tests/tests/google/bt/RfcommTest.py
index 58de1be..14dc7bb 100644
--- a/acts_tests/tests/google/bt/RfcommTest.py
+++ b/acts_tests/tests/google/bt/RfcommTest.py
@@ -23,18 +23,18 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import bt_rfcomm_uuids
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
-from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
-from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import bt_rfcomm_uuids
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import kill_bluetooth_process
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.bt_test_utils import verify_server_and_client_connected
-from acts.test_utils.bt.BtEnum import RfcommUuid
+from acts_contrib.test_utils.bt.BtEnum import RfcommUuid
class RfcommTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/SonyXB2PairingTest.py b/acts_tests/tests/google/bt/SonyXB2PairingTest.py
index 4dcf863..795914e 100644
--- a/acts_tests/tests/google/bt/SonyXB2PairingTest.py
+++ b/acts_tests/tests/google/bt/SonyXB2PairingTest.py
@@ -20,7 +20,7 @@
import time
from acts.controllers.relay_lib.relay import SynchronizeRelays
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
log = logging
diff --git a/acts_tests/tests/google/bt/audio_lab/BtChameleonTest.py b/acts_tests/tests/google/bt/audio_lab/BtChameleonTest.py
index 1e76729..3a0be9d 100644
--- a/acts_tests/tests/google/bt/audio_lab/BtChameleonTest.py
+++ b/acts_tests/tests/google/bt/audio_lab/BtChameleonTest.py
@@ -27,16 +27,16 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.audio_analysis_lib.check_quality import quality_analysis
-from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
-from acts.test_utils.bt.bt_constants import audio_bits_per_sample_32
-from acts.test_utils.bt.bt_constants import audio_channel_mode_8
-from acts.test_utils.bt.bt_constants import audio_sample_rate_48000
-from acts.test_utils.bt.bt_constants import delay_after_binding_seconds
-from acts.test_utils.bt.bt_constants import delay_before_record_seconds
-from acts.test_utils.bt.bt_constants import fpga_linein_bus_endpoint
-from acts.test_utils.bt.bt_constants import headphone_bus_endpoint
-from acts.test_utils.bt.bt_constants import silence_wait_seconds
+from acts_contrib.test_utils.audio_analysis_lib.check_quality import quality_analysis
+from acts_contrib.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
+from acts_contrib.test_utils.bt.bt_constants import audio_bits_per_sample_32
+from acts_contrib.test_utils.bt.bt_constants import audio_channel_mode_8
+from acts_contrib.test_utils.bt.bt_constants import audio_sample_rate_48000
+from acts_contrib.test_utils.bt.bt_constants import delay_after_binding_seconds
+from acts_contrib.test_utils.bt.bt_constants import delay_before_record_seconds
+from acts_contrib.test_utils.bt.bt_constants import fpga_linein_bus_endpoint
+from acts_contrib.test_utils.bt.bt_constants import headphone_bus_endpoint
+from acts_contrib.test_utils.bt.bt_constants import silence_wait_seconds
class BtChameleonTest(BtFunhausBaseTest):
diff --git a/acts_tests/tests/google/bt/audio_lab/BtFunhausMetricsTest.py b/acts_tests/tests/google/bt/audio_lab/BtFunhausMetricsTest.py
index c734191..84166e9 100644
--- a/acts_tests/tests/google/bt/audio_lab/BtFunhausMetricsTest.py
+++ b/acts_tests/tests/google/bt/audio_lab/BtFunhausMetricsTest.py
@@ -15,7 +15,7 @@
from acts.test_decorators import test_tracker_info
from acts import asserts
-from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
+from acts_contrib.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
class BtFunhausMetricsTest(BtFunhausBaseTest):
diff --git a/acts_tests/tests/google/bt/audio_lab/BtFunhausTest.py b/acts_tests/tests/google/bt/audio_lab/BtFunhausTest.py
index 42cbc22..20363f9 100644
--- a/acts_tests/tests/google/bt/audio_lab/BtFunhausTest.py
+++ b/acts_tests/tests/google/bt/audio_lab/BtFunhausTest.py
@@ -19,7 +19,7 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
+from acts_contrib.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
class BtFunhausTest(BtFunhausBaseTest):
diff --git a/acts_tests/tests/google/bt/audio_lab/ThreeButtonDongleTest.py b/acts_tests/tests/google/bt/audio_lab/ThreeButtonDongleTest.py
index abf97be..4c032fd 100644
--- a/acts_tests/tests/google/bt/audio_lab/ThreeButtonDongleTest.py
+++ b/acts_tests/tests/google/bt/audio_lab/ThreeButtonDongleTest.py
@@ -19,8 +19,8 @@
import time
from acts.controllers.relay_lib.relay import SynchronizeRelays
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
class ThreeButtonDongleTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/avrcp/BtAvrcpPassthroughTest.py b/acts_tests/tests/google/bt/avrcp/BtAvrcpPassthroughTest.py
index 496db44..a0c82ac 100644
--- a/acts_tests/tests/google/bt/avrcp/BtAvrcpPassthroughTest.py
+++ b/acts_tests/tests/google/bt/avrcp/BtAvrcpPassthroughTest.py
@@ -17,8 +17,8 @@
import time
from acts.asserts import assert_equal
-from acts.test_utils.bt.AvrcpBaseTest import AvrcpBaseTest
-from acts.test_utils.car.car_media_utils import PlaybackState
+from acts_contrib.test_utils.bt.AvrcpBaseTest import AvrcpBaseTest
+from acts_contrib.test_utils.car.car_media_utils import PlaybackState
DEFAULT_TIMEOUT = 0.5
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py b/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
index 99f2851..b8d5ec8 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
@@ -21,15 +21,15 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BtEnum import BluetoothScanModeType
-from acts.test_utils.bt.bt_test_utils import check_device_supported_profiles
-from acts.test_utils.bt.bt_test_utils import log_energy_info
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import set_device_name
-from acts.test_utils.bt.bt_test_utils import set_bt_scan_mode
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BtEnum import BluetoothScanModeType
+from acts_contrib.test_utils.bt.bt_test_utils import check_device_supported_profiles
+from acts_contrib.test_utils.bt.bt_test_utils import log_energy_info
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import set_device_name
+from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
class BtCarBasicFunctionalityTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarHfpConferenceTest.py b/acts_tests/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
index 6a12e8f..edb9683 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
@@ -20,19 +20,19 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_telecom_utils
-from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
-from acts.test_utils.tel.tel_voice_utils import get_audio_route
-from acts.test_utils.tel.tel_voice_utils import set_audio_route
-from acts.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_telecom_utils
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ringing_call
+from acts_contrib.test_utils.tel.tel_voice_utils import get_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
BLUETOOTH_PKG_NAME = "com.android.bluetooth"
CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py b/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
index 006aa20..bda7e88 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
@@ -20,16 +20,16 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_bt_utils
-from acts.test_utils.car import car_telecom_utils
-from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_bt_utils
+from acts_contrib.test_utils.car import car_telecom_utils
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
BLUETOOTH_PKG_NAME = "com.android.bluetooth"
CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarHfpFuzzTest.py b/acts_tests/tests/google/bt/car_bt/BtCarHfpFuzzTest.py
index ebb0d28..a081901 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarHfpFuzzTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarHfpFuzzTest.py
@@ -21,12 +21,12 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_telecom_utils
-from acts.test_utils.tel import tel_defines
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_telecom_utils
+from acts_contrib.test_utils.tel import tel_defines
STABILIZATION_DELAY_SEC = 5
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarHfpTest.py b/acts_tests/tests/google/bt/car_bt/BtCarHfpTest.py
index 2fc6260..2806feb 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarHfpTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarHfpTest.py
@@ -19,13 +19,13 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_telecom_utils
-from acts.test_utils.car import tel_telecom_utils
-from acts.test_utils.tel import tel_defines
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_telecom_utils
+from acts_contrib.test_utils.car import tel_telecom_utils
+from acts_contrib.test_utils.tel import tel_defines
BLUETOOTH_PKG_NAME = "com.android.bluetooth"
CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarMapMceTest.py b/acts_tests/tests/google/bt/car_bt/BtCarMapMceTest.py
index 93dbab8..b72ffbb 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarMapMceTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarMapMceTest.py
@@ -22,15 +22,15 @@
import acts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.tel.tel_defines import EventSmsReceived
-from acts.test_utils.tel.tel_defines import EventSmsSentSuccess
-from acts.test_utils.tel.tel_defines import EventSmsDeliverSuccess
-from acts.test_utils.tel.tel_test_utils import get_phone_number
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.tel.tel_defines import EventSmsReceived
+from acts_contrib.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts_contrib.test_utils.tel.tel_defines import EventSmsDeliverSuccess
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
EVENT_MAP_MESSAGE_RECEIVED = "MapMessageReceived"
TIMEOUT = 2000
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarMediaConnectionTest.py b/acts_tests/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
index 3f6e495..068b807 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
@@ -20,11 +20,11 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_bt_utils
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_bt_utils
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import is_a2dp_connected
class BtCarMediaConnectionTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py b/acts_tests/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
index dbb1cc9..1db9a11 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
@@ -21,11 +21,11 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.car import car_media_utils
-from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.car import car_media_utils
+from acts_contrib.test_utils.bt.bt_test_utils import is_a2dp_connected
from acts.keys import Config
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py b/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
index 3cde80f..6ec1320 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
@@ -18,9 +18,9 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.users import users
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.users import users
import time
import random
import re
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py b/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
index 705b4ec..eb2a06c 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
@@ -28,10 +28,10 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.base_test import BaseTestClass
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.bt import BtEnum
from acts import asserts
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarPairingTest.py b/acts_tests/tests/google/bt/car_bt/BtCarPairingTest.py
index 603f1a8..135c450 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarPairingTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarPairingTest.py
@@ -20,11 +20,11 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.base_test import BaseTestClass
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_bt_utils
-from acts.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_bt_utils
+from acts_contrib.test_utils.bt import BtEnum
# Timed wait between Bonding happens and Android actually gets the list of
# supported services (and subsequently updates the priorities)
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py b/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
index 6d5bccb..a4d241f 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -20,14 +20,14 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
from acts.base_test import BaseTestClass
-from acts.test_utils.bt import bt_contacts_utils
-from acts.test_utils.bt import bt_test_utils
-from acts.test_utils.car import car_bt_utils
+from acts_contrib.test_utils.bt import bt_contacts_utils
+from acts_contrib.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.car import car_bt_utils
from acts.utils import exe_cmd
-import acts.test_utils.bt.BtEnum as BtEnum
+import acts_contrib.test_utils.bt.BtEnum as BtEnum
# Offset call logs by 1 minute
CALL_LOG_TIME_OFFSET_IN_MSEC = 60000
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarToggleTest.py b/acts_tests/tests/google/bt/car_bt/BtCarToggleTest.py
index aa7f5b4..27d08e8 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarToggleTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarToggleTest.py
@@ -18,8 +18,8 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt import bt_test_utils
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt import bt_test_utils
import random
import time
diff --git a/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py b/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
index 7985540..0d0ac0c 100644
--- a/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
+++ b/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
@@ -21,22 +21,22 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_service_types
-from acts.test_utils.bt.bt_constants import gatt_transport
-from acts.test_utils.bt.bt_constants import gatt_cb_strings
-from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
-from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
-from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_characteristics
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import setup_gatt_descriptors
-from acts.test_utils.bt.bt_gatt_utils import setup_multiple_services
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_transport
+from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_characteristics
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_descriptors
+from acts_contrib.test_utils.bt.bt_gatt_utils import setup_multiple_services
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs
class GattOverBrEdrTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py b/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
index 43934a1..9d04088 100644
--- a/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
+++ b/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
@@ -30,12 +30,12 @@
from acts.asserts import assert_true
from acts.keys import Config
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
from acts.controllers.relay_lib.sony_xb2_speaker import SonyXB2Speaker
diff --git a/acts_tests/tests/google/bt/headphone_automation/SineWaveQualityTest.py b/acts_tests/tests/google/bt/headphone_automation/SineWaveQualityTest.py
index b7fef62..5688afc 100644
--- a/acts_tests/tests/google/bt/headphone_automation/SineWaveQualityTest.py
+++ b/acts_tests/tests/google/bt/headphone_automation/SineWaveQualityTest.py
@@ -1,7 +1,7 @@
from acts import asserts
from acts.signals import TestPass
-from acts.test_utils.audio_analysis_lib import audio_analysis
-from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.audio_analysis_lib import audio_analysis
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
class SineWaveQualityTest(A2dpBaseTest):
diff --git a/acts_tests/tests/google/bt/hid/HidDeviceTest.py b/acts_tests/tests/google/bt/hid/HidDeviceTest.py
index e7e1778..0466376 100644
--- a/acts_tests/tests/google/bt/hid/HidDeviceTest.py
+++ b/acts_tests/tests/google/bt/hid/HidDeviceTest.py
@@ -19,14 +19,14 @@
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts.test_utils.bt.bt_test_utils import hid_keyboard_report
-from acts.test_utils.bt.bt_test_utils import hid_device_send_key_data_report
-from acts.test_utils.bt.bt_constants import hid_connection_timeout
-from acts.test_utils.bt import bt_constants
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.bt.bt_test_utils import hid_keyboard_report
+from acts_contrib.test_utils.bt.bt_test_utils import hid_device_send_key_data_report
+from acts_contrib.test_utils.bt.bt_constants import hid_connection_timeout
+from acts_contrib.test_utils.bt import bt_constants
import time
diff --git a/acts_tests/tests/google/bt/ota/BtOtaTest.py b/acts_tests/tests/google/bt/ota/BtOtaTest.py
index 86e3098..2c8411d 100644
--- a/acts_tests/tests/google/bt/ota/BtOtaTest.py
+++ b/acts_tests/tests/google/bt/ota/BtOtaTest.py
@@ -15,8 +15,8 @@
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
from acts import signals
diff --git a/acts_tests/tests/google/bt/pan/BtPanTest.py b/acts_tests/tests/google/bt/pan/BtPanTest.py
index 01f6078..75d14b2 100644
--- a/acts_tests/tests/google/bt/pan/BtPanTest.py
+++ b/acts_tests/tests/google/bt/pan/BtPanTest.py
@@ -23,10 +23,10 @@
This device was not intended to run in a sheild box.
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts.test_utils.bt.bt_test_utils import orchestrate_and_verify_pan_connection
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_and_verify_pan_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
from queue import Empty
import time
diff --git a/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py b/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py
index e0a7962..e794b1b 100755
--- a/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py
+++ b/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py
@@ -9,13 +9,13 @@
import os
import pyvisa
import time
-import acts.test_utils.coex.audio_test_utils as atu
-import acts.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.coex.audio_test_utils as atu
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
import pandas as pd
from acts import asserts
-from acts.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory
-from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest
-from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.power.PowerBTBaseTest import ramp_attenuation
PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music'
diff --git a/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py b/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
index 55b0751..d616230 100644
--- a/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
+++ b/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
@@ -14,14 +14,14 @@
# License for the specific language governing permissions and limitations under
# the License.
-import acts.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
from acts import asserts
from acts.signals import TestPass
-from acts.test_utils.bt import bt_constants
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest
-from acts.test_utils.bt.loggers import bluetooth_metric_logger as log
-from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation
+from acts_contrib.test_utils.bt import bt_constants
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.bt.loggers import bluetooth_metric_logger as log
+from acts_contrib.test_utils.power.PowerBTBaseTest import ramp_attenuation
class BtA2dpRangeTest(A2dpBaseTest):
diff --git a/acts_tests/tests/google/bt/performance/BtCodecSweepTest.py b/acts_tests/tests/google/bt/performance/BtCodecSweepTest.py
index 7770852..e9cc90f 100644
--- a/acts_tests/tests/google/bt/performance/BtCodecSweepTest.py
+++ b/acts_tests/tests/google/bt/performance/BtCodecSweepTest.py
@@ -18,9 +18,9 @@
from acts import asserts
from acts.signals import TestPass
-from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest
-from acts.test_utils.bt import bt_constants
-from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.bt import bt_constants
+from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
DEFAULT_THDN_THRESHOLD = .1
DEFAULT_ANOMALIES_THRESHOLD = 0
diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py b/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py
index 664121d..59721ca 100644
--- a/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py
+++ b/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py
@@ -19,9 +19,9 @@
import random
import time
from acts.signals import TestFailure
-from acts.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest
-from acts.test_utils.bt.BtInterferenceBaseTest import get_iperf_results
-from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation
+from acts_contrib.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest
+from acts_contrib.test_utils.bt.BtInterferenceBaseTest import get_iperf_results
+from acts_contrib.test_utils.power.PowerBTBaseTest import ramp_attenuation
from multiprocessing import Process, Queue
DEFAULT_THDN_THRESHOLD = 0.9
diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceRSSITest.py b/acts_tests/tests/google/bt/performance/BtInterferenceRSSITest.py
index a1c40f7..4f75031 100644
--- a/acts_tests/tests/google/bt/performance/BtInterferenceRSSITest.py
+++ b/acts_tests/tests/google/bt/performance/BtInterferenceRSSITest.py
@@ -1,7 +1,7 @@
from multiprocessing import Process
import time
-from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest
+from acts_contrib.test_utils.bt.A2dpBaseTest import A2dpBaseTest
END_TOKEN = "end"
diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
index 84ca6d4..bf6b0de 100644
--- a/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
+++ b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
@@ -16,9 +16,9 @@
"""Stream music through connected device from phone across different
attenuations."""
from acts.signals import TestPass
-from acts.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest
+from acts_contrib.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest
from acts.metrics.loggers.blackbox import BlackboxMetricLogger
-from acts.test_utils.bt.BtInterferenceBaseTest import get_iperf_results
+from acts_contrib.test_utils.bt.BtInterferenceBaseTest import get_iperf_results
from multiprocessing import Process, Queue
DEFAULT_THDN_THRESHOLD = 0.9
diff --git a/acts_tests/tests/google/bt/pts/A2dpPtsTest.py b/acts_tests/tests/google/bt/pts/A2dpPtsTest.py
index 2c2ffee..84b7b1b 100644
--- a/acts_tests/tests/google/bt/pts/A2dpPtsTest.py
+++ b/acts_tests/tests/google/bt/pts/A2dpPtsTest.py
@@ -16,12 +16,12 @@
"""
A2DP PTS Tests.
"""
-from acts.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
-from acts.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
-from acts.test_utils.bt.pts.pts_base_class import PtsBaseClass
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
+from acts_contrib.test_utils.bt.pts.pts_base_class import PtsBaseClass
-import acts.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
-import acts.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
class A2dpPtsTest(PtsBaseClass):
diff --git a/acts_tests/tests/google/bt/pts/BtCmdLineTest.py b/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
index 8d925ed..16e1aa0 100644
--- a/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
+++ b/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
@@ -21,14 +21,14 @@
Optional config parameters:
'sim_conf_file' : '/path_to_config/'
"""
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from cmd_input import CmdInput
from queue import Empty
import os
import uuid
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
class BtCmdLineTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/pts/GattPtsTest.py b/acts_tests/tests/google/bt/pts/GattPtsTest.py
index 4166f8e..d910233 100644
--- a/acts_tests/tests/google/bt/pts/GattPtsTest.py
+++ b/acts_tests/tests/google/bt/pts/GattPtsTest.py
@@ -18,14 +18,14 @@
"""
from acts import signals
-from acts.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
-from acts.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
from acts.controllers.bluetooth_pts_device import VERDICT_STRINGS
-from acts.test_utils.bt.pts.pts_base_class import PtsBaseClass
+from acts_contrib.test_utils.bt.pts.pts_base_class import PtsBaseClass
-import acts.test_utils.bt.gatt_test_database as gatt_test_database
-import acts.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
-import acts.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
+import acts_contrib.test_utils.bt.gatt_test_database as gatt_test_database
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
class GattPtsTest(PtsBaseClass):
diff --git a/acts_tests/tests/google/bt/pts/SdpPtsTest.py b/acts_tests/tests/google/bt/pts/SdpPtsTest.py
index 1dd5d66..25642b8 100644
--- a/acts_tests/tests/google/bt/pts/SdpPtsTest.py
+++ b/acts_tests/tests/google/bt/pts/SdpPtsTest.py
@@ -16,14 +16,14 @@
"""
SDP PTS Tests.
"""
-from acts.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
-from acts.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
-from acts.test_utils.bt.bt_constants import bt_attribute_values
-from acts.test_utils.bt.bt_constants import sig_uuid_constants
-from acts.test_utils.bt.pts.pts_base_class import PtsBaseClass
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
+from acts_contrib.test_utils.bt.bt_constants import bt_attribute_values
+from acts_contrib.test_utils.bt.bt_constants import sig_uuid_constants
+from acts_contrib.test_utils.bt.pts.pts_base_class import PtsBaseClass
-import acts.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
-import acts.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ics_lib as f_ics_lib
+import acts_contrib.test_utils.bt.pts.fuchsia_pts_ixit_lib as f_ixit_lib
# SDP_RECORD Definition is WIP
SDP_RECORD = {
diff --git a/acts_tests/tests/google/bt/pts/cmd_input.py b/acts_tests/tests/google/bt/pts/cmd_input.py
index 4037efc..2db77a7 100644
--- a/acts_tests/tests/google/bt/pts/cmd_input.py
+++ b/acts_tests/tests/google/bt/pts/cmd_input.py
@@ -16,10 +16,10 @@
"""
Python script for wrappers to various libraries.
"""
-from acts.test_utils.bt.bt_constants import bt_scan_mode_types
-from acts.test_utils.bt.bt_constants import gatt_server_responses
-import acts.test_utils.bt.gatt_test_database as gatt_test_database
-from acts.test_utils.bt.bt_carkit_lib import E2eBtCarkitLib
+from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_server_responses
+import acts_contrib.test_utils.bt.gatt_test_database as gatt_test_database
+from acts_contrib.test_utils.bt.bt_carkit_lib import E2eBtCarkitLib
import threading
import time
diff --git a/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py b/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py
index 490cb30..cf9ea2b 100644
--- a/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py
+++ b/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py
@@ -18,13 +18,13 @@
import time
from acts import utils
-import acts.test_utils.bt.bt_test_utils as bt_utils
-import acts.test_utils.wifi.wifi_performance_test_utils as wifi_utils
-from acts.test_utils.bt.ble_performance_test_utils import ble_gatt_disconnection
-from acts.test_utils.bt.ble_performance_test_utils import ble_coc_connection
-from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest
+import acts_contrib.test_utils.bt.bt_test_utils as bt_utils
+import acts_contrib.test_utils.wifi.wifi_performance_test_utils as wifi_utils
+from acts_contrib.test_utils.bt.ble_performance_test_utils import ble_gatt_disconnection
+from acts_contrib.test_utils.bt.ble_performance_test_utils import ble_coc_connection
+from acts_contrib.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.BtSarBaseTest import BtSarBaseTest
FIXED_ATTENUATION = 36
diff --git a/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py b/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py
index 74a1000..5b75606 100644
--- a/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py
+++ b/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py
@@ -16,10 +16,10 @@
import os
import time
-import acts.test_utils.bt.bt_test_utils as bt_utils
-from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest
-from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation
-import acts.test_utils.wifi.wifi_performance_test_utils as wifi_utils
+import acts_contrib.test_utils.bt.bt_test_utils as bt_utils
+from acts_contrib.test_utils.bt.BtSarBaseTest import BtSarBaseTest
+from acts_contrib.test_utils.power.PowerBTBaseTest import ramp_attenuation
+import acts_contrib.test_utils.wifi.wifi_performance_test_utils as wifi_utils
SLEEP_DURATION = 2
diff --git a/acts_tests/tests/google/bt/sar/BtSarSanityTest.py b/acts_tests/tests/google/bt/sar/BtSarSanityTest.py
index 7ef8840..08a4369 100644
--- a/acts_tests/tests/google/bt/sar/BtSarSanityTest.py
+++ b/acts_tests/tests/google/bt/sar/BtSarSanityTest.py
@@ -19,8 +19,8 @@
import time
from acts import asserts
-import acts.test_utils.bt.bt_test_utils as bt_utils
-from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest
+import acts_contrib.test_utils.bt.bt_test_utils as bt_utils
+from acts_contrib.test_utils.bt.BtSarBaseTest import BtSarBaseTest
class BtSarSanityTest(BtSarBaseTest):
diff --git a/acts_tests/tests/google/bt/sar/BtSarTpcTest.py b/acts_tests/tests/google/bt/sar/BtSarTpcTest.py
index cf29d68..770ebf3 100644
--- a/acts_tests/tests/google/bt/sar/BtSarTpcTest.py
+++ b/acts_tests/tests/google/bt/sar/BtSarTpcTest.py
@@ -17,13 +17,13 @@
import os
import time
import numpy as np
-import acts.test_utils.bt.bt_test_utils as bt_utils
+import acts_contrib.test_utils.bt.bt_test_utils as bt_utils
from acts.metrics.loggers.blackbox import BlackboxMetricLogger
-import acts.test_utils.wifi.wifi_performance_test_utils as wifi_utils
+import acts_contrib.test_utils.wifi.wifi_performance_test_utils as wifi_utils
from acts import asserts
from functools import partial
-from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest
+from acts_contrib.test_utils.bt.BtSarBaseTest import BtSarBaseTest
class BtSarTpcTest(BtSarBaseTest):
diff --git a/acts_tests/tests/google/bt/sdp/SdpSetupTest.py b/acts_tests/tests/google/bt/sdp/SdpSetupTest.py
index 0725825..6a9409a 100644
--- a/acts_tests/tests/google/bt/sdp/SdpSetupTest.py
+++ b/acts_tests/tests/google/bt/sdp/SdpSetupTest.py
@@ -22,12 +22,12 @@
"""
from acts import signals
from acts.base_test import BaseTestClass
-from acts.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
-from acts.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
-from acts.test_utils.abstract_devices.bluetooth_device import create_bluetooth_device
-from acts.test_utils.bt.bt_constants import bt_attribute_values
-from acts.test_utils.bt.bt_constants import sig_uuid_constants
-from acts.test_utils.fuchsia.sdp_records import sdp_pts_record_list
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import AndroidBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import FuchsiaBluetoothDevice
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import create_bluetooth_device
+from acts_contrib.test_utils.bt.bt_constants import bt_attribute_values
+from acts_contrib.test_utils.bt.bt_constants import sig_uuid_constants
+from acts_contrib.test_utils.fuchsia.sdp_records import sdp_pts_record_list
class SdpSetupTest(BaseTestClass):
diff --git a/acts_tests/tests/google/bt/system_tests/BtStressTest.py b/acts_tests/tests/google/bt/system_tests/BtStressTest.py
index 0473aa0..107fdac 100644
--- a/acts_tests/tests/google/bt/system_tests/BtStressTest.py
+++ b/acts_tests/tests/google/bt/system_tests/BtStressTest.py
@@ -20,13 +20,13 @@
import time
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import bluetooth_off
-from acts.test_utils.bt.bt_constants import bluetooth_on
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_off
+from acts_contrib.test_utils.bt.bt_constants import bluetooth_on
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
class BtStressTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py b/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
index d1d4fe5..356ac8d 100644
--- a/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
+++ b/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
@@ -23,10 +23,10 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
class RfcommLongevityTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py b/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
index 3fac543..ea9d999 100644
--- a/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
+++ b/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
@@ -23,10 +23,10 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
class RfcommStressTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/test_tools/BtReconnectTest.py b/acts_tests/tests/google/bt/test_tools/BtReconnectTest.py
index a56264b..a2f7f98 100644
--- a/acts_tests/tests/google/bt/test_tools/BtReconnectTest.py
+++ b/acts_tests/tests/google/bt/test_tools/BtReconnectTest.py
@@ -16,8 +16,8 @@
import time
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import *
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import *
class BtReconnectTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/test_tools/EnergyTest.py b/acts_tests/tests/google/bt/test_tools/EnergyTest.py
index a9afc86..7797f8a 100644
--- a/acts_tests/tests/google/bt/test_tools/EnergyTest.py
+++ b/acts_tests/tests/google/bt/test_tools/EnergyTest.py
@@ -18,7 +18,7 @@
"""
from queue import Empty
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
class EnergyTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/bt/test_tools/ToolsTest.py b/acts_tests/tests/google/bt/test_tools/ToolsTest.py
index eabf5fc..5bffd6f 100644
--- a/acts_tests/tests/google/bt/test_tools/ToolsTest.py
+++ b/acts_tests/tests/google/bt/test_tools/ToolsTest.py
@@ -14,8 +14,8 @@
# License for the specific language governing permissions and limitations under
# the License.
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import *
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import *
import time
import pprint
diff --git a/acts_tests/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py b/acts_tests/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py
index 38db103..1775b9e 100644
--- a/acts_tests/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py
+++ b/acts_tests/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py
@@ -21,11 +21,11 @@
One Android device.
"""
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import multithread_func
-from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
-from acts.test_utils.coex.coex_test_utils import toggle_bluetooth
-from acts.test_utils.coex.coex_test_utils import start_fping
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import multithread_func
+from acts_contrib.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts_contrib.test_utils.coex.coex_test_utils import toggle_bluetooth
+from acts_contrib.test_utils.coex.coex_test_utils import start_fping
class CoexBasicFunctionalityTest(CoexBaseTest):
diff --git a/acts_tests/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py b/acts_tests/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py
index 53dc7fa..1acfc0c 100644
--- a/acts_tests/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py
+++ b/acts_tests/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py
@@ -21,15 +21,15 @@
Two Android device.
One A2DP and HFP Headset connected to Relay.
"""
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import connect_ble
-from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
-from acts.test_utils.coex.coex_test_utils import multithread_func
-from acts.test_utils.coex.coex_test_utils import music_play_and_check_via_app
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.coex.coex_test_utils import setup_tel_config
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import connect_ble
+from acts_contrib.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts_contrib.test_utils.coex.coex_test_utils import multithread_func
+from acts_contrib.test_utils.coex.coex_test_utils import music_play_and_check_via_app
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.coex.coex_test_utils import setup_tel_config
class CoexBtMultiProfileFunctionalityTest(CoexBaseTest):
diff --git a/acts_tests/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py b/acts_tests/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py
index f03ade0..f6aadc3 100644
--- a/acts_tests/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py
+++ b/acts_tests/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py
@@ -14,21 +14,21 @@
# License for the specific language governing permissions and limitations under
# the License.
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
-from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
-from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
-from acts.test_utils.coex.coex_test_utils import initiate_disconnect_call_dut
-from acts.test_utils.coex.coex_test_utils import multithread_func
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
-from acts.test_utils.coex.coex_test_utils import toggle_screen_state
-from acts.test_utils.coex.coex_test_utils import setup_tel_config
-from acts.test_utils.coex.coex_test_utils import start_fping
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts_contrib.test_utils.coex.coex_test_utils import connect_wlan_profile
+from acts_contrib.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts_contrib.test_utils.coex.coex_test_utils import initiate_disconnect_call_dut
+from acts_contrib.test_utils.coex.coex_test_utils import multithread_func
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts_contrib.test_utils.coex.coex_test_utils import toggle_screen_state
+from acts_contrib.test_utils.coex.coex_test_utils import setup_tel_config
+from acts_contrib.test_utils.coex.coex_test_utils import start_fping
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
BLUETOOTH_WAIT_TIME = 2
diff --git a/acts_tests/tests/google/coex/hotspot_tests/HotspotWiFiChannelTest.py b/acts_tests/tests/google/coex/hotspot_tests/HotspotWiFiChannelTest.py
index b276a3a..2a26d88 100644
--- a/acts_tests/tests/google/coex/hotspot_tests/HotspotWiFiChannelTest.py
+++ b/acts_tests/tests/google/coex/hotspot_tests/HotspotWiFiChannelTest.py
@@ -25,16 +25,16 @@
import acts.base_test
import acts.controllers.rohdeschwarz_lib.cmw500 as cmw500
-from acts.test_utils.coex.hotspot_utils import band_channel_map
-from acts.test_utils.coex.hotspot_utils import supported_lte_bands
-from acts.test_utils.coex.hotspot_utils import tdd_band_list
-from acts.test_utils.coex.hotspot_utils import wifi_channel_map
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.wifi.wifi_test_utils import reset_wifi
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_tethering
-from acts.test_utils.wifi.wifi_test_utils import stop_wifi_tethering
-from acts.test_utils.wifi.wifi_test_utils import wifi_connect
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
+from acts_contrib.test_utils.coex.hotspot_utils import band_channel_map
+from acts_contrib.test_utils.coex.hotspot_utils import supported_lte_bands
+from acts_contrib.test_utils.coex.hotspot_utils import tdd_band_list
+from acts_contrib.test_utils.coex.hotspot_utils import wifi_channel_map
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.wifi.wifi_test_utils import reset_wifi
+from acts_contrib.test_utils.wifi.wifi_test_utils import start_wifi_tethering
+from acts_contrib.test_utils.wifi.wifi_test_utils import stop_wifi_tethering
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts_contrib.test_utils.wifi.wifi_test_utils import WifiEnums
BANDWIDTH_2G = 20
CNSS_LOG_PATH = '/data/vendor/wifi/wlan_logs'
diff --git a/acts_tests/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
index c5c1879..76d7151 100644
--- a/acts_tests/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
@@ -16,9 +16,9 @@
import itertools
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
-from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import perform_classic_discovery
class CoexBasicPerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py
index af75373..de36052 100644
--- a/acts_tests/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py
@@ -24,18 +24,18 @@
"""
import time
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.car.tel_telecom_utils import wait_for_dialing
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
-from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
-from acts.test_utils.coex.coex_test_utils import music_play_and_check_via_app
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.coex.coex_test_utils import setup_tel_config
-from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.car.tel_telecom_utils import wait_for_dialing
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts_contrib.test_utils.coex.coex_test_utils import music_play_and_check_via_app
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.coex.coex_test_utils import setup_tel_config
+from acts_contrib.test_utils.coex.coex_test_utils import connect_wlan_profile
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
class CoexBtMultiProfilePerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py
index 8edd0c5..4aea4e2 100644
--- a/acts_tests/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py
@@ -16,8 +16,8 @@
import itertools
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
class WlanStandalonePerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py
index e06b4c6..01e177f 100644
--- a/acts_tests/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py
@@ -16,15 +16,15 @@
import itertools
-from acts.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bf
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
-from acts.test_utils.coex.coex_test_utils import avrcp_actions
-from acts.test_utils.coex.coex_test_utils import music_play_and_check
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
-from acts.test_utils.coex.coex_test_utils import push_music_to_android_device
+from acts_contrib.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bf
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import avrcp_actions
+from acts_contrib.test_utils.coex.coex_test_utils import music_play_and_check
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts_contrib.test_utils.coex.coex_test_utils import push_music_to_android_device
class WlanWithA2dpPerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py
index 23d7423..e3179df 100644
--- a/acts_tests/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py
@@ -17,12 +17,12 @@
import itertools
import time
-from acts.test_utils.bt.bt_gatt_utils import close_gatt_client
-from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
-from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
-from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
-from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.bt.bt_gatt_utils import close_gatt_client
+from acts_contrib.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts_contrib.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts_contrib.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
class WlanWithBlePerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py b/acts_tests/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py
index bec999e..b56cbde 100644
--- a/acts_tests/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py
+++ b/acts_tests/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py
@@ -16,14 +16,14 @@
import time
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
-from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.coex.coex_test_utils import setup_tel_config
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.coex.coex_test_utils import setup_tel_config
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
class WlanWithHfpPerformanceTest(CoexPerformanceBaseTest):
diff --git a/acts_tests/tests/google/coex/stress_tests/CoexBasicStressTest.py b/acts_tests/tests/google/coex/stress_tests/CoexBasicStressTest.py
index 1bd7b06..60881de 100644
--- a/acts_tests/tests/google/coex/stress_tests/CoexBasicStressTest.py
+++ b/acts_tests/tests/google/coex/stress_tests/CoexBasicStressTest.py
@@ -22,9 +22,9 @@
"""
import time
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import toggle_bluetooth
-from acts.test_utils.coex.coex_test_utils import device_discoverable
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import toggle_bluetooth
+from acts_contrib.test_utils.coex.coex_test_utils import device_discoverable
class CoexBasicStressTest(CoexBaseTest):
diff --git a/acts_tests/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py b/acts_tests/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py
index 5516bec..a709dda 100644
--- a/acts_tests/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py
+++ b/acts_tests/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py
@@ -22,11 +22,11 @@
"""
import time
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
class CoexBtMultiProfileStressTest(CoexBaseTest):
diff --git a/acts_tests/tests/google/coex/stress_tests/CoexHfpStressTest.py b/acts_tests/tests/google/coex/stress_tests/CoexHfpStressTest.py
index b210d59..8add009 100644
--- a/acts_tests/tests/google/coex/stress_tests/CoexHfpStressTest.py
+++ b/acts_tests/tests/google/coex/stress_tests/CoexHfpStressTest.py
@@ -16,18 +16,18 @@
import time
-from acts.test_utils.bt import BtEnum
-from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
-from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
-from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
-from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
-from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
-from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_BLUETOOTH
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts_contrib.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts_contrib.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts_contrib.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts_contrib.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts_contrib.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts_contrib.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_BLUETOOTH
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_voice_utils import set_audio_route
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
class CoexHfpStressTest(CoexBaseTest):
diff --git a/acts_tests/tests/google/experimental/BluetoothLatencyTest.py b/acts_tests/tests/google/experimental/BluetoothLatencyTest.py
index ea6ed4a..6ccc949 100644
--- a/acts_tests/tests/google/experimental/BluetoothLatencyTest.py
+++ b/acts_tests/tests/google/experimental/BluetoothLatencyTest.py
@@ -22,13 +22,13 @@
from acts.base_test import BaseTestClass
from acts.signals import TestPass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
-from acts.test_utils.bt.bt_test_utils import write_read_verify_data
-from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
-from acts.test_utils.bt.loggers.protos import bluetooth_metric_pb2 as proto_module
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
+from acts_contrib.test_utils.bt.loggers.protos import bluetooth_metric_pb2 as proto_module
from acts.utils import set_location_service
diff --git a/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py b/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py
index 2402fb5..2040af5 100644
--- a/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py
+++ b/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py
@@ -28,11 +28,11 @@
from acts.signals import TestFailure
from acts.signals import TestPass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import factory_reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import factory_reset_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
from acts.utils import set_location_service
diff --git a/acts_tests/tests/google/experimental/BluetoothReconnectTest.py b/acts_tests/tests/google/experimental/BluetoothReconnectTest.py
index 5339e51..dc66486 100644
--- a/acts_tests/tests/google/experimental/BluetoothReconnectTest.py
+++ b/acts_tests/tests/google/experimental/BluetoothReconnectTest.py
@@ -25,10 +25,10 @@
from acts.signals import TestFailure
from acts.signals import TestPass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
from acts.utils import set_location_service
# The number of reconnections to be attempted during the test
diff --git a/acts_tests/tests/google/experimental/BluetoothThroughputTest.py b/acts_tests/tests/google/experimental/BluetoothThroughputTest.py
index 1f53172..f1d8dba 100644
--- a/acts_tests/tests/google/experimental/BluetoothThroughputTest.py
+++ b/acts_tests/tests/google/experimental/BluetoothThroughputTest.py
@@ -19,12 +19,12 @@
from acts.base_test import BaseTestClass
from acts.signals import TestPass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
-from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
-from acts.test_utils.bt.loggers.protos import bluetooth_metric_pb2 as proto_module
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
+from acts_contrib.test_utils.bt.loggers.protos import bluetooth_metric_pb2 as proto_module
from acts.utils import set_location_service
diff --git a/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py b/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py
index 9d15781..9cc6c0c 100644
--- a/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py
+++ b/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py
@@ -21,7 +21,7 @@
from acts import asserts, signals
from acts.base_test import BaseTestClass
from acts.libs.proc.job import Error
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS = 2
diff --git a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
index e799991..636db87 100644
--- a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
@@ -22,14 +22,14 @@
import time
from acts.controllers import android_device
-from acts.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
-from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt.bt_constants import scan_result
-from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import adv_succ
+from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts_contrib.test_utils.bt.bt_constants import scan_result
+from acts_contrib.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth
class BleFuchsiaAndroidTest(BluetoothBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
index e0efc77..a74db66 100644
--- a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
@@ -21,7 +21,7 @@
import time
from acts.base_test import BaseTestClass
-from acts.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
+from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
class BleFuchsiaTest(BaseTestClass):
diff --git a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
index e368127..2ce6b54 100644
--- a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
@@ -26,7 +26,7 @@
from acts import signals
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_test_utils import generate_id_by_size
+from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
class FuchsiaBtMacAddressTest(BaseTestClass):
diff --git a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
index 72103e9..074d3e5 100644
--- a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
@@ -26,7 +26,7 @@
from acts import signals
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_test_utils import generate_id_by_size
+from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
class FuchsiaBtScanTest(BaseTestClass):
diff --git a/acts_tests/tests/google/fuchsia/bt/command_input.py b/acts_tests/tests/google/fuchsia/bt/command_input.py
index 872a896..75d904c 100644
--- a/acts_tests/tests/google/fuchsia/bt/command_input.py
+++ b/acts_tests/tests/google/fuchsia/bt/command_input.py
@@ -43,13 +43,13 @@
"""
-from acts.test_utils.abstract_devices.bluetooth_device import create_bluetooth_device
-from acts.test_utils.bt.bt_constants import bt_attribute_values
-from acts.test_utils.bt.bt_constants import sig_appearance_constants
-from acts.test_utils.bt.bt_constants import sig_uuid_constants
-from acts.test_utils.fuchsia.sdp_records import sdp_pts_record_list
+from acts_contrib.test_utils.abstract_devices.bluetooth_device import create_bluetooth_device
+from acts_contrib.test_utils.bt.bt_constants import bt_attribute_values
+from acts_contrib.test_utils.bt.bt_constants import sig_appearance_constants
+from acts_contrib.test_utils.bt.bt_constants import sig_uuid_constants
+from acts_contrib.test_utils.fuchsia.sdp_records import sdp_pts_record_list
-import acts.test_utils.bt.gatt_test_database as gatt_test_database
+import acts_contrib.test_utils.bt.gatt_test_database as gatt_test_database
import cmd
import pprint
@@ -1391,7 +1391,7 @@
Supports Tab Autocomplete.
Input(s):
descriptor_db_name: The descriptor db name that matches one in
- acts.test_utils.bt.gatt_test_database
+ acts_contrib.test_utils.bt.gatt_test_database
Usage:
Examples:
gatts_setup_database LARGE_DB_1
diff --git a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
index 2f2e666..da7d641 100644
--- a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
@@ -30,8 +30,8 @@
from acts import signals
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_test_utils import generate_id_by_size
-from acts.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
+from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
+from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
import time
diff --git a/acts_tests/tests/google/fuchsia/bt/gatt/gatt_server_databases.py b/acts_tests/tests/google/fuchsia/bt/gatt/gatt_server_databases.py
index c554405..c854281 100644
--- a/acts_tests/tests/google/fuchsia/bt/gatt/gatt_server_databases.py
+++ b/acts_tests/tests/google/fuchsia/bt/gatt/gatt_server_databases.py
@@ -18,12 +18,12 @@
GATT server dictionaries which will be setup in various tests.
"""
-from acts.test_utils.bt.bt_constants import gatt_characteristic
-from acts.test_utils.bt.bt_constants import gatt_descriptor
-from acts.test_utils.bt.bt_constants import gatt_service_types
-from acts.test_utils.bt.bt_constants import gatt_char_types
-from acts.test_utils.bt.bt_constants import gatt_characteristic_value_format
-from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic
+from acts_contrib.test_utils.bt.bt_constants import gatt_descriptor
+from acts_contrib.test_utils.bt.bt_constants import gatt_service_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_types
+from acts_contrib.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
SINGLE_PRIMARY_SERVICE = {
diff --git a/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py b/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
index d2116a9..d7491dd 100644
--- a/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
+++ b/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
@@ -24,7 +24,7 @@
import uuid
from acts import signals
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
class Sl4fSanityTest(BaseTestClass):
diff --git a/acts_tests/tests/google/fuchsia/netstack/NetstackIxiaTest.py b/acts_tests/tests/google/fuchsia/netstack/NetstackIxiaTest.py
index 81d69bf..129d28f 100644
--- a/acts_tests/tests/google/fuchsia/netstack/NetstackIxiaTest.py
+++ b/acts_tests/tests/google/fuchsia/netstack/NetstackIxiaTest.py
@@ -20,7 +20,7 @@
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.net.NetstackBaseTest import NetstackBaseTest
+from acts_contrib.test_utils.net.NetstackBaseTest import NetstackBaseTest
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/fuchsia/ram/RamTest.py b/acts_tests/tests/google/fuchsia/ram/RamTest.py
index b9bd75c..2b11e01 100644
--- a/acts_tests/tests/google/fuchsia/ram/RamTest.py
+++ b/acts_tests/tests/google/fuchsia/ram/RamTest.py
@@ -21,7 +21,7 @@
from acts import asserts, signals
from acts.base_test import BaseTestClass
from acts.libs.proc.job import Error
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
# Some number bigger than the minimum 512k:
MEMORY_CYCLES_TO_MEASURE = 1024 * 1024
diff --git a/acts_tests/tests/google/fuchsia/wlan/BeaconLossTest.py b/acts_tests/tests/google/fuchsia/wlan/BeaconLossTest.py
index 2944707..00e61b3 100644
--- a/acts_tests/tests/google/fuchsia/wlan/BeaconLossTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/BeaconLossTest.py
@@ -31,11 +31,11 @@
from acts import utils
from acts.base_test import BaseTestClass
from acts.controllers.ap_lib import hostapd_constants
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import associate
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import associate
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py b/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py
index 431a2a3..57ecbb0 100644
--- a/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py
@@ -33,9 +33,9 @@
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib.hostapd_security import Security
from acts.controllers.iperf_server import IPerfResult
-from acts.test_utils.abstract_devices.utils_lib import wlan_utils
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib import wlan_utils
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
N_CAPABILITIES_DEFAULT = [
hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
diff --git a/acts_tests/tests/google/fuchsia/wlan/ConnectionStressTest.py b/acts_tests/tests/google/fuchsia/wlan/ConnectionStressTest.py
index 836846e..7ad2fdb 100644
--- a/acts_tests/tests/google/fuchsia/wlan/ConnectionStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/ConnectionStressTest.py
@@ -26,13 +26,13 @@
from acts import signals
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import associate
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.fuchsia import utils
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import associate
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.fuchsia import utils
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/fuchsia/wlan/DownloadStressTest.py b/acts_tests/tests/google/fuchsia/wlan/DownloadStressTest.py
index f3f3803..5fa4fbb 100644
--- a/acts_tests/tests/google/fuchsia/wlan/DownloadStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/DownloadStressTest.py
@@ -24,10 +24,10 @@
from acts.base_test import BaseTestClass
from acts import signals
from acts.controllers.ap_lib import hostapd_constants
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.fuchsia import utils
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.fuchsia import utils
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py b/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py
index 8577f49..ef860a9 100644
--- a/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py
@@ -25,10 +25,10 @@
from acts import signals
from acts.controllers.ap_lib import hostapd_constants
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.tel.tel_test_utils import setup_droid_properties
-from acts.test_utils.fuchsia import utils
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts_contrib.test_utils.fuchsia import utils
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py b/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py
index 0f2e697..c364481 100644
--- a/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py
@@ -26,9 +26,9 @@
from acts.controllers import iperf_client
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.abstract_devices.utils_lib import wlan_utils
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib import wlan_utils
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
ANDROID_DEFAULT_WLAN_INTERFACE = 'wlan0'
CONNECTIVITY_MODE_LOCAL = 'local_only'
diff --git a/acts_tests/tests/google/fuchsia/wlan/VapeInteropTest.py b/acts_tests/tests/google/fuchsia/wlan/VapeInteropTest.py
index e9e216c..dd6374d 100644
--- a/acts_tests/tests/google/fuchsia/wlan/VapeInteropTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/VapeInteropTest.py
@@ -18,10 +18,10 @@
from acts.controllers.ap_lib import hostapd_ap_preset
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib.hostapd_security import Security
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class VapeInteropTest(AbstractDeviceWlanDeviceBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py
index d470bf7..4994dd2 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py
@@ -17,8 +17,8 @@
from acts import signals
from acts.base_test import BaseTestClass
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
class WlanInterfaceTest(AbstractDeviceWlanDeviceBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11ACTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11ACTest.py
index fde4e6d..72dcfe6 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11ACTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11ACTest.py
@@ -21,10 +21,10 @@
from acts.controllers.ap_lib.hostapd_security import Security
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_config
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
# AC Capabilities
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
index f8cc874..f538242 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
@@ -21,10 +21,10 @@
from acts.controllers.ap_lib.hostapd_security import Security
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_config
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
FREQUENCY_24 = ['2.4GHz']
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanPhyComplianceABGTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanPhyComplianceABGTest.py
index 8ef8591..df9931b 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanPhyComplianceABGTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanPhyComplianceABGTest.py
@@ -18,11 +18,11 @@
from acts.controllers.ap_lib import hostapd_ap_preset
from acts.controllers.ap_lib import hostapd_constants
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
-from acts.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class WlanPhyComplianceABGTest(AbstractDeviceWlanDeviceBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py
index 7c8bd21..d880924 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py
@@ -30,9 +30,9 @@
from acts.controllers.ap_lib.radvd import Radvd
from acts.controllers.ap_lib import radvd_constants
from acts.controllers.ap_lib.radvd_config import RadvdConfig
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.utils_lib import wlan_utils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.utils_lib import wlan_utils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
# Constants, for readibility
AP = 'ap'
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py
index 87bf1a7..4eb07fc 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py
@@ -24,11 +24,11 @@
from acts.controllers.ap_lib.hostapd_security import Security
from acts.controllers.attenuator import get_attenuators_for_device
from acts.controllers.iperf_server import IPerfResult
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import associate
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import associate
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
from bokeh.plotting import ColumnDataSource
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanScanTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanScanTest.py
index 012e086..eca08cb 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanScanTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanScanTest.py
@@ -25,14 +25,14 @@
import time
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import signals
from acts.controllers.ap_lib import hostapd_ap_preset
from acts.controllers.ap_lib import hostapd_bss_settings
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class WlanScanTest(WifiBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanSecurityComplianceABGTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanSecurityComplianceABGTest.py
index 2f43085..79878fc 100644
--- a/acts_tests/tests/google/fuchsia/wlan/WlanSecurityComplianceABGTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/WlanSecurityComplianceABGTest.py
@@ -19,11 +19,11 @@
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib.hostapd_security import Security
-from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
-from acts.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
from acts.utils import rand_hex_str
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
index 670aec0..ad911ca 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
@@ -16,9 +16,9 @@
from acts import signals
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.utils_lib.wlan_policy_utils import reboot_device, restore_state, save_network, setup_policy_tests
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_policy_utils import reboot_device, restore_state, save_network, setup_policy_tests
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
import time
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
index a1c0d7b..beb3fc2 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
@@ -21,7 +21,7 @@
from acts import signals, utils
from acts.controllers.ap_lib import (hostapd_ap_preset, hostapd_bss_settings,
hostapd_constants, hostapd_security)
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class PolicyScanTest(WifiBaseTest):
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
index e16c4f6..1a8bc42 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
@@ -22,9 +22,9 @@
from acts.controllers.ap_lib import hostapd_ap_preset
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.utils_lib.wlan_policy_utils import reboot_device, restore_state, save_network, setup_policy_tests
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_policy_utils import reboot_device, restore_state, save_network, setup_policy_tests
from acts.utils import rand_ascii_str, rand_hex_str, timeout
import requests
import time
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
index 7156a80..5ca70f4 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
@@ -17,9 +17,9 @@
from acts import signals
from acts.controllers.ap_lib import hostapd_constants
from acts.controllers.ap_lib import hostapd_security
-from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
-from acts.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state, save_network, start_connections, stop_connections
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
+from acts_contrib.test_utils.abstract_devices.utils_lib.wlan_policy_utils import setup_policy_tests, restore_state, save_network, start_connections, stop_connections
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import rand_ascii_str
DISCONNECTED = "Disconnected"
diff --git a/acts_tests/tests/google/fugu/AndroidFuguRemotePairingTest.py b/acts_tests/tests/google/fugu/AndroidFuguRemotePairingTest.py
index a41f9fc..78b05d4 100644
--- a/acts_tests/tests/google/fugu/AndroidFuguRemotePairingTest.py
+++ b/acts_tests/tests/google/fugu/AndroidFuguRemotePairingTest.py
@@ -19,7 +19,7 @@
import time
from acts.controllers.relay_lib.relay import SynchronizeRelays
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
class AndroidFuguRemotePairingTest(BluetoothBaseTest):
def setup_class(self):
diff --git a/acts_tests/tests/google/gnss/AGNSSPerformanceTest.py b/acts_tests/tests/google/gnss/AGNSSPerformanceTest.py
index 57d4c35..803e735 100644
--- a/acts_tests/tests/google/gnss/AGNSSPerformanceTest.py
+++ b/acts_tests/tests/google/gnss/AGNSSPerformanceTest.py
@@ -19,7 +19,7 @@
from acts import base_test
from acts import asserts
from acts.controllers.rohdeschwarz_lib import contest
-from acts.test_utils.tel import tel_test_utils
+from acts_contrib.test_utils.tel import tel_test_utils
from acts.metrics.loggers import blackbox
import json
diff --git a/acts_tests/tests/google/gnss/FlpTtffTest.py b/acts_tests/tests/google/gnss/FlpTtffTest.py
index 6d73b99..59b19b5 100644
--- a/acts_tests/tests/google/gnss/FlpTtffTest.py
+++ b/acts_tests/tests/google/gnss/FlpTtffTest.py
@@ -20,29 +20,29 @@
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
from acts.utils import get_current_epoch_time
-from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import start_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import stop_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.gnss.gnss_test_utils import get_baseband_and_gms_version
-from acts.test_utils.gnss.gnss_test_utils import _init_device
-from acts.test_utils.gnss.gnss_test_utils import check_location_service
-from acts.test_utils.gnss.gnss_test_utils import clear_logd_gnss_qxdm_log
-from acts.test_utils.gnss.gnss_test_utils import set_mobile_data
-from acts.test_utils.gnss.gnss_test_utils import get_gnss_qxdm_log
-from acts.test_utils.gnss.gnss_test_utils import set_wifi_and_bt_scanning
-from acts.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import start_ttff_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import check_ttff_data
-from acts.test_utils.gnss.gnss_test_utils import set_attenuator_gnss_signal
-from acts.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
-from acts.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
-from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import get_tcpdump_log
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import stop_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.gnss.gnss_test_utils import get_baseband_and_gms_version
+from acts_contrib.test_utils.gnss.gnss_test_utils import _init_device
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_location_service
+from acts_contrib.test_utils.gnss.gnss_test_utils import clear_logd_gnss_qxdm_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_mobile_data
+from acts_contrib.test_utils.gnss.gnss_test_utils import get_gnss_qxdm_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_wifi_and_bt_scanning
+from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import start_ttff_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_ttff_data
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_attenuator_gnss_signal
+from acts_contrib.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
+from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
+from acts_contrib.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_test_utils import stop_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_test_utils import get_tcpdump_log
class FlpTtffTest(BaseTestClass):
diff --git a/acts_tests/tests/google/gnss/GnssFunctionTest.py b/acts_tests/tests/google/gnss/GnssFunctionTest.py
index b1de836..7bc8f44 100644
--- a/acts_tests/tests/google/gnss/GnssFunctionTest.py
+++ b/acts_tests/tests/google/gnss/GnssFunctionTest.py
@@ -24,58 +24,58 @@
from acts import signals
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.tel import tel_test_utils as tutils
-from acts.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_test_utils as tutils
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
from acts.utils import get_current_epoch_time
from acts.utils import unzip_maintain_permissions
from acts.utils import force_airplane_mode
-from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import flash_radio
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import stop_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import check_call_state_connected_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import http_file_download_by_sl4a
-from acts.test_utils.tel.tel_test_utils import start_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash
-from acts.test_utils.gnss.gnss_test_utils import get_baseband_and_gms_version
-from acts.test_utils.gnss.gnss_test_utils import set_attenuator_gnss_signal
-from acts.test_utils.gnss.gnss_test_utils import _init_device
-from acts.test_utils.gnss.gnss_test_utils import check_location_service
-from acts.test_utils.gnss.gnss_test_utils import clear_logd_gnss_qxdm_log
-from acts.test_utils.gnss.gnss_test_utils import set_mobile_data
-from acts.test_utils.gnss.gnss_test_utils import set_wifi_and_bt_scanning
-from acts.test_utils.gnss.gnss_test_utils import get_gnss_qxdm_log
-from acts.test_utils.gnss.gnss_test_utils import remount_device
-from acts.test_utils.gnss.gnss_test_utils import reboot
-from acts.test_utils.gnss.gnss_test_utils import check_network_location
-from acts.test_utils.gnss.gnss_test_utils import launch_google_map
-from acts.test_utils.gnss.gnss_test_utils import check_location_api
-from acts.test_utils.gnss.gnss_test_utils import set_battery_saver_mode
-from acts.test_utils.gnss.gnss_test_utils import kill_xtra_daemon
-from acts.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import start_ttff_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import check_ttff_data
-from acts.test_utils.gnss.gnss_test_utils import start_youtube_video
-from acts.test_utils.gnss.gnss_test_utils import fastboot_factory_reset
-from acts.test_utils.gnss.gnss_test_utils import gnss_trigger_modem_ssr_by_adb
-from acts.test_utils.gnss.gnss_test_utils import gnss_trigger_modem_ssr_by_mds
-from acts.test_utils.gnss.gnss_test_utils import disable_supl_mode
-from acts.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
-from acts.test_utils.gnss.gnss_test_utils import check_xtra_download
-from acts.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
-from acts.test_utils.gnss.gnss_test_utils import enable_supl_mode
-from acts.test_utils.gnss.gnss_test_utils import start_toggle_gnss_by_gtw_gpstool
-from acts.test_utils.gnss.gnss_test_utils import grant_location_permission
-from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import get_tcpdump_log
+from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import flash_radio
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.tel.tel_test_utils import stop_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_connected_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import http_file_download_by_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash
+from acts_contrib.test_utils.gnss.gnss_test_utils import get_baseband_and_gms_version
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_attenuator_gnss_signal
+from acts_contrib.test_utils.gnss.gnss_test_utils import _init_device
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_location_service
+from acts_contrib.test_utils.gnss.gnss_test_utils import clear_logd_gnss_qxdm_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_mobile_data
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_wifi_and_bt_scanning
+from acts_contrib.test_utils.gnss.gnss_test_utils import get_gnss_qxdm_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import remount_device
+from acts_contrib.test_utils.gnss.gnss_test_utils import reboot
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_network_location
+from acts_contrib.test_utils.gnss.gnss_test_utils import launch_google_map
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_location_api
+from acts_contrib.test_utils.gnss.gnss_test_utils import set_battery_saver_mode
+from acts_contrib.test_utils.gnss.gnss_test_utils import kill_xtra_daemon
+from acts_contrib.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import start_ttff_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_ttff_data
+from acts_contrib.test_utils.gnss.gnss_test_utils import start_youtube_video
+from acts_contrib.test_utils.gnss.gnss_test_utils import fastboot_factory_reset
+from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_trigger_modem_ssr_by_adb
+from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_trigger_modem_ssr_by_mds
+from acts_contrib.test_utils.gnss.gnss_test_utils import disable_supl_mode
+from acts_contrib.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
+from acts_contrib.test_utils.gnss.gnss_test_utils import check_xtra_download
+from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import enable_supl_mode
+from acts_contrib.test_utils.gnss.gnss_test_utils import start_toggle_gnss_by_gtw_gpstool
+from acts_contrib.test_utils.gnss.gnss_test_utils import grant_location_permission
+from acts_contrib.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_test_utils import stop_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_test_utils import get_tcpdump_log
class GnssFunctionTest(BaseTestClass):
diff --git a/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py b/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py
index f5a3dcc..e0f70a5 100644
--- a/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py
+++ b/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
from acts import utils
-from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
-from acts.test_utils.gnss import gnss_test_utils as gutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
class GnssPowerAGPSTest(PowerGTWGnssBaseTest):
diff --git a/acts_tests/tests/google/gnss/GnssPowerBasicTest.py b/acts_tests/tests/google/gnss/GnssPowerBasicTest.py
index a6c0066..cd60c48 100644
--- a/acts_tests/tests/google/gnss/GnssPowerBasicTest.py
+++ b/acts_tests/tests/google/gnss/GnssPowerBasicTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
from acts import utils
-from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
-from acts.test_utils.gnss import gnss_test_utils as gutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
class GnssPowerBasicTest(PowerGTWGnssBaseTest):
diff --git a/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py b/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py
index 100b6bf..ec4733f 100644
--- a/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py
+++ b/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
from acts import utils
-from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
-from acts.test_utils.gnss import gnss_test_utils as gutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
class GnssPowerLongIntervalTest(PowerGTWGnssBaseTest):
diff --git a/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py b/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py
index 4e64be2..8a2b99d 100644
--- a/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py
+++ b/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
from acts import utils
-from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
-from acts.test_utils.gnss import gnss_test_utils as gutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
class GnssPowerLowPowerTest(PowerGTWGnssBaseTest):
diff --git a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
index 8844cbc..cabd82d 100644
--- a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
+++ b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
@@ -5,9 +5,9 @@
from acts import utils
from acts import signals
from acts.base_test import BaseTestClass
-from acts.test_utils.tel.tel_defines import EventSmsSentSuccess
-from acts.test_utils.tel.tel_test_utils import get_iccid_by_adb
-from acts.test_utils.tel.tel_test_utils import is_sim_ready_by_adb
+from acts_contrib.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts_contrib.test_utils.tel.tel_test_utils import get_iccid_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_ready_by_adb
class GnssSimInventoryTest(BaseTestClass):
diff --git a/acts_tests/tests/google/gnss/LocationPlatinumTest.py b/acts_tests/tests/google/gnss/LocationPlatinumTest.py
index 73b628f..110748f 100644
--- a/acts_tests/tests/google/gnss/LocationPlatinumTest.py
+++ b/acts_tests/tests/google/gnss/LocationPlatinumTest.py
@@ -20,9 +20,9 @@
from acts import signals
from acts import utils
from acts.base_test import BaseTestClass
-from acts.test_utils.gnss import gnss_test_utils as gutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.tel import tel_test_utils as tutils
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_test_utils as tutils
BACKGROUND_LOCATION_PERMISSION = 'android.permission.ACCESS_BACKGROUND_LOCATION'
APP_CLEAN_UP_TIME = 60
diff --git a/acts_tests/tests/google/native/NativeTest.py b/acts_tests/tests/google/native/NativeTest.py
index 90ebceb..b62b456 100644
--- a/acts_tests/tests/google/native/NativeTest.py
+++ b/acts_tests/tests/google/native/NativeTest.py
@@ -16,8 +16,8 @@
import time
from acts.base_test import BaseTestClass
-from acts.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
-from acts.test_utils.bt.bt_test_utils import generate_id_by_size
+from acts_contrib.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
class NativeTest(BaseTestClass):
tests = None
diff --git a/acts_tests/tests/google/native/bt/BtNativeTest.py b/acts_tests/tests/google/native/bt/BtNativeTest.py
index 55674bc..abb8f17 100644
--- a/acts_tests/tests/google/native/bt/BtNativeTest.py
+++ b/acts_tests/tests/google/native/bt/BtNativeTest.py
@@ -1,7 +1,7 @@
from acts.base_test import BaseTestClass
from acts.controllers import native_android_device
-from acts.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
-from acts.test_utils.bt.bt_test_utils import generate_id_by_size
+from acts_contrib.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
class BtNativeTest(BaseTestClass):
diff --git a/acts_tests/tests/google/net/ApfCountersTest.py b/acts_tests/tests/google/net/ApfCountersTest.py
index 4033377..0b4f3dc 100755
--- a/acts_tests/tests/google/net/ApfCountersTest.py
+++ b/acts_tests/tests/google/net/ApfCountersTest.py
@@ -15,17 +15,17 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from scapy.all import ICMPv6ND_RA
from scapy.all import rdpcap
from scapy.all import Scapy_Exception
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import copy
import os
diff --git a/acts_tests/tests/google/net/BluetoothTetheringTest.py b/acts_tests/tests/google/net/BluetoothTetheringTest.py
index df7bad5..e4d3c67 100644
--- a/acts_tests/tests/google/net/BluetoothTetheringTest.py
+++ b/acts_tests/tests/google/net/BluetoothTetheringTest.py
@@ -18,12 +18,12 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import orchestrate_and_verify_pan_connection
-from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_and_verify_pan_connection
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
DEFAULT_PING_URL = "https://www.google.com/robots.txt"
diff --git a/acts_tests/tests/google/net/CaptivePortalTest.py b/acts_tests/tests/google/net/CaptivePortalTest.py
index 7df4372..5b03ea2 100644
--- a/acts_tests/tests/google/net/CaptivePortalTest.py
+++ b/acts_tests/tests/google/net/CaptivePortalTest.py
@@ -18,11 +18,11 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconst
-from acts.test_utils.net import connectivity_test_utils as cutils
-from acts.test_utils.net import ui_utils as uutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.net import connectivity_test_utils as cutils
+from acts_contrib.test_utils.net import ui_utils as uutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
IFACE = "InterfaceName"
diff --git a/acts_tests/tests/google/net/CoreNetworkingOTATest.py b/acts_tests/tests/google/net/CoreNetworkingOTATest.py
index 5b350f8..019409a 100755
--- a/acts_tests/tests/google/net/CoreNetworkingOTATest.py
+++ b/acts_tests/tests/google/net/CoreNetworkingOTATest.py
@@ -26,10 +26,10 @@
from acts.base_test import BaseTestClass
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.net import connectivity_const as cconst
-import acts.test_utils.net.net_test_utils as nutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.net.net_test_utils as nutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
VPN_CONST = cconst.VpnProfile
VPN_TYPE = cconst.VpnProfileType
diff --git a/acts_tests/tests/google/net/CoreNetworkingTest.py b/acts_tests/tests/google/net/CoreNetworkingTest.py
index 308beda..fc32f92 100644
--- a/acts_tests/tests/google/net/CoreNetworkingTest.py
+++ b/acts_tests/tests/google/net/CoreNetworkingTest.py
@@ -17,8 +17,8 @@
from acts import base_test
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
dum_class = "com.android.tests.connectivity.uid.ConnectivityTestActivity"
diff --git a/acts_tests/tests/google/net/DataCostTest.py b/acts_tests/tests/google/net/DataCostTest.py
index a07fdcd..6174009 100644
--- a/acts_tests/tests/google/net/DataCostTest.py
+++ b/acts_tests/tests/google/net/DataCostTest.py
@@ -27,14 +27,14 @@
from acts import test_runner
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.tel.tel_test_utils import _check_file_existance
-from acts.test_utils.tel.tel_test_utils import _generate_file_directory_and_file_name
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_NONE as NONE
-from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_HANDOVER as HANDOVER
-from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_RELIABILITY as RELIABILITY
-from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_PERFORMANCE as PERFORMANCE
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.tel.tel_test_utils import _check_file_existance
+from acts_contrib.test_utils.tel.tel_test_utils import _generate_file_directory_and_file_name
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_NONE as NONE
+from acts_contrib.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_HANDOVER as HANDOVER
+from acts_contrib.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_RELIABILITY as RELIABILITY
+from acts_contrib.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_PERFORMANCE as PERFORMANCE
DOWNLOAD_PATH = "/sdcard/Download/"
RELIABLE = RELIABILITY | HANDOVER
diff --git a/acts_tests/tests/google/net/DataUsageTest.py b/acts_tests/tests/google/net/DataUsageTest.py
index 12a21c6..1582886 100644
--- a/acts_tests/tests/google/net/DataUsageTest.py
+++ b/acts_tests/tests/google/net/DataUsageTest.py
@@ -23,15 +23,15 @@
from acts.controllers.adb_lib.error import AdbError
from acts.controllers.ap_lib import hostapd_constants
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconst
-from acts.test_utils.net import connectivity_test_utils as cutils
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.tel import tel_test_utils as ttutils
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import http_file_download_by_chrome
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.net import connectivity_test_utils as cutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.tel import tel_test_utils as ttutils
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import http_file_download_by_chrome
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
import queue
from queue import Empty
diff --git a/acts_tests/tests/google/net/DnsOverTlsTest.py b/acts_tests/tests/google/net/DnsOverTlsTest.py
index 5ab964b..13d8fe1 100644
--- a/acts_tests/tests/google/net/DnsOverTlsTest.py
+++ b/acts_tests/tests/google/net/DnsOverTlsTest.py
@@ -18,16 +18,16 @@
from acts import asserts
from acts.controllers.openwrt_ap import MOBLY_CONTROLLER_CONFIG_NAME as OPENWRT
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconst
-from acts.test_utils.net import connectivity_test_utils as cutils
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.net import connectivity_test_utils as cutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from scapy.all import rdpcap
from scapy.all import Scapy_Exception
from scapy.all import TCP
diff --git a/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py b/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py
index cd9c41a..e0fb160 100644
--- a/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py
+++ b/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py
@@ -16,9 +16,9 @@
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import connectivity_const
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
VPN_CONST = connectivity_const.VpnProfile
VPN_TYPE = connectivity_const.VpnProfileType
diff --git a/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py b/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py
index 1f36e9a..6196f61 100644
--- a/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py
+++ b/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py
@@ -16,9 +16,9 @@
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import connectivity_const
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
VPN_CONST = connectivity_const.VpnProfile
VPN_TYPE = connectivity_const.VpnProfileType
diff --git a/acts_tests/tests/google/net/IpSecTest.py b/acts_tests/tests/google/net/IpSecTest.py
index 1b4a144..4c61b3e 100644
--- a/acts_tests/tests/google/net/IpSecTest.py
+++ b/acts_tests/tests/google/net/IpSecTest.py
@@ -17,12 +17,12 @@
from acts import base_test
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconst
-from acts.test_utils.net import ipsec_test_utils as iutils
-from acts.test_utils.net import socket_test_utils as sutils
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.net import ipsec_test_utils as iutils
+from acts_contrib.test_utils.net import socket_test_utils as sutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
import random
import time
diff --git a/acts_tests/tests/google/net/LegacyVpnTest.py b/acts_tests/tests/google/net/LegacyVpnTest.py
index a4bfc52..9a8465a 100644
--- a/acts_tests/tests/google/net/LegacyVpnTest.py
+++ b/acts_tests/tests/google/net/LegacyVpnTest.py
@@ -25,10 +25,10 @@
from acts import test_runner
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net import connectivity_const
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
VPN_CONST = connectivity_const.VpnProfile
VPN_TYPE = connectivity_const.VpnProfileType
diff --git a/acts_tests/tests/google/net/ProxyTest.py b/acts_tests/tests/google/net/ProxyTest.py
index 4162e82..0511632 100644
--- a/acts_tests/tests/google/net/ProxyTest.py
+++ b/acts_tests/tests/google/net/ProxyTest.py
@@ -16,9 +16,9 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from scapy.all import IP
diff --git a/acts_tests/tests/google/net/SocketKeepaliveTest.py b/acts_tests/tests/google/net/SocketKeepaliveTest.py
index 5c63497..d348b72 100644
--- a/acts_tests/tests/google/net/SocketKeepaliveTest.py
+++ b/acts_tests/tests/google/net/SocketKeepaliveTest.py
@@ -19,12 +19,12 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_test_utils as cutils
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.net import socket_test_utils as sutils
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import connectivity_test_utils as cutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.net import socket_test_utils as sutils
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from scapy.all import rdpcap
from scapy.all import Scapy_Exception
from scapy.all import TCP
diff --git a/acts_tests/tests/google/net/UsbTetheringTest.py b/acts_tests/tests/google/net/UsbTetheringTest.py
index 8df51f5..e02611e 100644
--- a/acts_tests/tests/google/net/UsbTetheringTest.py
+++ b/acts_tests/tests/google/net/UsbTetheringTest.py
@@ -1,8 +1,8 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from scapy.all import get_if_list
from scapy.all import get_if_raw_hwaddr
diff --git a/acts_tests/tests/google/net/VpnOverLTETest.py b/acts_tests/tests/google/net/VpnOverLTETest.py
index a7e4172..fdb7e26 100644
--- a/acts_tests/tests/google/net/VpnOverLTETest.py
+++ b/acts_tests/tests/google/net/VpnOverLTETest.py
@@ -15,10 +15,10 @@
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net import connectivity_const
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
VPN_CONST = connectivity_const.VpnProfile
VPN_TYPE = connectivity_const.VpnProfileType
diff --git a/acts_tests/tests/google/power/PowerBaselineTest.py b/acts_tests/tests/google/power/PowerBaselineTest.py
index f1904e3..e056371 100644
--- a/acts_tests/tests/google/power/PowerBaselineTest.py
+++ b/acts_tests/tests/google/power/PowerBaselineTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from acts.test_utils.power.PowerBaseTest import PowerBaseTest
+from acts_contrib.test_utils.power.PowerBaseTest import PowerBaseTest
class PowerBaselineTest(PowerBaseTest):
diff --git a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
index 1933699..4f42ae0 100644
--- a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
import time
-import acts.test_utils.bt.BleEnum as bleenum
-import acts.test_utils.bt.bt_power_test_utils as btputils
-import acts.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.bt.BleEnum as bleenum
+import acts_contrib.test_utils.bt.bt_power_test_utils as btputils
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
BLE_LOCATION_SCAN_ENABLE = 'settings put secure location_mode 3'
EXTRA_ADV_TIME = 3
diff --git a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
index f859f0c..0aa5ad9 100644
--- a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
import time
-import acts.test_utils.bt.BleEnum as bleenum
-import acts.test_utils.bt.bt_power_test_utils as btputils
-import acts.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.bt.BleEnum as bleenum
+import acts_contrib.test_utils.bt.bt_power_test_utils as btputils
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
BLE_LOCATION_SCAN_ENABLE = 'settings put secure location_mode 3'
EXTRA_SCAN_TIME = 3
diff --git a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
index b6d60f1..2d0bc9d 100644
--- a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
@@ -15,10 +15,10 @@
# limitations under the License.
import time
-import acts.test_utils.bt.bt_test_utils as btutils
-import acts.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
from acts import asserts
-from acts.test_utils.bt import BtEnum
+from acts_contrib.test_utils.bt import BtEnum
EXTRA_PLAY_TIME = 10
diff --git a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
index 4b14f17..13b3f39 100644
--- a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
@@ -17,8 +17,8 @@
import csv
import os
import time
-import acts.test_utils.bt.bt_test_utils as btutils
-import acts.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
EXTRA_PLAY_TIME = 30
diff --git a/acts_tests/tests/google/power/bt/PowerBTidleTest.py b/acts_tests/tests/google/power/bt/PowerBTidleTest.py
index bab79d0..79831e7 100644
--- a/acts_tests/tests/google/power/bt/PowerBTidleTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBTidleTest.py
@@ -15,8 +15,8 @@
# limitations under the License.
import time
-import acts.test_utils.power.PowerBTBaseTest as PBtBT
-import acts.test_utils.bt.bt_test_utils as btutils
+import acts_contrib.test_utils.power.PowerBTBaseTest as PBtBT
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
SCREEN_OFF_WAIT_TIME = 2
diff --git a/acts_tests/tests/google/power/coex/PowerCoexbaselineTest.py b/acts_tests/tests/google/power/coex/PowerCoexbaselineTest.py
index 19d7763..5b6c8c1 100644
--- a/acts_tests/tests/google/power/coex/PowerCoexbaselineTest.py
+++ b/acts_tests/tests/google/power/coex/PowerCoexbaselineTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.PowerCoexBaseTest as PCoBT
+import acts_contrib.test_utils.power.PowerCoexBaseTest as PCoBT
from acts.test_decorators import test_tracker_info
diff --git a/acts_tests/tests/google/power/coex/PowerCoexscanTest.py b/acts_tests/tests/google/power/coex/PowerCoexscanTest.py
index 3d1f1e3..f15f76b 100644
--- a/acts_tests/tests/google/power/coex/PowerCoexscanTest.py
+++ b/acts_tests/tests/google/power/coex/PowerCoexscanTest.py
@@ -15,7 +15,7 @@
# limitations under the License.
import math
-import acts.test_utils.power.PowerCoexBaseTest as PCoBT
+import acts_contrib.test_utils.power.PowerCoexBaseTest as PCoBT
from acts.test_decorators import test_tracker_info
diff --git a/acts_tests/tests/google/power/gnss/PowerGnssDpoSimTest.py b/acts_tests/tests/google/power/gnss/PowerGnssDpoSimTest.py
index 9c2ce9a..14b36e3 100644
--- a/acts_tests/tests/google/power/gnss/PowerGnssDpoSimTest.py
+++ b/acts_tests/tests/google/power/gnss/PowerGnssDpoSimTest.py
@@ -14,9 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.PowerGnssBaseTest as GBT
-from acts.test_utils.gnss import dut_log_test_utils as diaglog
-from acts.test_utils.gnss import gnss_test_utils as gutil
+import acts_contrib.test_utils.power.PowerGnssBaseTest as GBT
+from acts_contrib.test_utils.gnss import dut_log_test_utils as diaglog
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutil
import time
import os
from acts import utils
diff --git a/acts_tests/tests/google/power/tel/PowerTelHotspot_LTE_Test.py b/acts_tests/tests/google/power/tel/PowerTelHotspot_LTE_Test.py
index dd9502e..ad98e85 100644
--- a/acts_tests/tests/google/power/tel/PowerTelHotspot_LTE_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelHotspot_LTE_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_hotspot_traffic_power_test as chtpw
+import acts_contrib.test_utils.power.cellular.cellular_hotspot_traffic_power_test as chtpw
class PowerTelHotspot_LTE_Test(chtpw.PowerTelHotspotTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelIdle_LTE_Test.py b/acts_tests/tests/google/power/tel/PowerTelIdle_LTE_Test.py
index 47151aa..12c9973 100644
--- a/acts_tests/tests/google/power/tel/PowerTelIdle_LTE_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelIdle_LTE_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_idle_power_test as cipt
+import acts_contrib.test_utils.power.cellular.cellular_idle_power_test as cipt
class PowerTelIdle_LTE_Test(cipt.PowerTelIdleTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelIdle_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelIdle_Modem_Test.py
index 40095fb..5ba1674 100644
--- a/acts_tests/tests/google/power/tel/PowerTelIdle_Modem_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelIdle_Modem_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_idle_power_test as cipt
+import acts_contrib.test_utils.power.cellular.cellular_idle_power_test as cipt
class PowerTelIdle_Modem_Test(cipt.PowerTelIdleTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelIdle_UMTS_Test.py b/acts_tests/tests/google/power/tel/PowerTelIdle_UMTS_Test.py
index 07617be..7af7fea 100644
--- a/acts_tests/tests/google/power/tel/PowerTelIdle_UMTS_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelIdle_UMTS_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_idle_power_test as cipt
+import acts_contrib.test_utils.power.cellular.cellular_idle_power_test as cipt
class PowerTelIdle_UMTS_Test(cipt.PowerTelIdleTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py
index 0ceade6..2ee5c5b 100644
--- a/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_pdcch_power_test as cppt
+import acts_contrib.test_utils.power.cellular.cellular_pdcch_power_test as cppt
class PowerTelMac_Modem_Test(cppt.PowerTelPDCCHTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
index 47b987a..18dcf0e 100644
--- a/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_pdcch_power_test as cppt
+import acts_contrib.test_utils.power.cellular.cellular_pdcch_power_test as cppt
class PowerTelPdcch_Modem_Test(cppt.PowerTelPDCCHTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelStandby_LTE_Test.py b/acts_tests/tests/google/power/tel/PowerTelStandby_LTE_Test.py
index 36e2021..7836f6b 100644
--- a/acts_tests/tests/google/power/tel/PowerTelStandby_LTE_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelStandby_LTE_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_idle_power_test as cipt
+import acts_contrib.test_utils.power.cellular.cellular_idle_power_test as cipt
class PowerTelStandby_LTE_Test(cipt.PowerTelIdleTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_GSM_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_GSM_Test.py
index b99aba0..934d827 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_GSM_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_GSM_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_GSM_Test(ctpt.PowerTelTrafficTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_LTECA_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_LTECA_Test.py
index 1e94c3b..71d5840 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_LTECA_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_LTECA_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_LTECA_Test(ctpt.PowerTelTrafficTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_LTE_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_LTE_Test.py
index 7facc34..3fb2fa7 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_LTE_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_LTE_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_LTE_Test(ctpt.PowerTelTrafficTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_Modem_Test.py
index 1206639..61897ee 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_Modem_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_Modem_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_Modem_Test(ctpt.PowerTelTrafficTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_TxSweep_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_TxSweep_Test.py
index ee4740c..09af00c 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_TxSweep_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_TxSweep_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_TxSweep_Test(ctpt.PowerTelTxPowerSweepTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_UMTS_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_UMTS_Test.py
index acc2048..406b643 100644
--- a/acts_tests/tests/google/power/tel/PowerTelTraffic_UMTS_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_UMTS_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt
+import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
class PowerTelTraffic_UMTS_Test(ctpt.PowerTelTrafficTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelVoiceCall_LTE_Test.py b/acts_tests/tests/google/power/tel/PowerTelVoiceCall_LTE_Test.py
index f352418..206004f 100644
--- a/acts_tests/tests/google/power/tel/PowerTelVoiceCall_LTE_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelVoiceCall_LTE_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_volte_power_test as cvltept
+import acts_contrib.test_utils.power.cellular.cellular_volte_power_test as cvltept
class PowerTelVoiceCall_LTE_Test(cvltept.PowerTelVoLTECallTest):
diff --git a/acts_tests/tests/google/power/tel/PowerTelVoiceCall_UMTS_Test.py b/acts_tests/tests/google/power/tel/PowerTelVoiceCall_UMTS_Test.py
index 3201cb9..6a92da9 100644
--- a/acts_tests/tests/google/power/tel/PowerTelVoiceCall_UMTS_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelVoiceCall_UMTS_Test.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.power.cellular.cellular_voice_call_power_test as cvcpt
+import acts_contrib.test_utils.power.cellular.cellular_voice_call_power_test as cvcpt
class PowerTelVoiceCall_UMTS_Test(cvcpt.PowerTelVoiceCallTest):
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py b/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
index 78388e6..74106c9 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
@@ -16,12 +16,12 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.test_utils.power.IperfHelper import IperfHelper
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.power.IperfHelper import IperfHelper
class PowerWiFiHotspotTest(PWBT.PowerWiFiBaseTest):
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFibaselineTest.py b/acts_tests/tests/google/power/wifi/PowerWiFibaselineTest.py
index 213c81a..8e442c4 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFibaselineTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFibaselineTest.py
@@ -14,8 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from acts.test_decorators import test_tracker_info
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFidtimTest.py b/acts_tests/tests/google/power/wifi/PowerWiFidtimTest.py
index 013931d..2d6eec1 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFidtimTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFidtimTest.py
@@ -16,8 +16,8 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
class PowerWiFidtimTest(PWBT.PowerWiFiBaseTest):
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFimulticastTest.py b/acts_tests/tests/google/power/wifi/PowerWiFimulticastTest.py
index 8832469..8b7e063 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFimulticastTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFimulticastTest.py
@@ -16,8 +16,8 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
from acts.controllers import packet_sender as pkt_utils
RA_SHORT_LIFETIME = 3
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFiroamingTest.py b/acts_tests/tests/google/power/wifi/PowerWiFiroamingTest.py
index 2a56a84..b366584 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFiroamingTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFiroamingTest.py
@@ -19,10 +19,10 @@
from acts import utils
from acts.controllers.ap_lib import hostapd_constants as hc
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_constants as wc
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.power import plot_utils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_constants as wc
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power import plot_utils
PHONE_BATTERY_VOLTAGE = 4.2
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py b/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
index e1153c1..72f733f 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
@@ -16,8 +16,8 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
UNLOCK_SCREEN = 'input keyevent 82'
LOCATION_ON = 'settings put secure location_mode 3'
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFitrafficTest.py b/acts_tests/tests/google/power/wifi/PowerWiFitrafficTest.py
index c6b1a3e..7ba9c35 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFitrafficTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFitrafficTest.py
@@ -16,8 +16,8 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.power import PowerWiFiBaseTest as PWBT
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
+from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
TEMP_FILE = '/sdcard/Download/tmp.log'
diff --git a/acts_tests/tests/google/tel/etc/manage_sim.py b/acts_tests/tests/google/tel/etc/manage_sim.py
index 1d51cb0..91a3887 100755
--- a/acts_tests/tests/google/tel/etc/manage_sim.py
+++ b/acts_tests/tests/google/tel/etc/manage_sim.py
@@ -23,9 +23,9 @@
import argparse
import json
import acts.controllers.android_device as android_device
-import acts.test_utils.tel.tel_defines as tel_defines
-import acts.test_utils.tel.tel_lookup_tables as tel_lookup_tables
-import acts.test_utils.tel.tel_test_utils as tel_test_utils
+import acts_contrib.test_utils.tel.tel_defines as tel_defines
+import acts_contrib.test_utils.tel.tel_lookup_tables as tel_lookup_tables
+import acts_contrib.test_utils.tel.tel_test_utils as tel_test_utils
def get_active_sim_list(verbose_warnings=False):
diff --git a/acts_tests/tests/google/tel/lab/TelLabCmasTest.py b/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
index 906c0a9..4292c87 100644
--- a/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
@@ -22,46 +22,46 @@
from acts.controllers.anritsu_lib.md8475a import CBCHSetup
from acts.controllers.anritsu_lib.md8475a import CTCHSetup
from acts.controllers.anritsu_lib.md8475a import MD8475A
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_AMBER
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_EXTREME
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_PRESIDENTIAL
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_LIKELY
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_EVACUATE
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_MONITOR
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_SHELTER
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_SEVERITY_EXTREME
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_URGENCY_IMMEDIATE
-from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_OBSERVED
-from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY
-from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY
-from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_PRESIDENTIAL_ALERT
-from acts.test_utils.tel.anritsu_utils import cb_serial_number
-from acts.test_utils.tel.anritsu_utils import cmas_receive_verify_message_cdma1x
-from acts.test_utils.tel.anritsu_utils import cmas_receive_verify_message_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_GSM
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_AMBER
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_EXTREME
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_PRESIDENTIAL
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_LIKELY
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_EVACUATE
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_MONITOR
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_SHELTER
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_SEVERITY_EXTREME
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_URGENCY_IMMEDIATE
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_OBSERVED
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY
+from acts_contrib.test_utils.tel.anritsu_utils import CMAS_MESSAGE_PRESIDENTIAL_ALERT
+from acts_contrib.test_utils.tel.anritsu_utils import cb_serial_number
+from acts_contrib.test_utils.tel.anritsu_utils import cmas_receive_verify_message_cdma1x
+from acts_contrib.test_utils.tel.anritsu_utils import cmas_receive_verify_message_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
WAIT_TIME_BETWEEN_REG_AND_MSG = 15 # default 15 sec
diff --git a/acts_tests/tests/google/tel/lab/TelLabDataRoamingTest.py b/acts_tests/tests/google/tel/lab/TelLabDataRoamingTest.py
index 8f09fea..af4ca6c 100644
--- a/acts_tests/tests/google/tel/lab/TelLabDataRoamingTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabDataRoamingTest.py
@@ -23,18 +23,18 @@
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsServiceState
from acts.controllers.anritsu_lib.md8475a import BtsPacketRate
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_cell_data_roaming
-from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_cell_data_roaming
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.utils import adb_shell_ping
PING_DURATION = 5 # Number of packets to ping
diff --git a/acts_tests/tests/google/tel/lab/TelLabDataTest.py b/acts_tests/tests/google/tel/lab/TelLabDataTest.py
index c48446c..9a1355d 100644
--- a/acts_tests/tests/google/tel/lab/TelLabDataTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabDataTest.py
@@ -27,54 +27,54 @@
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
-from acts.test_utils.tel.anritsu_utils import cb_serial_number
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import sms_mo_send
-from acts.test_utils.tel.anritsu_utils import sms_mt_receive_verify
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import RAT_GSM
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import POWER_LEVEL_OUT_OF_SERVICE
-from acts.test_utils.tel.tel_defines import POWER_LEVEL_FULL_SERVICE
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import get_host_ip_address
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import check_data_stall_detection
-from acts.test_utils.tel.tel_test_utils import check_network_validation_fail
-from acts.test_utils.tel.tel_test_utils import check_data_stall_recovery
-from acts.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts.test_utils.tel.tel_test_utils import break_internet_except_sl4a_port
-from acts.test_utils.tel.tel_test_utils import resume_internet_with_sl4a_port
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.anritsu_utils import cb_serial_number
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import sms_mo_send
+from acts_contrib.test_utils.tel.anritsu_utils import sms_mt_receive_verify
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import POWER_LEVEL_OUT_OF_SERVICE
+from acts_contrib.test_utils.tel.tel_defines import POWER_LEVEL_FULL_SERVICE
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import get_host_ip_address
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import check_data_stall_detection
+from acts_contrib.test_utils.tel.tel_test_utils import check_network_validation_fail
+from acts_contrib.test_utils.tel.tel_test_utils import check_data_stall_recovery
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import break_internet_except_sl4a_port
+from acts_contrib.test_utils.tel.tel_test_utils import resume_internet_with_sl4a_port
+from acts_contrib.test_utils.tel.tel_test_utils import \
test_data_browsing_success_using_sl4a
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
test_data_browsing_failure_using_sl4a
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.utils import adb_shell_ping
from acts.utils import rand_ascii_str
from acts.controllers import iperf_server
diff --git a/acts_tests/tests/google/tel/lab/TelLabEmergencyCallTest.py b/acts_tests/tests/google/tel/lab/TelLabEmergencyCallTest.py
index 4ea6e7f..d83faa3 100644
--- a/acts_tests/tests/google/tel/lab/TelLabEmergencyCallTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabEmergencyCallTest.py
@@ -23,47 +23,47 @@
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
-from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
-from acts.test_utils.tel.anritsu_utils import call_mo_setup_teardown
-from acts.test_utils.tel.anritsu_utils import ims_call_cs_teardown
-from acts.test_utils.tel.anritsu_utils import call_mt_setup_teardown
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
-from acts.test_utils.tel.tel_defines import DEFAULT_EMERGENCY_CALL_NUMBER
-from acts.test_utils.tel.tel_defines import EMERGENCY_CALL_NUMBERS
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import check_apm_mode_on_by_serial
-from acts.test_utils.tel.tel_test_utils import set_apm_mode_on_by_serial
-from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
+from acts_contrib.test_utils.tel.anritsu_utils import call_mo_setup_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import ims_call_cs_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import call_mt_setup_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts_contrib.test_utils.tel.tel_defines import DEFAULT_EMERGENCY_CALL_NUMBER
+from acts_contrib.test_utils.tel.tel_defines import EMERGENCY_CALL_NUMBERS
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import check_apm_mode_on_by_serial
+from acts_contrib.test_utils.tel.tel_test_utils import set_apm_mode_on_by_serial
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
from acts.utils import exe_cmd
diff --git a/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py b/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
index 1a1fbf9..3683173 100644
--- a/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
@@ -22,32 +22,32 @@
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import CBCHSetup
from acts.controllers.anritsu_lib.md8475a import CTCHSetup
-from acts.test_utils.tel.anritsu_utils import ETWS_WARNING_EARTHQUAKETSUNAMI
-from acts.test_utils.tel.anritsu_utils import ETWS_WARNING_OTHER_EMERGENCY
-from acts.test_utils.tel.anritsu_utils import cb_serial_number
-from acts.test_utils.tel.anritsu_utils import etws_receive_verify_message_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import RAT_GSM
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import ETWS_WARNING_EARTHQUAKETSUNAMI
+from acts_contrib.test_utils.tel.anritsu_utils import ETWS_WARNING_OTHER_EMERGENCY
+from acts_contrib.test_utils.tel.anritsu_utils import cb_serial_number
+from acts_contrib.test_utils.tel.anritsu_utils import etws_receive_verify_message_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
WAIT_TIME_BETWEEN_REG_AND_MSG = 15 # default 15 sec
diff --git a/acts_tests/tests/google/tel/lab/TelLabMobilityTest.py b/acts_tests/tests/google/tel/lab/TelLabMobilityTest.py
index f620826..aba648b 100644
--- a/acts_tests/tests/google/tel/lab/TelLabMobilityTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabMobilityTest.py
@@ -22,42 +22,42 @@
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsNumber
-from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
-from acts.test_utils.tel.anritsu_utils import handover_tc
-from acts.test_utils.tel.anritsu_utils import make_ims_call
-from acts.test_utils.tel.anritsu_utils import tear_down_call
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_evdo
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import get_host_ip_address
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
-from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
+from acts_contrib.test_utils.tel.anritsu_utils import handover_tc
+from acts_contrib.test_utils.tel.anritsu_utils import make_ims_call
+from acts_contrib.test_utils.tel.anritsu_utils import tear_down_call
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_evdo
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import get_host_ip_address
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.utils import adb_shell_ping
from acts.utils import rand_ascii_str
from acts.controllers import iperf_server
diff --git a/acts_tests/tests/google/tel/lab/TelLabNeighborCellTest.py b/acts_tests/tests/google/tel/lab/TelLabNeighborCellTest.py
index ae3ac1d..8690ae7 100644
--- a/acts_tests/tests/google/tel/lab/TelLabNeighborCellTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabNeighborCellTest.py
@@ -27,27 +27,27 @@
from acts.controllers.anritsu_lib.md8475a import BtsServiceState
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.mg3710a import MG3710A
-from acts.test_utils.tel.anritsu_utils import LTE_BAND_2
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import LTE_BAND_2
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.controllers.anritsu_lib.cell_configurations import \
gsm_band850_ch128_fr869_cid58_cell
from acts.controllers.anritsu_lib.cell_configurations import \
diff --git a/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py b/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
index b5ccd08..4208689 100644
--- a/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
@@ -21,37 +21,37 @@
from acts.controllers.anritsu_lib.md8475a import CBCHSetup
from acts.controllers.anritsu_lib.md8475a import CTCHSetup
from acts.controllers.anritsu_lib.md8475a import MD8475A
-from acts.test_utils.tel.anritsu_utils import cb_serial_number
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.anritsu_utils import cb_serial_number
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_test_utils import \
ensure_preferred_network_type_for_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import CARRIER_SPT
-from acts.test_utils.tel.tel_defines import CARRIER_TMO
-from acts.test_utils.tel.tel_defines import CARRIER_USCC
-from acts.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import is_sim_ready
-from acts.test_utils.tel.tel_test_utils import log_screen_shot
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import refresh_droid_config
-from acts.test_utils.tel.tel_test_utils import send_dialer_secret_code
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import add_google_account
-from acts.test_utils.tel.tel_test_utils import remove_google_account
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_SPT
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_TMO
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_USCC
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_ready
+from acts_contrib.test_utils.tel.tel_test_utils import log_screen_shot
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import refresh_droid_config
+from acts_contrib.test_utils.tel.tel_test_utils import send_dialer_secret_code
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import add_google_account
+from acts_contrib.test_utils.tel.tel_test_utils import remove_google_account
WAIT_TIME_BETWEEN_REG_AND_MSG = 15 # default 15 sec
CARRIER = None
diff --git a/acts_tests/tests/google/tel/lab/TelLabSmsTest.py b/acts_tests/tests/google/tel/lab/TelLabSmsTest.py
index 47270c9..2f2bc68 100644
--- a/acts_tests/tests/google/tel/lab/TelLabSmsTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabSmsTest.py
@@ -22,45 +22,45 @@
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import sms_mo_send
-from acts.test_utils.tel.anritsu_utils import sms_mt_receive_verify
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import RAT_GSM
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
-from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import sms_mo_send
+from acts_contrib.test_utils.tel.anritsu_utils import sms_mt_receive_verify
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
from acts.utils import rand_ascii_str
SINGLE_PART_LEN = 40
diff --git a/acts_tests/tests/google/tel/lab/TelLabUeIdentityTest.py b/acts_tests/tests/google/tel/lab/TelLabUeIdentityTest.py
index 3a9bb77..e7858ce 100644
--- a/acts_tests/tests/google/tel/lab/TelLabUeIdentityTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabUeIdentityTest.py
@@ -20,27 +20,27 @@
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import UEIdentityType
-from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_OPER
-from acts.test_utils.tel.anritsu_utils import read_ue_identity
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import RAT_GSM
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_OPER
+from acts_contrib.test_utils.tel.anritsu_utils import read_ue_identity
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
class TelLabUeIdentityTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/lab/TelLabVoiceTest.py b/acts_tests/tests/google/tel/lab/TelLabVoiceTest.py
index 1a638f9..f2417dd 100644
--- a/acts_tests/tests/google/tel/lab/TelLabVoiceTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabVoiceTest.py
@@ -24,42 +24,42 @@
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
-from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
-from acts.test_utils.tel.anritsu_utils import call_mo_setup_teardown
-from acts.test_utils.tel.anritsu_utils import ims_call_cs_teardown
-from acts.test_utils.tel.anritsu_utils import call_mt_setup_teardown
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
-from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
-from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
-from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
-from acts.test_utils.tel.anritsu_utils import set_usim_parameters
-from acts.test_utils.tel.anritsu_utils import set_post_sim_params
-from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
-from acts.test_utils.tel.tel_defines import RAT_1XRTT
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_test_utils import ensure_network_rat
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
+from acts_contrib.test_utils.tel.anritsu_utils import call_mo_setup_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import ims_call_cs_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import call_mt_setup_teardown
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_1x
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
+from acts_contrib.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts_contrib.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts_contrib.test_utils.tel.anritsu_utils import set_post_sim_params
+from acts_contrib.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
DEFAULT_CALL_NUMBER = "0123456789"
diff --git a/acts_tests/tests/google/tel/live/TelLiveCBRSTest.py b/acts_tests/tests/google/tel/live/TelLiveCBRSTest.py
index 5269bd6..7b63288 100644
--- a/acts_tests/tests/google/tel/live/TelLiveCBRSTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveCBRSTest.py
@@ -21,52 +21,52 @@
import collections
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_CBRS_DATA_SWITCH
-from acts.test_utils.tel.tel_defines import EventActiveDataSubIdChanged
-from acts.test_utils.tel.tel_defines import NetworkCallbackAvailable
-from acts.test_utils.tel.tel_defines import EventNetworkCallback
-from acts.test_utils.tel.tel_test_utils import get_phone_number
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_phone_not_in_call
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import load_scone_cat_simulate_data
-from acts.test_utils.tel.tel_test_utils import test_data_browsing_success_using_sl4a
-from acts.test_utils.tel.tel_test_utils import test_data_browsing_failure_using_sl4a
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import STORY_LINE
-from acts.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts.test_utils.tel.tel_test_utils import start_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_cdma
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_2g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_cbrs_and_default_sub_id
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_CBRS_DATA_SWITCH
+from acts_contrib.test_utils.tel.tel_defines import EventActiveDataSubIdChanged
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackAvailable
+from acts_contrib.test_utils.tel.tel_defines import EventNetworkCallback
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_not_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import load_scone_cat_simulate_data
+from acts_contrib.test_utils.tel.tel_test_utils import test_data_browsing_success_using_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import test_data_browsing_failure_using_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import STORY_LINE
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_cdma
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_cbrs_and_default_sub_id
from acts.utils import get_current_epoch_time
from queue import Empty
diff --git a/acts_tests/tests/google/tel/live/TelLiveCellInfoTest.py b/acts_tests/tests/google/tel/live/TelLiveCellInfoTest.py
index 037f260..5895d10 100644
--- a/acts_tests/tests/google/tel/live/TelLiveCellInfoTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveCellInfoTest.py
@@ -18,10 +18,10 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected, \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected, \
toggle_airplane_mode, ensure_phones_idle, start_qxdm_loggers
-from acts.test_utils.wifi import wifi_test_utils
+from acts_contrib.test_utils.wifi import wifi_test_utils
from acts.utils import disable_usb_charging, enable_usb_charging
NANO_TO_SEC = 1000000000
diff --git a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorBaseTest.py b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorBaseTest.py
index b7e4403..5ec5ddb 100644
--- a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorBaseTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorBaseTest.py
@@ -22,44 +22,44 @@
import time
from acts import signals
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
-from acts.test_utils.tel.tel_defines import CAPABILITY_VT
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_test_utils import bring_up_connectivity_monitor
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts.test_utils.tel.tel_test_utils import get_model_name
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import last_call_drop_reason
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import toggle_wfc
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_test_utils import bring_up_connectivity_monitor
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import get_model_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_wfc
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
CALL_DROP_CODE_MAPPING = {
diff --git a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorMobilityTest.py b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorMobilityTest.py
index 2cd6ad5..fd76813 100644
--- a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorMobilityTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorMobilityTest.py
@@ -19,24 +19,24 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_atten_utils import set_rssi
-from acts.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
-from acts.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import SignalStrengthContainer
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_atten_utils import set_rssi
+from acts_contrib.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
+from acts_contrib.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import SignalStrengthContainer
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
from TelLiveConnectivityMonitorBaseTest import TelLiveConnectivityMonitorBaseTest
# Attenuator name
diff --git a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorTest.py b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorTest.py
index c0f0953..995c292 100644
--- a/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveConnectivityMonitorTest.py
@@ -18,8 +18,8 @@
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
from TelLiveConnectivityMonitorBaseTest import TelLiveConnectivityMonitorBaseTest
from TelLiveConnectivityMonitorBaseTest import ACTIONS
from TelLiveConnectivityMonitorBaseTest import TROUBLES
diff --git a/acts_tests/tests/google/tel/live/TelLiveDSDSVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveDSDSVoiceTest.py
index c979c7a..b547132 100644
--- a/acts_tests/tests/google/tel/live/TelLiveDSDSVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveDSDSVoiceTest.py
@@ -23,103 +23,103 @@
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_IWLAN
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import EventNetworkCallback
-from acts.test_utils.tel.tel_defines import NetworkCallbackAvailable
-from acts.test_utils.tel.tel_defines import NetworkCallbackLost
-from acts.test_utils.tel.tel_defines import SignalStrengthContainer
-from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import get_network_rat
-from acts.test_utils.tel.tel_test_utils import get_phone_number
-from acts.test_utils.tel.tel_test_utils import get_phone_number_for_subscription
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_network_call_back_event_match
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import is_phone_not_in_call
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts.test_utils.tel.tel_test_utils import get_lte_rsrp
-from acts.test_utils.tel.tel_test_utils import get_wifi_signal_strength
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import start_qxdm_logger
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import test_data_browsing_success_using_sl4a
-from acts.test_utils.tel.tel_test_utils import test_data_browsing_failure_using_sl4a
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_idle_2g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_2g
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
-from acts.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts.test_utils.tel.tel_subscription_utils import perform_dds_switch
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_message
-from acts.test_utils.tel.tel_subscription_utils import set_slways_allow_mms_data
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import EventNetworkCallback
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackAvailable
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackLost
+from acts_contrib.test_utils.tel.tel_defines import SignalStrengthContainer
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_network_call_back_event_match
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_not_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import get_lte_rsrp
+from acts_contrib.test_utils.tel.tel_test_utils import get_wifi_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_logger
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import test_data_browsing_success_using_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import test_data_browsing_failure_using_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_3g_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_2g
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import perform_dds_switch
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_message
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_slways_allow_mms_data
from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/tel/live/TelLiveDataTest.py b/acts_tests/tests/google/tel/live/TelLiveDataTest.py
index efa1e9b..6c8726d 100755
--- a/acts_tests/tests/google/tel/live/TelLiveDataTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveDataTest.py
@@ -25,117 +25,117 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_from_slot_index
-from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
-from acts.test_utils.tel.tel_defines import FAKE_DATE_TIME
-from acts.test_utils.tel.tel_defines import FAKE_YEAR
-from acts.test_utils.tel.tel_defines import EventNetworkCallback
-from acts.test_utils.tel.tel_defines import NetworkCallbackCapabilitiesChanged
-from acts.test_utils.tel.tel_defines import NetworkCallbackLost
-from acts.test_utils.tel.tel_defines import GEN_2G
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import RAT_2G
-from acts.test_utils.tel.tel_defines import RAT_3G
-from acts.test_utils.tel.tel_defines import RAT_4G
-from acts.test_utils.tel.tel_defines import RAT_5G
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_NR_LTE_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import SIM1_SLOT_INDEX
-from acts.test_utils.tel.tel_defines import SIM2_SLOT_INDEX
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
-from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
-from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_USER_PLANE_DATA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
-from acts.test_utils.tel.tel_defines import \
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import FAKE_DATE_TIME
+from acts_contrib.test_utils.tel.tel_defines import FAKE_YEAR
+from acts_contrib.test_utils.tel.tel_defines import EventNetworkCallback
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackCapabilitiesChanged
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackLost
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import RAT_2G
+from acts_contrib.test_utils.tel.tel_defines import RAT_3G
+from acts_contrib.test_utils.tel.tel_defines import RAT_4G
+from acts_contrib.test_utils.tel.tel_defines import RAT_5G
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_NR_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import SIM1_SLOT_INDEX
+from acts_contrib.test_utils.tel.tel_defines import SIM2_SLOT_INDEX
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
+from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_USER_PLANE_DATA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import \
WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING
-from acts.test_utils.tel.tel_defines import WAIT_TIME_TETHERING_AFTER_REBOOT
-from acts.test_utils.tel.tel_data_utils import airplane_mode_test
-from acts.test_utils.tel.tel_data_utils import browsing_test
-from acts.test_utils.tel.tel_data_utils import reboot_test
-from acts.test_utils.tel.tel_data_utils import change_data_sim_and_verify_data
-from acts.test_utils.tel.tel_data_utils import data_connectivity_single_bearer
-from acts.test_utils.tel.tel_data_utils import tethering_check_internet_connection
-from acts.test_utils.tel.tel_data_utils import wifi_cell_switching
-from acts.test_utils.tel.tel_data_utils import wifi_tethering_cleanup
-from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_TETHERING_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_data_utils import airplane_mode_test
+from acts_contrib.test_utils.tel.tel_data_utils import browsing_test
+from acts_contrib.test_utils.tel.tel_data_utils import reboot_test
+from acts_contrib.test_utils.tel.tel_data_utils import change_data_sim_and_verify_data
+from acts_contrib.test_utils.tel.tel_data_utils import data_connectivity_single_bearer
+from acts_contrib.test_utils.tel.tel_data_utils import tethering_check_internet_connection
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_cell_switching
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_tethering_cleanup
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import check_is_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import \
ensure_network_generation_for_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
-from acts.test_utils.tel.tel_test_utils import start_wifi_tethering
-from acts.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
-from acts.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
-from acts.test_utils.tel.tel_test_utils import get_current_override_network_type
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import stop_wifi_tethering
+from acts_contrib.test_utils.tel.tel_test_utils import start_wifi_tethering
+from acts_contrib.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
+from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import \
wait_for_voice_attach_for_subscription
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
wait_for_data_attach_for_subscription
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import wifi_reset
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
-from acts.test_utils.tel.tel_test_utils import check_data_stall_detection
-from acts.test_utils.tel.tel_test_utils import check_network_validation_fail
-from acts.test_utils.tel.tel_test_utils import break_internet_except_sl4a_port
-from acts.test_utils.tel.tel_test_utils import resume_internet_with_sl4a_port
-from acts.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
-from acts.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts.test_utils.tel.tel_test_utils import check_data_stall_recovery
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_reset
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
+from acts_contrib.test_utils.tel.tel_test_utils import check_data_stall_detection
+from acts_contrib.test_utils.tel.tel_test_utils import check_network_validation_fail
+from acts_contrib.test_utils.tel.tel_test_utils import break_internet_except_sl4a_port
+from acts_contrib.test_utils.tel.tel_test_utils import resume_internet_with_sl4a_port
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import check_data_stall_recovery
+from acts_contrib.test_utils.tel.tel_test_utils import \
test_data_browsing_success_using_sl4a
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
test_data_browsing_failure_using_sl4a
-from acts.test_utils.tel.tel_test_utils import set_time_sync_from_network
-from acts.test_utils.tel.tel_test_utils import datetime_handle
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_4g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_5g
+from acts_contrib.test_utils.tel.tel_test_utils import set_time_sync_from_network
+from acts_contrib.test_utils.tel.tel_test_utils import datetime_handle
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_4g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_5g
from acts.utils import disable_doze
from acts.utils import enable_doze
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/tel/live/TelLiveEmergencyBase.py b/acts_tests/tests/google/tel/live/TelLiveEmergencyBase.py
index e1ec0e2..edb8b39 100644
--- a/acts_tests/tests/google/tel/live/TelLiveEmergencyBase.py
+++ b/acts_tests/tests/google/tel/live/TelLiveEmergencyBase.py
@@ -21,36 +21,36 @@
import time
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import dumpsys_last_call_info
-from acts.test_utils.tel.tel_test_utils import dumpsys_last_call_number
-from acts.test_utils.tel.tel_test_utils import dumpsys_new_call_info
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import get_service_state_by_adb
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_sim_lock_enabled
-from acts.test_utils.tel.tel_test_utils import initiate_emergency_dialer_call_by_adb
-from acts.test_utils.tel.tel_test_utils import last_call_drop_reason
-from acts.test_utils.tel.tel_test_utils import reset_device_password
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import unlock_sim
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_capabilities
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_last_call_info
+from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_last_call_number
+from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_new_call_info
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_lock_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_emergency_dialer_call_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
+from acts_contrib.test_utils.tel.tel_test_utils import reset_device_password
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
from acts.utils import get_current_epoch_time
CARRIER_OVERRIDE_CMD = (
diff --git a/acts_tests/tests/google/tel/live/TelLiveEmergencyTest.py b/acts_tests/tests/google/tel/live/TelLiveEmergencyTest.py
index 4f21ee5..e29ed7a 100644
--- a/acts_tests/tests/google/tel/live/TelLiveEmergencyTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveEmergencyTest.py
@@ -19,19 +19,19 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import reset_device_password
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_capabilities
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import reset_device_password
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
from TelLiveEmergencyBase import TelLiveEmergencyBase
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
index dc53fd6..2734fed 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
@@ -20,54 +20,54 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import \
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import \
TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_data_utils import reboot_test
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_message_subid
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_data_utils import reboot_test
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_message_subid
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_on_same_network_of_host_ad
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import start_youtube_video
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import start_youtube_video
+from acts_contrib.test_utils.tel.tel_test_utils import \
wait_for_cell_data_connection_for_subscription
-from acts.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
-from acts.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_send_receive_verify_for_subscription
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import log_messaging_screen_shot
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import is_volte_enabled
-from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import log_messaging_screen_shot
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import is_volte_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import check_is_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_volte_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
from acts.utils import rand_ascii_str
CallResult = TelephonyVoiceTestResult.CallResult.Value
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
index 82ef7ca..b735184 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
@@ -19,45 +19,45 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import \
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import \
TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_outgoing_message_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_message_subid
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_message_subid
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_on_same_network_of_host_ad
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_send_receive_verify_for_subscription
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_in_collision_send_receive_verify_for_subscription
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_rx_power_off_multiple_send_receive_verify_for_subscription
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
voice_call_in_collision_with_mt_sms_msim
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import log_messaging_screen_shot
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import log_messaging_screen_shot
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_voice_general_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
from acts.utils import rand_ascii_str
CallResult = TelephonyVoiceTestResult.CallResult.Value
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSSupplementaryServiceTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSSupplementaryServiceTest.py
index f7af340..aae30c3 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSSupplementaryServiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSSupplementaryServiceTest.py
@@ -20,49 +20,49 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import \
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import \
TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_on_same_network_of_host_ad
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import get_capability_for_subscription
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import set_call_waiting
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import set_call_waiting
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import \
wait_and_reject_call_for_subscription
-from acts.test_utils.tel.tel_test_utils import erase_call_forwarding_by_mmi
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import erase_call_forwarding_by_mmi
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts_contrib.test_utils.tel.tel_voice_utils import \
three_phone_call_forwarding_short_seq
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import \
three_phone_call_waiting_short_seq
-from acts.test_utils.tel.tel_voice_utils import swap_calls
-from acts.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
CallResult = TelephonyVoiceTestResult.CallResult.Value
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
index 43baf79..f984ed7 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
@@ -17,35 +17,35 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import \
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import \
TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
-from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_on_same_network_of_host_ad
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import get_capability_for_subscription
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_voice_general_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
CallResult = TelephonyVoiceTestResult.CallResult.Value
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSWfcSupplementaryServiceTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSWfcSupplementaryServiceTest.py
index a5f10e5..acdb8eb 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSWfcSupplementaryServiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSWfcSupplementaryServiceTest.py
@@ -20,74 +20,74 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import \
TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import \
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import \
TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
-from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_subid_on_same_network_of_host_ad
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
-from acts.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription
-from acts.test_utils.tel.tel_test_utils import get_capability_for_subscription
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import set_call_forwarding_by_mmi
-from acts.test_utils.tel.tel_test_utils import erase_call_forwarding_by_mmi
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import set_call_waiting
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import set_call_forwarding_by_mmi
+from acts_contrib.test_utils.tel.tel_test_utils import erase_call_forwarding_by_mmi
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import set_call_waiting
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import \
wait_and_reject_call_for_subscription
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_csfb_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_voice_3g_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_voice_general_for_subscription
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_volte_for_subscription
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import \
three_phone_call_forwarding_short_seq
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_voice_utils import \
three_phone_call_waiting_short_seq
-from acts.test_utils.tel.tel_voice_utils import swap_calls
-from acts.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
CallResult = TelephonyVoiceTestResult.CallResult.Value
diff --git a/acts_tests/tests/google/tel/live/TelLiveImsSettingsTest.py b/acts_tests/tests/google/tel/live/TelLiveImsSettingsTest.py
index 4454be6..2e9c918 100644
--- a/acts_tests/tests/google/tel/live/TelLiveImsSettingsTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveImsSettingsTest.py
@@ -21,57 +21,57 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CarrierConfigs
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC_MODE_CHANGE
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_IMS_REGISTRATION
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_UNKNOWN
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import dumpsys_carrier_config
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import get_user_config_profile
-from acts.test_utils.tel.tel_test_utils import is_droid_in_rat_family
-from acts.test_utils.tel.tel_test_utils import revert_default_telephony_setting
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import toggle_wfc
-from acts.test_utils.tel.tel_test_utils import verify_default_telephony_setting
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_ims_registered
-from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
-from acts.test_utils.tel.tel_test_utils import wait_for_not_network_rat
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import wait_for_voice_attach
-from acts.test_utils.tel.tel_test_utils import wait_for_volte_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import wifi_reset
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
-from acts.test_utils.tel.tel_test_utils import WIFI_PWD_KEY
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC_MODE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_IMS_REGISTRATION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_carrier_config
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_rat_family
+from acts_contrib.test_utils.tel.tel_test_utils import revert_default_telephony_setting
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_wfc
+from acts_contrib.test_utils.tel.tel_test_utils import verify_default_telephony_setting
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ims_registered
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_not_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_voice_attach
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_volte_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_reset
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_PWD_KEY
class TelLiveImsSettingsTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLiveLockedSimTest.py b/acts_tests/tests/google/tel/live/TelLiveLockedSimTest.py
index 55c989c..83c192a 100644
--- a/acts_tests/tests/google/tel/live/TelLiveLockedSimTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveLockedSimTest.py
@@ -19,25 +19,25 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
-from acts.test_utils.tel.tel_defines import GEN_2G
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_lookup_tables import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_lookup_tables import \
network_preference_for_generation
-from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import get_sim_state
-from acts.test_utils.tel.tel_test_utils import is_sim_lock_enabled
-from acts.test_utils.tel.tel_test_utils import is_sim_locked
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import reset_device_password
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import unlock_sim
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_capabilities
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import get_sim_state
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_lock_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_locked
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import reset_device_password
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
from TelLiveEmergencyBase import TelLiveEmergencyBase
EXPECTED_CALL_TEST_RESULT = False
diff --git a/acts_tests/tests/google/tel/live/TelLiveMobilityStressTest.py b/acts_tests/tests/google/tel/live/TelLiveMobilityStressTest.py
index 2120c04..0319763 100644
--- a/acts_tests/tests/google/tel/live/TelLiveMobilityStressTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveMobilityStressTest.py
@@ -23,41 +23,41 @@
from acts.asserts import explicit_pass
from acts.asserts import fail
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_atten_utils import set_rssi
-from acts.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import is_voice_attached
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import get_current_voice_rat
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_atten_utils import set_rssi
+from acts_contrib.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_voice_attached
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import get_current_voice_rat
from acts.logger import epoch_to_log_line_timestamp
from acts.utils import get_current_epoch_time
diff --git a/acts_tests/tests/google/tel/live/TelLiveNoQXDMLogTest.py b/acts_tests/tests/google/tel/live/TelLiveNoQXDMLogTest.py
index c50403c..2ada2a8 100644
--- a/acts_tests/tests/google/tel/live/TelLiveNoQXDMLogTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveNoQXDMLogTest.py
@@ -29,44 +29,44 @@
from acts.controllers.android_device import list_adb_devices
from acts.controllers.android_device import list_fastboot_devices
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_BOOT_COMPLETE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_CARRIERCONFIG_CHANGE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_CARRIERID_CHANGE
-from acts.test_utils.tel.tel_defines import VZW_CARRIER_CONFIG_VERSION
-from acts.test_utils.tel.tel_defines import ATT_CARRIER_CONFIG_VERSION
-from acts.test_utils.tel.tel_defines import CARRIER_ID_METADATA_URL
-from acts.test_utils.tel.tel_defines import CARRIER_ID_CONTENT_URL
-from acts.test_utils.tel.tel_defines import CARRIER_ID_VERSION
-from acts.test_utils.tel.tel_defines import ER_DB_ID_VERSION
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_ER_DB_CHANGE
-from acts.test_utils.tel.tel_defines import CARRIER_ID_METADATA_URL_P
-from acts.test_utils.tel.tel_defines import CARRIER_ID_CONTENT_URL_P
-from acts.test_utils.tel.tel_defines import CARRIER_ID_VERSION_P
-from acts.test_utils.tel.tel_lookup_tables import device_capabilities
-from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_test_utils import lock_lte_band_by_mds
-from acts.test_utils.tel.tel_test_utils import get_model_name
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
-from acts.test_utils.tel.tel_test_utils import bring_up_sl4a
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import get_carrier_config_version
-from acts.test_utils.tel.tel_test_utils import get_carrier_id_version
-from acts.test_utils.tel.tel_test_utils import get_er_db_id_version
-from acts.test_utils.tel.tel_test_utils import get_database_content
-from acts.test_utils.tel.tel_test_utils import install_googleaccountutil_apk
-from acts.test_utils.tel.tel_test_utils import add_whitelisted_account
-from acts.test_utils.tel.tel_test_utils import adb_disable_verity
-from acts.test_utils.tel.tel_test_utils import install_carriersettings_apk
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import cleanup_configupdater
-from acts.test_utils.tel.tel_test_utils import pull_carrier_id_files
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_subscription_utils import get_cbrs_and_default_sub_id
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_BOOT_COMPLETE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_CARRIERCONFIG_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_CARRIERID_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import VZW_CARRIER_CONFIG_VERSION
+from acts_contrib.test_utils.tel.tel_defines import ATT_CARRIER_CONFIG_VERSION
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_METADATA_URL
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_CONTENT_URL
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_VERSION
+from acts_contrib.test_utils.tel.tel_defines import ER_DB_ID_VERSION
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_ER_DB_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_METADATA_URL_P
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_CONTENT_URL_P
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_ID_VERSION_P
+from acts_contrib.test_utils.tel.tel_lookup_tables import device_capabilities
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_capabilities
+from acts_contrib.test_utils.tel.tel_test_utils import lock_lte_band_by_mds
+from acts_contrib.test_utils.tel.tel_test_utils import get_model_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
+from acts_contrib.test_utils.tel.tel_test_utils import bring_up_sl4a
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import get_carrier_config_version
+from acts_contrib.test_utils.tel.tel_test_utils import get_carrier_id_version
+from acts_contrib.test_utils.tel.tel_test_utils import get_er_db_id_version
+from acts_contrib.test_utils.tel.tel_test_utils import get_database_content
+from acts_contrib.test_utils.tel.tel_test_utils import install_googleaccountutil_apk
+from acts_contrib.test_utils.tel.tel_test_utils import add_whitelisted_account
+from acts_contrib.test_utils.tel.tel_test_utils import adb_disable_verity
+from acts_contrib.test_utils.tel.tel_test_utils import install_carriersettings_apk
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import cleanup_configupdater
+from acts_contrib.test_utils.tel.tel_test_utils import pull_carrier_id_files
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_cbrs_and_default_sub_id
from acts.utils import get_current_epoch_time
from acts.keys import Config
diff --git a/acts_tests/tests/google/tel/live/TelLiveNoSimTest.py b/acts_tests/tests/google/tel/live/TelLiveNoSimTest.py
index c8b2b7f..9e4cc07 100644
--- a/acts_tests/tests/google/tel/live/TelLiveNoSimTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveNoSimTest.py
@@ -19,19 +19,19 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
-from acts.test_utils.tel.tel_defines import GEN_2G
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import SIM_STATE_ABSENT
-from acts.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import get_sim_state
-from acts.test_utils.tel.tel_lookup_tables import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import get_sim_state
+from acts_contrib.test_utils.tel.tel_lookup_tables import \
network_preference_for_generation
-from acts.test_utils.tel.tel_test_utils import reset_device_password
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import reset_device_password
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from TelLiveEmergencyBase import TelLiveEmergencyBase
diff --git a/acts_tests/tests/google/tel/live/TelLivePostflightTest.py b/acts_tests/tests/google/tel/live/TelLivePostflightTest.py
index 4ccbc8a..6c7b33b 100644
--- a/acts_tests/tests/google/tel/live/TelLivePostflightTest.py
+++ b/acts_tests/tests/google/tel/live/TelLivePostflightTest.py
@@ -20,7 +20,7 @@
from acts.asserts import fail
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
class TelLivePostflightTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLivePreflightTest.py b/acts_tests/tests/google/tel/live/TelLivePreflightTest.py
index 18da880..134255e 100644
--- a/acts_tests/tests/google/tel/live/TelLivePreflightTest.py
+++ b/acts_tests/tests/google/tel/live/TelLivePreflightTest.py
@@ -23,29 +23,29 @@
from acts.controllers.android_device import get_info
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import get_user_config_profile
-from acts.test_utils.tel.tel_test_utils import is_sim_locked
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import unlock_sim
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan_cellular_preferred
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_locked
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan_cellular_preferred
class TelLivePreflightTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLiveProjectFiTest.py b/acts_tests/tests/google/tel/live/TelLiveProjectFiTest.py
index a86939c..5bf765e 100644
--- a/acts_tests/tests/google/tel/live/TelLiveProjectFiTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveProjectFiTest.py
@@ -20,24 +20,24 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CARRIER_SPT
-from acts.test_utils.tel.tel_defines import CARRIER_TMO
-from acts.test_utils.tel.tel_defines import CARRIER_USCC
-from acts.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
-from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import is_sim_ready
-from acts.test_utils.tel.tel_test_utils import log_screen_shot
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import refresh_droid_config
-from acts.test_utils.tel.tel_test_utils import send_dialer_secret_code
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import add_google_account
-from acts.test_utils.tel.tel_test_utils import remove_google_account
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_SPT
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_TMO
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_USCC
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
+from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_ready
+from acts_contrib.test_utils.tel.tel_test_utils import log_screen_shot
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import refresh_droid_config
+from acts_contrib.test_utils.tel.tel_test_utils import send_dialer_secret_code
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import add_google_account
+from acts_contrib.test_utils.tel.tel_test_utils import remove_google_account
CARRIER_AUTO = "auto"
diff --git a/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py b/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py
index 1fd39eb..bbfea86 100644
--- a/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py
@@ -23,57 +23,57 @@
from acts import signals
from acts.test_decorators import test_tracker_info
from acts.controllers.sl4a_lib.sl4a_types import Sl4aNetworkInfo
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
-from acts.test_utils.tel.tel_defines import CAPABILITY_VT
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import CAPABILITY_OMADM
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
-from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_CRASH
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import get_model_name
-from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_test_utils import is_droid_in_network_generation
-from acts.test_utils.tel.tel_test_utils import is_sim_locked
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import power_off_sim
-from acts.test_utils.tel.tel_test_utils import power_on_sim
-from acts.test_utils.tel.tel_test_utils import reboot_device
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash
-from acts.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
-from acts.test_utils.tel.tel_test_utils import unlock_sim
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
-from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_OMADM
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_CRASH
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_model_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_locked
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import power_off_sim
+from acts_contrib.test_utils.tel.tel_test_utils import power_on_sim
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash
+from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
from acts.utils import get_current_epoch_time
diff --git a/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py b/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
index 10b045f..879353e 100644
--- a/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
@@ -24,23 +24,23 @@
from acts.keys import Config
from acts.utils import unzip_maintain_permissions
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
-from acts.test_utils.tel.tel_test_utils import dumpsys_carrier_config
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import flash_radio
-from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts.test_utils.tel.tel_test_utils import is_sim_locked
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import power_off_sim
-from acts.test_utils.tel.tel_test_utils import power_on_sim
-from acts.test_utils.tel.tel_test_utils import print_radio_info
-from acts.test_utils.tel.tel_test_utils import revert_default_telephony_setting
-from acts.test_utils.tel.tel_test_utils import set_qxdm_logger_command
-from acts.test_utils.tel.tel_test_utils import system_file_push
-from acts.test_utils.tel.tel_test_utils import unlock_sim
-from acts.test_utils.tel.tel_test_utils import verify_default_telephony_setting
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
+from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_carrier_config
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import flash_radio
+from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts_contrib.test_utils.tel.tel_test_utils import is_sim_locked
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import power_off_sim
+from acts_contrib.test_utils.tel.tel_test_utils import power_on_sim
+from acts_contrib.test_utils.tel.tel_test_utils import print_radio_info
+from acts_contrib.test_utils.tel.tel_test_utils import revert_default_telephony_setting
+from acts_contrib.test_utils.tel.tel_test_utils import set_qxdm_logger_command
+from acts_contrib.test_utils.tel.tel_test_utils import system_file_push
+from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
+from acts_contrib.test_utils.tel.tel_test_utils import verify_default_telephony_setting
from acts.utils import set_mobile_data_always_on
diff --git a/acts_tests/tests/google/tel/live/TelLiveSmokeTest.py b/acts_tests/tests/google/tel/live/TelLiveSmokeTest.py
index ac536cf..0b8ce5d 100644
--- a/acts_tests/tests/google/tel/live/TelLiveSmokeTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveSmokeTest.py
@@ -19,35 +19,35 @@
import time
from acts.keys import Config
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_data_utils import airplane_mode_test
-from acts.test_utils.tel.tel_data_utils import wifi_cell_switching
-from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
-from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_lookup_tables import is_rat_svd_capable
-from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import get_network_rat
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_data_utils import airplane_mode_test
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_cell_switching
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
+from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_lookup_tables import is_rat_svd_capable
+from acts_contrib.test_utils.tel.tel_test_utils import stop_wifi_tethering
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
from acts.utils import rand_ascii_str
SKIP = 'Skip'
diff --git a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
index 57c241c..3fa07b1 100644
--- a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
@@ -20,59 +20,59 @@
import time
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_GSM
-from acts.test_utils.tel.tel_defines import RAT_3G
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import mms_receive_verify_after_call_hangup
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
-from acts.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
-from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
-from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import setup_sim
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_3G
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import mms_receive_verify_after_call_hangup
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
+from acts_contrib.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
+from acts_contrib.test_utils.tel.tel_test_utils import set_call_state_listen_level
+from acts_contrib.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import setup_sim
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_in_collision_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import \
sms_rx_power_off_multiple_send_receive_verify
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import is_phone_in_call_video_bidirectional
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_data_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan_cellular_preferred
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_video_bidirectional
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_data_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan_cellular_preferred
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
from acts.utils import rand_ascii_str
SMS_OVER_WIFI_PROVIDERS = ("vzw", "tmo", "fi", "rogers", "rjio", "eeuk",
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py b/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
index 3f30cd2..dc7a62e 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
@@ -20,36 +20,36 @@
import collections
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import last_call_drop_reason
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import video_call_setup
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
from acts.logger import epoch_to_log_line_timestamp
from acts.utils import get_current_epoch_time
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressDataTest.py b/acts_tests/tests/google/tel/live/TelLiveStressDataTest.py
index b8012ae..81e3ece 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressDataTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressDataTest.py
@@ -17,9 +17,9 @@
Test Script for Telephony Stress data Test
"""
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
-from acts.test_utils.tel.tel_test_utils import iperf_udp_test_by_adb
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import iperf_udp_test_by_adb
class TelLiveStressDataTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py b/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
index ea0bbf8..46bef20 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
@@ -22,34 +22,34 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import CAPABILITY_OMADM
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_FDR
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import get_model_name
-from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import is_droid_in_network_generation
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import fastboot_wipe
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_OMADM
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_FDR
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_model_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressTest.py b/acts_tests/tests/google/tel/live/TelLiveStressTest.py
index 0bde255..497a44f 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressTest.py
@@ -27,78 +27,78 @@
from acts import signals
from acts.libs.proc import job
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
-from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GLOBAL
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_TDSCDMA_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_MESSAGE_SUB_ID
-from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_VOICE_SUB_ID
-from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_CBRS_DATA_SWITCH
-from acts.test_utils.tel.tel_defines import CARRIER_SING
-from acts.test_utils.tel.tel_lookup_tables import is_rat_svd_capable
-from acts.test_utils.tel.tel_test_utils import STORY_LINE
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import extract_test_log
-from acts.test_utils.tel.tel_test_utils import force_connectivity_metrics_upload
-from acts.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import last_call_drop_reason
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
-from acts.test_utils.tel.tel_test_utils import start_sdm_loggers
-from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import synchronize_device_time
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection_by_ping
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_call_id_clearing
-from acts.test_utils.tel.tel_test_utils import wait_for_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_in_call_active
-from acts.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_voice_utils import get_current_voice_rat
-from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_carrierid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import get_isopportunistic_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_message
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
-from acts.test_utils.tel.tel_subscription_utils import set_slways_allow_mms_data
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GLOBAL
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_TDSCDMA_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_MESSAGE_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_VOICE_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_CBRS_DATA_SWITCH
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_SING
+from acts_contrib.test_utils.tel.tel_lookup_tables import is_rat_svd_capable
+from acts_contrib.test_utils.tel.tel_test_utils import STORY_LINE
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import extract_test_log
+from acts_contrib.test_utils.tel.tel_test_utils import force_connectivity_metrics_upload
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import start_sdm_loggers
+from acts_contrib.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_test_utils import synchronize_device_time
+from acts_contrib.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection_by_ping
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_call_id_clearing
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_in_call_active
+from acts_contrib.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import get_current_voice_rat
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_carrierid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_isopportunistic_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_message
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
+from acts_contrib.test_utils.tel.tel_subscription_utils import set_slways_allow_mms_data
from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
diff --git a/acts_tests/tests/google/tel/live/TelLiveVideoDataTest.py b/acts_tests/tests/google/tel/live/TelLiveVideoDataTest.py
index 18dd7f7..8f86f9f 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVideoDataTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVideoDataTest.py
@@ -16,15 +16,15 @@
"""
Test Script for VT Data test
"""
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
class TelLiveVideoDataTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLiveVideoTest.py b/acts_tests/tests/google/tel/live/TelLiveVideoTest.py
index 107e05f..de2e85a 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVideoTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVideoTest.py
@@ -21,58 +21,58 @@
from queue import Empty
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
-from acts.test_utils.tel.tel_defines import CAPABILITY_VT
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
-from acts.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
-from acts.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
-from acts.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
-from acts.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_EVENT
-from acts.test_utils.tel.tel_defines import EventTelecomVideoCallSessionEvent
-from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_PAUSE
-from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_RESUME
-from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import disconnect_call_by_id
-from acts.test_utils.tel.tel_test_utils import get_model_name
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import wait_for_video_enabled
-from acts.test_utils.tel.tel_test_utils import get_capability_for_subscription
-from acts.test_utils.tel.tel_video_utils import get_call_id_in_video_state
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
+from acts_contrib.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_EVENT
+from acts_contrib.test_utils.tel.tel_defines import EventTelecomVideoCallSessionEvent
+from acts_contrib.test_utils.tel.tel_defines import SESSION_EVENT_RX_PAUSE
+from acts_contrib.test_utils.tel.tel_defines import SESSION_EVENT_RX_RESUME
+from acts_contrib.test_utils.tel.tel_lookup_tables import operator_capabilities
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import disconnect_call_by_id
+from acts_contrib.test_utils.tel.tel_test_utils import get_model_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_video_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_video_utils import get_call_id_in_video_state
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
-from acts.test_utils.tel.tel_video_utils import is_phone_in_call_voice_hd
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_voice_hd
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import \
verify_video_call_in_expected_state
-from acts.test_utils.tel.tel_video_utils import video_call_downgrade
-from acts.test_utils.tel.tel_video_utils import video_call_modify_video
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
-from acts.test_utils.tel.tel_voice_utils import get_audio_route
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import set_audio_route
-from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_downgrade
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_modify_video
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import get_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60 # default 1 hour
diff --git a/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py b/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
index ae7e18f..ab7ab00 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
@@ -20,50 +20,50 @@
import time
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
-from acts.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_GSM
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import call_reject
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import get_call_uri
-from acts.test_utils.tel.tel_test_utils import get_phone_number
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import is_uri_equivalent
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts.test_utils.tel.tel_test_utils import get_capability_for_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_wcdma
-from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import swap_calls
-from acts.test_utils.tel.tel_voice_utils import three_phone_call_forwarding_short_seq
-from acts.test_utils.tel.tel_voice_utils import three_phone_call_waiting_short_seq
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_GSM
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import call_reject
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import get_call_uri
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_uri_equivalent
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_wcdma
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_forwarding_short_seq
+from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_waiting_short_seq
class TelLiveVoiceConfTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
index 94d9146..a129398 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
@@ -21,83 +21,83 @@
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
-from acts.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_data_utils import wifi_cell_switching
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import GEN_2G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
-from acts.test_utils.tel.tel_defines import PHONE_TYPE_GSM
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_data_utils import wifi_cell_switching
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_GSM
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import \
+from acts_contrib.test_utils.tel.tel_subscription_utils import \
get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import \
call_voicemail_erase_all_pending_voicemail
-from acts.test_utils.tel.tel_test_utils import active_file_download_task
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_task
from acts.utils import adb_shell_ping
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call_active
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_test_utils import start_youtube_video
-from acts.test_utils.tel.tel_test_utils import set_wifi_to_default
-from acts.test_utils.tel.tel_test_utils import STORY_LINE
-from acts.test_utils.tel.tel_test_utils import wait_for_in_call_active
-from acts.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
-from acts.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_wcdma
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import \
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call_active
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ringing_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_test_utils import start_youtube_video
+from acts_contrib.test_utils.tel.tel_test_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_test_utils import STORY_LINE
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_in_call_active
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
+from acts_contrib.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_wcdma
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import \
phone_setup_iwlan_cellular_preferred
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_2g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_leave_voice_mail
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_long_seq
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_2g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_leave_voice_mail
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_long_seq
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60 # default value 1 hour
DEFAULT_PING_DURATION = 120 # in seconds
diff --git a/acts_tests/tests/google/tel/live/TelWifiDataTest.py b/acts_tests/tests/google/tel/live/TelWifiDataTest.py
index fdc7333..4672feb 100644
--- a/acts_tests/tests/google/tel/live/TelWifiDataTest.py
+++ b/acts_tests/tests/google/tel/live/TelWifiDataTest.py
@@ -17,24 +17,24 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_atten_utils import set_rssi
-from acts.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts.test_utils.tel.tel_test_utils import get_wifi_signal_strength
-from acts.test_utils.tel.tel_test_utils import reboot_device
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_atten_utils import set_rssi
+from acts_contrib.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import run_multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import get_wifi_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
from acts.utils import adb_shell_ping
# Attenuator name
diff --git a/acts_tests/tests/google/tel/live/TelWifiVideoTest.py b/acts_tests/tests/google/tel/live/TelWifiVideoTest.py
index 360b09c..8f32038 100644
--- a/acts_tests/tests/google/tel/live/TelWifiVideoTest.py
+++ b/acts_tests/tests/google/tel/live/TelWifiVideoTest.py
@@ -20,56 +20,56 @@
import time
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
-from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
-from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
-from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
-from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
-from acts.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
-from acts.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
-from acts.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
-from acts.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_EVENT
-from acts.test_utils.tel.tel_defines import EventTelecomVideoCallSessionEvent
-from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_PAUSE
-from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_RESUME
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import disconnect_call_by_id
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_test_utils import num_active_calls
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import wait_for_video_enabled
-from acts.test_utils.tel.tel_video_utils import get_call_id_in_video_state
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
+from acts_contrib.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_EVENT
+from acts_contrib.test_utils.tel.tel_defines import EventTelecomVideoCallSessionEvent
+from acts_contrib.test_utils.tel.tel_defines import SESSION_EVENT_RX_PAUSE
+from acts_contrib.test_utils.tel.tel_defines import SESSION_EVENT_RX_RESUME
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_test_utils import disconnect_call_by_id
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_video_enabled
+from acts_contrib.test_utils.tel.tel_video_utils import get_call_id_in_video_state
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.tel_video_utils import \
is_phone_in_call_viwifi_bidirectional
-from acts.test_utils.tel.tel_video_utils import is_phone_in_call_voice_hd
-from acts.test_utils.tel.tel_video_utils import phone_setup_video
-from acts.test_utils.tel.tel_video_utils import \
+from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_voice_hd
+from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video
+from acts_contrib.test_utils.tel.tel_video_utils import \
verify_video_call_in_expected_state
-from acts.test_utils.tel.tel_video_utils import video_call_downgrade
-from acts.test_utils.tel.tel_video_utils import video_call_modify_video
-from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
-from acts.test_utils.tel.tel_voice_utils import get_audio_route
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import set_audio_route
-from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_downgrade
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_modify_video
+from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import get_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60 # default 1 hour
diff --git a/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py b/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py
index 9d0e4c5..1b177836 100644
--- a/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py
@@ -20,75 +20,75 @@
import time
from queue import Empty
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_atten_utils import set_rssi
-from acts.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
-from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
-from acts.test_utils.tel.tel_defines import GEN_3G
-from acts.test_utils.tel.tel_defines import GEN_4G
-from acts.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
-from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
-from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_IWLAN
-from acts.test_utils.tel.tel_defines import RAT_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
-from acts.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
-from acts.test_utils.tel.tel_defines import EventNetworkCallback
-from acts.test_utils.tel.tel_defines import NetworkCallbackAvailable
-from acts.test_utils.tel.tel_defines import NetworkCallbackLost
-from acts.test_utils.tel.tel_defines import SignalStrengthContainer
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import get_network_rat
-from acts.test_utils.tel.tel_test_utils import get_phone_number
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_network_call_back_event_match
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import is_phone_not_in_call
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
-from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts.test_utils.tel.tel_test_utils import get_wifi_signal_strength
-from acts.test_utils.tel.tel_test_utils import wait_for_state
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_atten_utils import set_rssi
+from acts_contrib.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import CELL_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import INVALID_WIFI_RSSI
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
+from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_SCREEN_ON
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
+from acts_contrib.test_utils.tel.tel_defines import EventNetworkCallback
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackAvailable
+from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackLost
+from acts_contrib.test_utils.tel.tel_defines import SignalStrengthContainer
+from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_network_call_back_event_match
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import is_phone_not_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import get_wifi_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_csfb
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_volte
# Attenuator name
ATTEN_NAME_FOR_WIFI_2G = 'wifi0'
diff --git a/acts_tests/tests/google/tel/live/msim/TelLiveMSIMSmsTest.py b/acts_tests/tests/google/tel/live/msim/TelLiveMSIMSmsTest.py
index 336ae01..ad6e713 100644
--- a/acts_tests/tests/google/tel/live/msim/TelLiveMSIMSmsTest.py
+++ b/acts_tests/tests/google/tel/live/msim/TelLiveMSIMSmsTest.py
@@ -15,16 +15,16 @@
# limitations under the License.
import time
-from acts.test_utils.tel.tel_test_utils \
+from acts_contrib.test_utils.tel.tel_test_utils \
import sms_send_receive_verify, multithread_func
from acts.utils import rand_ascii_str
-from acts.test_utils.tel.tel_subscription_utils \
+from acts_contrib.test_utils.tel.tel_subscription_utils \
import get_subid_from_slot_index, set_subid_for_message
-from acts.test_utils.tel.tel_defines \
+from acts_contrib.test_utils.tel.tel_defines \
import MULTI_SIM_CONFIG, WAIT_TIME_ANDROID_STATE_SETTLING
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_voice_utils \
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_voice_utils \
import phone_setup_voice_general_for_slot
diff --git a/acts_tests/tests/google/tel/live/msim/TelLiveMSIMVoiceTest.py b/acts_tests/tests/google/tel/live/msim/TelLiveMSIMVoiceTest.py
index f4cb7ca..501b0d6 100644
--- a/acts_tests/tests/google/tel/live/msim/TelLiveMSIMVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/msim/TelLiveMSIMVoiceTest.py
@@ -14,12 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from acts.test_utils.tel.tel_voice_utils \
+from acts_contrib.test_utils.tel.tel_voice_utils \
import two_phone_call_msim_short_seq, phone_setup_voice_general_for_slot
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_test_utils import multithread_func
-from acts.test_utils.tel.tel_defines import MULTI_SIM_CONFIG
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import multithread_func
+from acts_contrib.test_utils.tel.tel_defines import MULTI_SIM_CONFIG
class TelLiveMSIMVoiceTest(TelephonyBaseTest):
diff --git a/acts_tests/tests/google/usb/UsbTetheringFunctionsTest.py b/acts_tests/tests/google/usb/UsbTetheringFunctionsTest.py
index 3f0393b..14eb381 100644
--- a/acts_tests/tests/google/usb/UsbTetheringFunctionsTest.py
+++ b/acts_tests/tests/google/usb/UsbTetheringFunctionsTest.py
@@ -23,11 +23,11 @@
from acts.libs.proc import job
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.tel import tel_test_utils as tutils
-from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.anritsu_utils import wait_for_sms_sent_success
-from acts.test_utils.tel.tel_defines import EventMmsSentSuccess
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_test_utils as tutils
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel.anritsu_utils import wait_for_sms_sent_success
+from acts_contrib.test_utils.tel.tel_defines import EventMmsSentSuccess
# Time it takes for the usb tethering IP to
# show up in ifconfig and function waiting.
diff --git a/acts_tests/tests/google/wifi/WifiAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py
index 829fc6c..2fde995 100755
--- a/acts_tests/tests/google/wifi/WifiAutoJoinTest.py
+++ b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py
@@ -18,8 +18,8 @@
from acts import asserts
from acts import base_test
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
WifiEnums = wutils.WifiEnums
NETWORK_ID_ERROR = "Network don't have ID"
diff --git a/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py
index 8e20c81..04cc45a 100755
--- a/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py
+++ b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py
@@ -20,9 +20,9 @@
from acts.libs.ota import ota_updater
import acts.signals as signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-import acts.test_utils.wifi.wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
import acts.utils as utils
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py b/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py
index f76331e..5b6ab41 100644
--- a/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py
@@ -18,12 +18,12 @@
import random
import re
import logging
-import acts.test_utils.wifi.wifi_test_utils as wutils
-import acts.test_utils.tel.tel_test_utils as tel_utils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.tel.tel_test_utils as tel_utils
import acts.utils as utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts import signals
from acts.controllers import packet_capture
from acts.controllers.ap_lib.hostapd_constants import BAND_2G
diff --git a/acts_tests/tests/google/wifi/WifiChaosTest.py b/acts_tests/tests/google/wifi/WifiChaosTest.py
index d0a3722..f73ca6c 100755
--- a/acts_tests/tests/google/wifi/WifiChaosTest.py
+++ b/acts_tests/tests/google/wifi/WifiChaosTest.py
@@ -21,15 +21,15 @@
import acts.controllers.packet_capture as packet_capture
import acts.signals as signals
-import acts.test_utils.wifi.rpm_controller_utils as rutils
-import acts.test_utils.wifi.wifi_datastore_utils as dutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.rpm_controller_utils as rutils
+import acts_contrib.test_utils.wifi.wifi_datastore_utils as dutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.base_test import BaseTestClass
from acts.controllers.ap_lib import hostapd_constants
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
index bd10a8b..98061fd 100644
--- a/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
+++ b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
@@ -21,13 +21,13 @@
import acts.base_test
import acts.signals as signals
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils as utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
# Default timeout used for reboot, toggle WiFi and Airplane mode,
diff --git a/acts_tests/tests/google/wifi/WifiCrashStressTest.py b/acts_tests/tests/google/wifi/WifiCrashStressTest.py
index 837112a..5f61c63 100644
--- a/acts_tests/tests/google/wifi/WifiCrashStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiCrashStressTest.py
@@ -16,12 +16,12 @@
import time
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.tel.tel_test_utils import disable_qxdm_logger
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import disable_qxdm_logger
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiCrashTest.py b/acts_tests/tests/google/wifi/WifiCrashTest.py
index f76b1ed..86b5e95 100644
--- a/acts_tests/tests/google/wifi/WifiCrashTest.py
+++ b/acts_tests/tests/google/wifi/WifiCrashTest.py
@@ -21,12 +21,12 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
# Timeout used for crash recovery.
diff --git a/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
index 8ef24bd..d72e066 100644
--- a/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
+++ b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
@@ -21,12 +21,12 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiDppTest.py b/acts_tests/tests/google/wifi/WifiDppTest.py
index 36d265e..6252cbf 100644
--- a/acts_tests/tests/google/wifi/WifiDppTest.py
+++ b/acts_tests/tests/google/wifi/WifiDppTest.py
@@ -21,10 +21,10 @@
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
class WifiDppTest(WifiBaseTest):
"""This class tests the DPP API surface.
diff --git a/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py
index 0cebd42..70de1f5 100644
--- a/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py
+++ b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py
@@ -20,8 +20,8 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiEnterpriseTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py
index 20f5e9f..f3a88d9 100644
--- a/acts_tests/tests/google/wifi/WifiEnterpriseTest.py
+++ b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py
@@ -21,10 +21,10 @@
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net.net_test_utils import start_tcpdump
-from acts.test_utils.net.net_test_utils import stop_tcpdump
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump
+from acts_contrib.test_utils.net.net_test_utils import stop_tcpdump
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py
index a68144d..9dd73c3 100644
--- a/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py
+++ b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py
@@ -21,12 +21,12 @@
import acts.base_test
import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class WifiHiddenSSIDTest(WifiBaseTest):
diff --git a/acts_tests/tests/google/wifi/WifiIFSTwTest.py b/acts_tests/tests/google/wifi/WifiIFSTwTest.py
index 5b1603d..29a131d 100644
--- a/acts_tests/tests/google/wifi/WifiIFSTwTest.py
+++ b/acts_tests/tests/google/wifi/WifiIFSTwTest.py
@@ -25,10 +25,10 @@
from acts.controllers import attenuator
from acts.controllers.sl4a_lib import rpc_client
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net.net_test_utils import start_tcpdump, stop_tcpdump
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net.net_test_utils import start_tcpdump, stop_tcpdump
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.wifi_test_utils import WifiEnums
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import stop_standing_subprocess
TCPDUMP_PATH = '/data/local/tmp/tcpdump'
diff --git a/acts_tests/tests/google/wifi/WifiIOTTest.py b/acts_tests/tests/google/wifi/WifiIOTTest.py
index 1daf346..bbf3a71 100644
--- a/acts_tests/tests/google/wifi/WifiIOTTest.py
+++ b/acts_tests/tests/google/wifi/WifiIOTTest.py
@@ -19,11 +19,11 @@
import time
import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py
index c6f8c3d..c300804 100644
--- a/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py
+++ b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py
@@ -19,11 +19,11 @@
import time
import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.controllers import iperf_server as ipf
import json
diff --git a/acts_tests/tests/google/wifi/WifiIOTtpeTest.py b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py
index fd141ff..98729f2 100644
--- a/acts_tests/tests/google/wifi/WifiIOTtpeTest.py
+++ b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py
@@ -19,11 +19,11 @@
import time
import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiLinkProbeTest.py b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py
index 5f00e6c..b0312ae 100644
--- a/acts_tests/tests/google/wifi/WifiLinkProbeTest.py
+++ b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py
@@ -18,8 +18,8 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
WifiEnums = wutils.WifiEnums
NUM_LINK_PROBES = 8
diff --git a/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
index 64990d3..878a0b9 100644
--- a/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
+++ b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
@@ -22,14 +22,14 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from scapy.all import *
from acts.controllers.ap_lib import hostapd_constants
diff --git a/acts_tests/tests/google/wifi/WifiManagerTest.py b/acts_tests/tests/google/wifi/WifiManagerTest.py
index 9d7f8a0..0f6c902 100644
--- a/acts_tests/tests/google/wifi/WifiManagerTest.py
+++ b/acts_tests/tests/google/wifi/WifiManagerTest.py
@@ -21,14 +21,14 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
# Default timeout used for reboot, toggle WiFi and Airplane mode,
diff --git a/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py
index 7a1d8bc..b3bccf4 100644
--- a/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py
+++ b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py
@@ -21,14 +21,14 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.controllers.android_device import SL4A_APK_NAME
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_constants
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
index 41f723c..a50b17b 100644
--- a/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
+++ b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -24,8 +24,8 @@
from acts.controllers import android_device
from acts.controllers import attenuator
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
index 3f989b5..c6cba50 100644
--- a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
+++ b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
@@ -21,14 +21,14 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.controllers.android_device import SL4A_APK_NAME
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_constants
WifiEnums = wutils.WifiEnums
# EAP Macros
diff --git a/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py
index b5ba899..6be4e5c 100644
--- a/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py
+++ b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py
@@ -19,9 +19,9 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
NETWORK_ID_ERROR = "Network don't have ID"
diff --git a/acts_tests/tests/google/wifi/WifiPasspointTest.py b/acts_tests/tests/google/wifi/WifiPasspointTest.py
index 54c13d5..ff757bf 100755
--- a/acts_tests/tests/google/wifi/WifiPasspointTest.py
+++ b/acts_tests/tests/google/wifi/WifiPasspointTest.py
@@ -20,7 +20,7 @@
import time
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
@@ -28,8 +28,8 @@
from acts.libs.uicd.uicd_cli import UicdCli
from acts.libs.uicd.uicd_cli import UicdError
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.utils import force_airplane_mode
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py
index 35f2966..27703d2 100644
--- a/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py
+++ b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py
@@ -16,8 +16,8 @@
from acts import base_test
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
class WifiPerformancePreflightTest(base_test.BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiPingTest.py b/acts_tests/tests/google/wifi/WifiPingTest.py
index 1df52d5..0fb3823 100644
--- a/acts_tests/tests/google/wifi/WifiPingTest.py
+++ b/acts_tests/tests/google/wifi/WifiPingTest.py
@@ -26,11 +26,11 @@
from acts import utils
from acts.controllers.utils_lib import ssh
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_chamber
-from acts.test_utils.wifi import ota_sniffer
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import ota_chamber
+from acts_contrib.test_utils.wifi import ota_sniffer
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from functools import partial
diff --git a/acts_tests/tests/google/wifi/WifiPnoTest.py b/acts_tests/tests/google/wifi/WifiPnoTest.py
index ba2eb4e..cbbf3e4 100644
--- a/acts_tests/tests/google/wifi/WifiPnoTest.py
+++ b/acts_tests/tests/google/wifi/WifiPnoTest.py
@@ -18,8 +18,8 @@
from acts import asserts
from acts import base_test
from acts.test_decorators import test_tracker_info
-import acts.test_utils.wifi.wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
MAX_ATTN = 95
diff --git a/acts_tests/tests/google/wifi/WifiPreFlightTest.py b/acts_tests/tests/google/wifi/WifiPreFlightTest.py
index 14a4190..3d0db2d 100644
--- a/acts_tests/tests/google/wifi/WifiPreFlightTest.py
+++ b/acts_tests/tests/google/wifi/WifiPreFlightTest.py
@@ -19,11 +19,11 @@
import time
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
SCAN_TIME = 30
WAIT_TIME = 2
diff --git a/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py
index bfc96d5..e011866 100644
--- a/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py
+++ b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py
@@ -26,9 +26,9 @@
from acts.controllers import iperf_server as ipf
from acts.controllers.utils_lib import ssh
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
SHORT_SLEEP = 1
MED_SLEEP = 5
diff --git a/acts_tests/tests/google/wifi/WifiRoamingTest.py b/acts_tests/tests/google/wifi/WifiRoamingTest.py
index b0f1a71..ebbc664 100644
--- a/acts_tests/tests/google/wifi/WifiRoamingTest.py
+++ b/acts_tests/tests/google/wifi/WifiRoamingTest.py
@@ -23,8 +23,8 @@
from acts import base_test
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
DEF_ATTN = 60
diff --git a/acts_tests/tests/google/wifi/WifiRssiTest.py b/acts_tests/tests/google/wifi/WifiRssiTest.py
index 9ff9afa..3055985 100644
--- a/acts_tests/tests/google/wifi/WifiRssiTest.py
+++ b/acts_tests/tests/google/wifi/WifiRssiTest.py
@@ -29,10 +29,10 @@
from acts.controllers.utils_lib import ssh
from acts.controllers import iperf_server as ipf
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_chamber
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import ota_chamber
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from concurrent.futures import ThreadPoolExecutor
from functools import partial
diff --git a/acts_tests/tests/google/wifi/WifiRttManagerTest.py b/acts_tests/tests/google/wifi/WifiRttManagerTest.py
index f0985de..4d558e6 100644
--- a/acts_tests/tests/google/wifi/WifiRttManagerTest.py
+++ b/acts_tests/tests/google/wifi/WifiRttManagerTest.py
@@ -18,7 +18,7 @@
import queue
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.controllers.sl4a_lib import rpc_client
diff --git a/acts_tests/tests/google/wifi/WifiRvrTest.py b/acts_tests/tests/google/wifi/WifiRvrTest.py
index 979ac76..ae839df 100644
--- a/acts_tests/tests/google/wifi/WifiRvrTest.py
+++ b/acts_tests/tests/google/wifi/WifiRvrTest.py
@@ -27,11 +27,11 @@
from acts.controllers import iperf_server as ipf
from acts.controllers.utils_lib import ssh
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_chamber
-from acts.test_utils.wifi import ota_sniffer
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import ota_chamber
+from acts_contrib.test_utils.wifi import ota_sniffer
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from functools import partial
diff --git a/acts_tests/tests/google/wifi/WifiRvrTwTest.py b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
index 2f9dc12..e732b83 100644
--- a/acts_tests/tests/google/wifi/WifiRvrTwTest.py
+++ b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
@@ -19,11 +19,11 @@
import time
import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from acts.controllers import iperf_server as ipf
import json
diff --git a/acts_tests/tests/google/wifi/WifiScannerBssidTest.py b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py
index e91c449..dd75444 100644
--- a/acts_tests/tests/google/wifi/WifiScannerBssidTest.py
+++ b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py
@@ -21,7 +21,7 @@
from acts import base_test
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
BSSID_EVENT_WAIT = 30
diff --git a/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py
index aebcaf7..548c6eb 100755
--- a/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py
+++ b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -21,8 +21,8 @@
from acts import base_test
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiChannelUS = wutils.WifiChannelUS
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiScannerScanTest.py b/acts_tests/tests/google/wifi/WifiScannerScanTest.py
index 89ce0d6..4034f76 100755
--- a/acts_tests/tests/google/wifi/WifiScannerScanTest.py
+++ b/acts_tests/tests/google/wifi/WifiScannerScanTest.py
@@ -23,9 +23,9 @@
from acts import base_test
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
SCANTIME = 10000 #framework support only 10s as minimum scan interval
NUMBSSIDPERSCAN = 8
diff --git a/acts_tests/tests/google/wifi/WifiSensitivityTest.py b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
index 7307850..3172aaa 100644
--- a/acts_tests/tests/google/wifi/WifiSensitivityTest.py
+++ b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
@@ -27,10 +27,10 @@
from acts.controllers import iperf_client
from acts.controllers.utils_lib import ssh
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_chamber
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import ota_chamber
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
from functools import partial
from WifiRvrTest import WifiRvrTest
from WifiPingTest import WifiPingTest
diff --git a/acts_tests/tests/google/wifi/WifiServiceApiTest.py b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
index b5eed89..f34f3eb 100644
--- a/acts_tests/tests/google/wifi/WifiServiceApiTest.py
+++ b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
@@ -23,8 +23,8 @@
from acts import signals
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
class WifiServiceApiTest(base_test.BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
index 22d08b1..26bb862 100644
--- a/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
+++ b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
@@ -22,15 +22,15 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils as utils
from acts import asserts
from acts.controllers.ap_lib import hostapd_constants
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from threading import Thread
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py
index 37a2ca8..841eeff 100644
--- a/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py
+++ b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py
@@ -22,10 +22,10 @@
from acts.controllers import iperf_server as ipf
from acts.controllers import iperf_client as ipc
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_sniffer
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import ota_sniffer
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
from WifiRvrTest import WifiRvrTest
AccessPointTuple = collections.namedtuple(('AccessPointTuple'),
diff --git a/acts_tests/tests/google/wifi/WifiSoftApTest.py b/acts_tests/tests/google/wifi/WifiSoftApTest.py
index 7c7840d..073c98f 100644
--- a/acts_tests/tests/google/wifi/WifiSoftApTest.py
+++ b/acts_tests/tests/google/wifi/WifiSoftApTest.py
@@ -22,16 +22,16 @@
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import arduino_test_utils as dutils
-from acts.test_utils.net import socket_test_utils as sutils
-from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel import tel_test_utils as tel_utils
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_AUTO
-from acts.test_utils.wifi import wifi_constants
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.net import arduino_test_utils as dutils
+from acts_contrib.test_utils.net import socket_test_utils as sutils
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel import tel_test_utils as tel_utils
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_AUTO
+from acts_contrib.test_utils.wifi import wifi_constants
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
index f84c06c..7a10f03 100755
--- a/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
@@ -21,10 +21,10 @@
from acts import signals
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
from WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py
index d44b7dc..fd8e27f 100644
--- a/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py
+++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py
@@ -23,12 +23,12 @@
from acts.controllers.ap_lib import hostapd_constants
import acts.signals as signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-import acts.test_utils.wifi.wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
import acts.utils as utils
-import acts.test_utils.tel.tel_test_utils as tel_utils
+import acts_contrib.test_utils.tel.tel_test_utils as tel_utils
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiStressTest.py b/acts_tests/tests/google/wifi/WifiStressTest.py
index e31adbb..d02d78a 100644
--- a/acts_tests/tests/google/wifi/WifiStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiStressTest.py
@@ -20,16 +20,16 @@
import time
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
-import acts.test_utils.tel.tel_test_utils as tutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.tel.tel_test_utils as tutils
from acts import asserts
from acts import signals
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
WAIT_FOR_AUTO_CONNECT = 40
diff --git a/acts_tests/tests/google/wifi/WifiTeleCoexTest.py b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
index 6d5fef8..9e93a9d 100644
--- a/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
+++ b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
@@ -4,16 +4,16 @@
import time
import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wifi_utils
-import acts.test_utils.tel.tel_test_utils as tele_utils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wifi_utils
+import acts_contrib.test_utils.tel.tel_test_utils as tele_utils
import acts.utils
from acts import asserts
from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
WifiEnums = wifi_utils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py
index 0716158..0790546 100755
--- a/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py
+++ b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py
@@ -20,10 +20,10 @@
from acts.base_test import BaseTestClass
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-import acts.test_utils.net.net_test_utils as nutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.net.net_test_utils as nutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
class WifiTethering2GOpenOTATest(BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py
index 7399e32..c0b6d28 100755
--- a/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py
+++ b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py
@@ -20,10 +20,10 @@
from acts.base_test import BaseTestClass
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-import acts.test_utils.net.net_test_utils as nutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.net.net_test_utils as nutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
class WifiTethering2GPskOTATest(BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py
index 985e7a7..12f6824 100755
--- a/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py
+++ b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py
@@ -20,10 +20,10 @@
from acts.base_test import BaseTestClass
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-import acts.test_utils.net.net_test_utils as nutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.net.net_test_utils as nutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
class WifiTethering5GOpenOTATest(BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py
index 9e68f22..de0f901 100755
--- a/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py
+++ b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py
@@ -20,10 +20,10 @@
from acts.base_test import BaseTestClass
from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-import acts.test_utils.net.net_test_utils as nutils
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.net.net_test_utils as nutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
class WifiTethering5GPskOTATest(BaseTestClass):
diff --git a/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py
index dd3cf74..577e100 100644
--- a/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py
+++ b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py
@@ -23,11 +23,11 @@
from acts.controllers import adb
from acts.controllers import monsoon
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.tel import tel_data_utils as tel_utils
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.tel.tel_test_utils import http_file_download_by_chrome
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_data_utils as tel_utils
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_test_utils import http_file_download_by_chrome
from acts.utils import force_airplane_mode
from acts.utils import set_adaptive_brightness
from acts.utils import set_ambient_display
diff --git a/acts_tests/tests/google/wifi/WifiTetheringTest.py b/acts_tests/tests/google/wifi/WifiTetheringTest.py
index 2ad29e4..8a70d11 100644
--- a/acts_tests/tests/google/wifi/WifiTetheringTest.py
+++ b/acts_tests/tests/google/wifi/WifiTetheringTest.py
@@ -24,16 +24,16 @@
from acts import utils
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import arduino_test_utils as dutils
-from acts.test_utils.net import net_test_utils as nutils
-from acts.test_utils.net import socket_test_utils as sutils
-from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
-from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.net import arduino_test_utils as dutils
+from acts_contrib.test_utils.net import net_test_utils as nutils
+from acts_contrib.test_utils.net import socket_test_utils as sutils
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
WAIT_TIME = 5
diff --git a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
index 71ed011..44b4686 100644
--- a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -28,10 +28,10 @@
from acts.controllers import iperf_server as ipf
from acts.controllers.utils_lib import ssh
from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
-from acts.test_utils.wifi import ota_chamber
-from acts.test_utils.wifi import wifi_performance_test_utils as wputils
-from acts.test_utils.wifi import wifi_retail_ap as retail_ap
-from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import ota_chamber
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi import wifi_retail_ap as retail_ap
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
from functools import partial
TEST_TIMEOUT = 10
diff --git a/acts_tests/tests/google/wifi/WifiWakeTest.py b/acts_tests/tests/google/wifi/WifiWakeTest.py
index 7495863..f16f44f 100644
--- a/acts_tests/tests/google/wifi/WifiWakeTest.py
+++ b/acts_tests/tests/google/wifi/WifiWakeTest.py
@@ -20,8 +20,8 @@
from acts import asserts
from acts.controllers.android_device import SL4A_APK_NAME
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py b/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py
index 8e1395b..4c2639e 100644
--- a/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py
+++ b/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py
@@ -16,8 +16,8 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-import acts.test_utils.wifi.wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/WifiWpa3OweTest.py b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py
index ab0f23d..2fc0356 100644
--- a/acts_tests/tests/google/wifi/WifiWpa3OweTest.py
+++ b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py
@@ -21,13 +21,13 @@
import acts.base_test
import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
from acts import asserts
from acts.controllers.ap_lib import hostapd_constants
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
diff --git a/acts_tests/tests/google/wifi/aware/functional/AttachTest.py b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
index 0df82a1..89a9bdc 100644
--- a/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
@@ -19,10 +19,10 @@
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class AttachTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py
index 3bed77f..6f7c10d 100644
--- a/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py
@@ -17,10 +17,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class CapabilitiesTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py
index b8ac693..145b1a6 100644
--- a/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py
@@ -18,12 +18,12 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class DataPathTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
index 31255af..6c4e20f 100644
--- a/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -19,10 +19,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class DiscoveryTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
index 3898832..d800755 100644
--- a/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
@@ -16,14 +16,14 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
from acts.controllers.ap_lib.hostapd_constants import BAND_2G
from acts.controllers.ap_lib.hostapd_constants import BAND_5G
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
from scapy.all import *
diff --git a/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py
index 6c8d5b1..93bf346 100644
--- a/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py
@@ -18,10 +18,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class MacRandomTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py
index e008e12..50eb710 100644
--- a/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py
@@ -20,9 +20,9 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class MatchFilterTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/MessageTest.py b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py
index 040f4e4..1e013c5 100644
--- a/acts_tests/tests/google/wifi/aware/functional/MessageTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py
@@ -19,10 +19,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class MessageTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py
index 51f4cae..56a6547 100644
--- a/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py
@@ -20,11 +20,11 @@
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_constants as wconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi import wifi_constants as wconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
# arbitrary timeout for events
EVENT_TIMEOUT = 10
diff --git a/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py
index 15e84ff..9bdbebb 100644
--- a/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py
@@ -16,10 +16,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import nsd_const as nconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import nsd_const as nconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class ProtocolsTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py
index e29cd71..81d8a49 100644
--- a/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py
+++ b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py
@@ -17,9 +17,9 @@
import time
from acts import asserts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class ServiceIdsTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
index 8ebff89..871602b 100644
--- a/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
+++ b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
@@ -18,10 +18,10 @@
import time
from acts import asserts
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class LatencyTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py
index 2dab276..7ab4506 100644
--- a/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py
+++ b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -21,10 +21,10 @@
import time
from acts import asserts
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class ThroughputTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py
index d2e95df..73e2ffa 100644
--- a/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py
+++ b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py
@@ -19,10 +19,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class DataPathStressTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py
index 55545ea..5643755 100644
--- a/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py
+++ b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py
@@ -18,9 +18,9 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class DiscoveryStressTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py
index 58e2c84..b37c908 100644
--- a/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py
+++ b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py
@@ -18,10 +18,10 @@
import threading
from acts import asserts
-from acts.test_utils.wifi import wifi_constants as wconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi import wifi_constants as wconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
class InfraAssociationStressTest(AwareBaseTest):
diff --git a/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py
index 153a81f..2112782 100644
--- a/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py
+++ b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py
@@ -18,9 +18,9 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
KEY_ID = "id"
KEY_TX_OK_COUNT = "tx_ok_count"
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py
index dd27f21..9253db5 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
import time
@@ -22,9 +22,9 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
-from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
-from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+from acts_contrib.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC
WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py
index b043eb9..874e295 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py
@@ -17,9 +17,9 @@
import time
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
-from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
-from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+from acts_contrib.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
class WifiP2pLocalServiceTest(WifiP2pBaseTest):
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py
index 6bda400..d3aeadf 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
import time
@@ -22,9 +22,9 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
-from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
-from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+from acts_contrib.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC
WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py
index 2951be6..df57b60 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
import time
@@ -22,10 +22,10 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
-from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
-from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC
WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
index 2db5273..fa4f53f 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
import time
import re
@@ -23,9 +23,9 @@
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
-from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
-from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+from acts_contrib.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
from acts.controllers.ap_lib.hostapd_constants import BAND_2G
from scapy.all import *
diff --git a/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
index da0ba4b..5f3f91b 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
@@ -19,13 +19,13 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.net import connectivity_const as cconsts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.net import connectivity_const as cconsts
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py
index c68ca92..b21ef55 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py
@@ -15,10 +15,10 @@
# limitations under the License.
from acts import asserts
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RangeApMiscTest(RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
index 1ffbb02..a963779 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
@@ -16,10 +16,10 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RangeApNonSupporting11McTest(WifiBaseTest, RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
index c218898..020f6e2 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
@@ -19,11 +19,11 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RangeApSupporting11McTest(RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
index 2e53c6d..eebfecd 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -19,12 +19,12 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RangeAwareTest(AwareBaseTest, RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py
index c23b5b0..6f7d5fe 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py
@@ -16,11 +16,11 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RangeSoftApTest(RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py
index 635837c..94ebb01 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py
@@ -17,10 +17,10 @@
from acts import asserts
from acts import utils
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
class RttDisableTest(WifiBaseTest, RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
index c91521d..f7c1f91 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
@@ -19,9 +19,9 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class RttRequestManagementTest(RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py
index d0e9fe9..d88fd26 100644
--- a/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py
+++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py
@@ -16,8 +16,8 @@
from acts import asserts
from acts.base_test import BaseTestClass
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class StressRangeApTest(RttBaseTest):
diff --git a/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
index ccf8b9d..b03ab5b 100644
--- a/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
+++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
@@ -18,12 +18,12 @@
import time
from acts import asserts
-from acts.test_utils.wifi.aware import aware_const as aconsts
-from acts.test_utils.wifi.aware import aware_test_utils as autils
-from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.test_utils.wifi.rtt import rtt_const as rconsts
-from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
-from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts_contrib.test_utils.wifi.aware import aware_const as aconsts
+from acts_contrib.test_utils.wifi.aware import aware_test_utils as autils
+from acts_contrib.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts_contrib.test_utils.wifi.rtt import rtt_const as rconsts
+from acts_contrib.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts_contrib.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
class StressRangeAwareTest(AwareBaseTest, RttBaseTest):