Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3.4 |
| 2 | # |
| 3 | # Copyright 2017 Google, Inc. |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | import logging |
| 18 | import os |
| 19 | import time |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 20 | from acts import asserts |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 21 | from acts import utils |
| 22 | from acts.controllers import monsoon |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 23 | from acts.libs.proc import job |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 24 | from acts.controllers.ap_lib import bridge_interface as bi |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 25 | from acts.test_utils.wifi import wifi_test_utils as wutils |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 26 | from bokeh.layouts import layout |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 27 | from bokeh.models import CustomJS, ColumnDataSource |
Omar El Ayach | f4f132b | 2018-01-26 03:37:08 +0000 | [diff] [blame] | 28 | from bokeh.models import tools as bokeh_tools |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 29 | from bokeh.models.widgets import DataTable, TableColumn |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 30 | from bokeh.plotting import figure, output_file, save |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 31 | from acts.controllers.ap_lib import hostapd_security |
| 32 | from acts.controllers.ap_lib import hostapd_ap_preset |
Daniel Barros | 4376030 | 2017-12-06 04:16:03 +0000 | [diff] [blame] | 33 | from acts.test_utils.bt.bt_test_utils import enable_bluetooth |
| 34 | from acts.test_utils.bt.bt_test_utils import disable_bluetooth |
| 35 | |
Daniel Barros | fefd8c3 | 2017-08-04 15:36:40 -0700 | [diff] [blame] | 36 | # http://www.secdev.org/projects/scapy/ |
| 37 | # On ubuntu, sudo pip3 install scapy-python3 |
| 38 | import scapy.all as scapy |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 39 | |
| 40 | SETTINGS_PAGE = "am start -n com.android.settings/.Settings" |
| 41 | SCROLL_BOTTOM = "input swipe 0 2000 0 0" |
| 42 | UNLOCK_SCREEN = "input keyevent 82" |
| 43 | SCREENON_USB_DISABLE = "dumpsys battery unplug" |
| 44 | RESET_BATTERY_STATS = "dumpsys batterystats --reset" |
| 45 | AOD_OFF = "settings put secure doze_always_on 0" |
| 46 | MUSIC_IQ_OFF = "pm disable-user com.google.intelligence.sense" |
| 47 | # Command to disable gestures |
| 48 | LIFT = "settings put secure doze_pulse_on_pick_up 0" |
| 49 | DOUBLE_TAP = "settings put secure doze_pulse_on_double_tap 0" |
| 50 | JUMP_TO_CAMERA = "settings put secure camera_double_tap_power_gesture_disabled 1" |
| 51 | RAISE_TO_CAMERA = "settings put secure camera_lift_trigger_enabled 0" |
| 52 | FLIP_CAMERA = "settings put secure camera_double_twist_to_flip_enabled 0" |
| 53 | ASSIST_GESTURE = "settings put secure assist_gesture_enabled 0" |
| 54 | ASSIST_GESTURE_ALERT = "settings put secure assist_gesture_silence_alerts_enabled 0" |
| 55 | ASSIST_GESTURE_WAKE = "settings put secure assist_gesture_wake_enabled 0" |
| 56 | SYSTEM_NAVI = "settings put secure system_navigation_keys_enabled 0" |
| 57 | # End of command to disable gestures |
| 58 | AUTO_TIME_OFF = "settings put global auto_time 0" |
| 59 | AUTO_TIMEZONE_OFF = "settings put global auto_time_zone 0" |
Qi Jiang | 5073338 | 2017-08-22 01:11:58 +0000 | [diff] [blame] | 60 | FORCE_YOUTUBE_STOP = "am force-stop com.google.android.youtube" |
| 61 | FORCE_DIALER_STOP = "am force-stop com.google.android.dialer" |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 62 | IPERF_TIMEOUT = 180 |
Daniel Barros | 7cc34e8 | 2017-09-22 02:41:32 +0000 | [diff] [blame] | 63 | THRESHOLD_TOLERANCE = 0.2 |
Daniel Barros | ebbf91a | 2017-10-04 01:08:40 +0000 | [diff] [blame] | 64 | GET_FROM_PHONE = 'get_from_dut' |
| 65 | GET_FROM_AP = 'get_from_ap' |
Daniel Barros | 35c901a | 2017-10-25 13:30:57 +0000 | [diff] [blame] | 66 | PHONE_BATTERY_VOLTAGE = 4.2 |
| 67 | MONSOON_MAX_CURRENT = 8.0 |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 68 | MONSOON_RECOVER_TIME_MAX = 300 |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 69 | MONSOON_RETRY_INTERVAL = 60 |
| 70 | MONSOON_WAIT = 60 |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 71 | MEASUREMENT_RETRY_COUNT = 3 |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 72 | MIN_PERCENT_SAMPLE = 95 |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 73 | |
| 74 | |
| 75 | def dut_rockbottom(ad): |
| 76 | """Set the phone into Rock-bottom state. |
| 77 | |
| 78 | Args: |
| 79 | ad: the target android device, AndroidDevice object |
| 80 | |
| 81 | """ |
| 82 | ad.log.info("Now set the device to Rockbottom State") |
| 83 | utils.require_sl4a((ad, )) |
Daniel Barros | 7cc34e8 | 2017-09-22 02:41:32 +0000 | [diff] [blame] | 84 | ad.droid.connectivityToggleAirplaneMode(False) |
| 85 | time.sleep(5) |
| 86 | ad.droid.connectivityToggleAirplaneMode(True) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 87 | utils.set_ambient_display(ad, False) |
| 88 | utils.set_auto_rotate(ad, False) |
| 89 | utils.set_adaptive_brightness(ad, False) |
| 90 | utils.sync_device_time(ad) |
| 91 | utils.set_location_service(ad, False) |
| 92 | utils.set_mobile_data_always_on(ad, False) |
| 93 | utils.disable_doze_light(ad) |
| 94 | utils.disable_doze(ad) |
| 95 | wutils.reset_wifi(ad) |
| 96 | wutils.wifi_toggle_state(ad, False) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 97 | ad.droid.nfcDisable() |
| 98 | ad.droid.setScreenBrightness(0) |
| 99 | ad.adb.shell(AOD_OFF) |
| 100 | ad.droid.setScreenTimeout(2200) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 101 | ad.droid.wakeUpNow() |
| 102 | ad.adb.shell(LIFT) |
| 103 | ad.adb.shell(DOUBLE_TAP) |
| 104 | ad.adb.shell(JUMP_TO_CAMERA) |
| 105 | ad.adb.shell(RAISE_TO_CAMERA) |
| 106 | ad.adb.shell(FLIP_CAMERA) |
| 107 | ad.adb.shell(ASSIST_GESTURE) |
| 108 | ad.adb.shell(ASSIST_GESTURE_ALERT) |
| 109 | ad.adb.shell(ASSIST_GESTURE_WAKE) |
| 110 | ad.adb.shell(SCREENON_USB_DISABLE) |
| 111 | ad.adb.shell(UNLOCK_SCREEN) |
| 112 | ad.adb.shell(SETTINGS_PAGE) |
| 113 | ad.adb.shell(SCROLL_BOTTOM) |
| 114 | ad.adb.shell(MUSIC_IQ_OFF) |
| 115 | ad.adb.shell(AUTO_TIME_OFF) |
| 116 | ad.adb.shell(AUTO_TIMEZONE_OFF) |
Qi Jiang | 5073338 | 2017-08-22 01:11:58 +0000 | [diff] [blame] | 117 | ad.adb.shell(FORCE_YOUTUBE_STOP) |
| 118 | ad.adb.shell(FORCE_DIALER_STOP) |
| 119 | ad.droid.wakeUpNow() |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 120 | ad.log.info('Device has been set to Rockbottom state') |
Daniel Barros | 35c901a | 2017-10-25 13:30:57 +0000 | [diff] [blame] | 121 | ad.log.info('Screen is ON') |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 122 | |
| 123 | |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 124 | def pass_fail_check(test_class, test_result): |
| 125 | """Check the test result and decide if it passed or failed. |
| 126 | The threshold is provided in the config file |
| 127 | |
| 128 | Args: |
| 129 | test_class: the specific test class where test is running |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 130 | test_result: the average current as the test result |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 131 | """ |
| 132 | test_name = test_class.current_test_name |
| 133 | current_threshold = test_class.threshold[test_name] |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 134 | if test_result: |
| 135 | asserts.assert_true( |
| 136 | abs(test_result - current_threshold) / current_threshold < |
| 137 | THRESHOLD_TOLERANCE, |
| 138 | ("Measured average current in [%s]: %s, which is " |
| 139 | "more than %d percent off than acceptable threshold %.2fmA") % |
| 140 | (test_name, test_result, THRESHOLD_TOLERANCE * 100, |
| 141 | current_threshold)) |
| 142 | asserts.explicit_pass("Measurement finished for %s." % test_name) |
| 143 | else: |
| 144 | asserts.fail( |
| 145 | "Something happened, measurement is not complete, test failed") |
Qi Jiang | 8f9c758 | 2017-09-06 21:00:14 +0000 | [diff] [blame] | 146 | |
| 147 | |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 148 | def monsoon_recover(mon, time_max, retry_interval): |
| 149 | """Test loop to wait for monsoon recover from unexpected error. |
| 150 | |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 151 | Wait for a certain time duration, then quit.0 |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 152 | Args: |
| 153 | mon: monsoon object |
| 154 | """ |
| 155 | mon_status = False |
| 156 | time_start = time.time() |
| 157 | while time.time() < time_start + time_max and not mon_status: |
| 158 | try: |
| 159 | mon.usb("on") |
| 160 | mon_status = True |
| 161 | logging.info("Monsoon recovered from unexpected error") |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 162 | time.sleep(5) |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 163 | return mon_status |
| 164 | except monsoon.MonsoonError: |
| 165 | time.sleep(retry_interval) |
| 166 | continue |
| 167 | logging.warning("Couldn't recover monsoon from unexpected error") |
| 168 | return mon_status |
| 169 | |
| 170 | |
Qi Jiang | c64e51b | 2018-02-09 04:02:47 +0000 | [diff] [blame] | 171 | def monsoon_data_collect_save(ad, mon_info, test_name): |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 172 | """Current measurement and save the log file. |
| 173 | |
| 174 | Collect current data using Monsoon box and return the path of the |
| 175 | log file. Take bug report if requested. |
| 176 | |
| 177 | Args: |
| 178 | ad: the android device under test |
| 179 | mon_info: dict with information of monsoon measurement, including |
| 180 | monsoon device object, measurement frequency, duration and |
| 181 | offset etc. |
| 182 | test_name: current test name, used to contruct the result file name |
| 183 | bug_report: indicator to take bug report or not, 0 or 1 |
| 184 | Returns: |
| 185 | data_path: the absolute path to the log file of monsoon current |
| 186 | measurement |
| 187 | avg_current: the average current of the test |
| 188 | """ |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 189 | |
| 190 | log = logging.getLogger() |
| 191 | tag = (test_name + '_' + ad.model + '_' + ad.build_info['build_id']) |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 192 | data_path = os.path.join(mon_info['data_path'], "%s.txt" % tag) |
| 193 | total_expected_samples = mon_info['freq'] * ( |
| 194 | mon_info['duration'] + mon_info['offset']) |
| 195 | min_required_samples = total_expected_samples * MIN_PERCENT_SAMPLE / 100 |
| 196 | # Retry counter |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 197 | retry = 1 |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 198 | # Indicator that need to recover monsoon from serial port errors |
| 199 | need_usb_on = 0 |
| 200 | # Indicator that need to re-collect data |
| 201 | need_collect_data = 1 |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 202 | result = None |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 203 | while retry <= MEASUREMENT_RETRY_COUNT: |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 204 | try: |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 205 | # If need to retake data |
| 206 | if need_collect_data == 1: |
| 207 | # If monsoon needs to be recovered |
| 208 | if need_usb_on == 1: |
| 209 | time.sleep(MONSOON_WAIT) |
| 210 | mon_info['dut'].usb('on') |
| 211 | #Resets the battery status right before the test started |
| 212 | ad.adb.shell(RESET_BATTERY_STATS) |
| 213 | log.info( |
| 214 | "Starting power measurement with monsoon box, try #{}". |
| 215 | format(retry)) |
| 216 | #Start the power measurement using monsoon |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 217 | mon_info['dut'].disconnect_dut() |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 218 | result = mon_info['dut'].measure_power( |
| 219 | mon_info['freq'], |
| 220 | mon_info['duration'], |
| 221 | tag=tag, |
| 222 | offset=mon_info['offset']) |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 223 | mon_info['dut'].reconnect_dut() |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 224 | # If no need to retake data but monsoon needs to be recovered |
| 225 | elif need_usb_on == 1: |
| 226 | time.sleep(MONSOON_WAIT) |
| 227 | mon_info['dut'].usb('on') |
| 228 | # Return measurement results if no error happens |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 229 | avg_current = result.average_current |
| 230 | monsoon.MonsoonData.save_to_text_file([result], data_path) |
| 231 | log.info("Power measurement done within {} try".format(retry)) |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 232 | return data_path, avg_current |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 233 | # Catch monsoon errors |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 234 | except monsoon.MonsoonError: |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 235 | # If captured samples are less than min required, re-take |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 236 | if not result or len(result.__data_points) <= min_required_samples: |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 237 | need_collect_data = 1 |
| 238 | log.warning( |
| 239 | 'More than {} percent of samples are missing due to monsoon error. Need to take one more measurement'. |
| 240 | format(100 - MIN_PERCENT_SAMPLE)) |
| 241 | mon_status = monsoon_recover(mon_info['dut'], |
| 242 | MONSOON_RECOVER_TIME_MAX, |
| 243 | MONSOON_RETRY_INTERVAL) |
| 244 | if mon_status: |
| 245 | need_usb_on = 0 |
| 246 | else: |
| 247 | need_usb_on = 1 |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 248 | retry += 1 |
| 249 | continue |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 250 | # No need to retake data, just recover monsoon |
Qi Jiang | 36a7c73 | 2018-02-25 05:05:40 +0000 | [diff] [blame] | 251 | else: |
| 252 | log.warning( |
Qi Jiang | ce0b127 | 2018-02-28 04:39:23 +0000 | [diff] [blame] | 253 | 'Error happened trying to reconnect to DUT, no need to re-collect data' |
| 254 | ) |
| 255 | need_collect_data = 0 |
| 256 | mon_status = monsoon_recover(mon_info['dut'], |
| 257 | MONSOON_RECOVER_TIME_MAX, |
| 258 | MONSOON_RETRY_INTERVAL) |
| 259 | if mon_status: |
| 260 | # Here should be all set, go back to return |
| 261 | need_usb_on = 0 |
| 262 | else: |
| 263 | need_usb_on = 1 |
| 264 | retry += 1 |
| 265 | continue |
| 266 | if retry > MEASUREMENT_RETRY_COUNT: |
| 267 | log.error( |
| 268 | 'Tried our best, test still failed due to Monsoon serial port error' |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 269 | ) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 270 | |
| 271 | |
| 272 | def monsoon_data_plot(mon_info, file_path, tag=""): |
| 273 | """Plot the monsoon current data using bokeh interactive plotting tool. |
| 274 | |
| 275 | Plotting power measurement data with bokeh to generate interactive plots. |
| 276 | You can do interactive data analysis on the plot after generating with the |
| 277 | provided widgets, which make the debugging much easier. To realize that, |
| 278 | bokeh callback java scripting is used. View a sample html output file: |
| 279 | https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA |
| 280 | |
| 281 | Args: |
| 282 | mon_info: dict with information of monsoon measurement, including |
| 283 | monsoon device object, measurement frequency, duration and |
| 284 | offset etc. |
| 285 | file_path: the path to the monsoon log file with current data |
| 286 | |
| 287 | Returns: |
| 288 | plot: the plotting object of bokeh, optional, will be needed if multiple |
| 289 | plots will be combined to one html file. |
| 290 | dt: the datatable object of bokeh, optional, will be needed if multiple |
| 291 | datatables will be combined to one html file. |
| 292 | """ |
| 293 | |
| 294 | log = logging.getLogger() |
| 295 | log.info("Plot the power measurement data") |
| 296 | #Get results as monsoon data object from the input file |
| 297 | results = monsoon.MonsoonData.from_text_file(file_path) |
| 298 | #Decouple current and timestamp data from the monsoon object |
| 299 | current_data = [] |
| 300 | timestamps = [] |
| 301 | voltage = results[0].voltage |
| 302 | [current_data.extend(x.data_points) for x in results] |
| 303 | [timestamps.extend(x.timestamps) for x in results] |
| 304 | period = 1 / float(mon_info['freq']) |
| 305 | time_relative = [x * period for x in range(len(current_data))] |
| 306 | #Calculate the average current for the test |
| 307 | current_data = [x * 1000 for x in current_data] |
| 308 | avg_current = sum(current_data) / len(current_data) |
| 309 | color = ['navy'] * len(current_data) |
| 310 | |
| 311 | #Preparing the data and source link for bokehn java callback |
Qi Jiang | 94b289a | 2018-03-01 03:50:15 +0000 | [diff] [blame^] | 312 | source = ColumnDataSource( |
| 313 | data=dict(x0=time_relative, y0=current_data, color=color)) |
| 314 | s2 = ColumnDataSource( |
| 315 | data=dict( |
| 316 | z0=[mon_info['duration']], |
| 317 | y0=[round(avg_current, 2)], |
| 318 | x0=[round(avg_current * voltage, 2)], |
| 319 | z1=[round(avg_current * voltage * mon_info['duration'], 2)], |
| 320 | z2=[round(avg_current * mon_info['duration'], 2)])) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 321 | #Setting up data table for the output |
| 322 | columns = [ |
| 323 | TableColumn(field='z0', title='Total Duration (s)'), |
| 324 | TableColumn(field='y0', title='Average Current (mA)'), |
| 325 | TableColumn(field='x0', title='Average Power (4.2v) (mW)'), |
| 326 | TableColumn(field='z1', title='Average Energy (mW*s)'), |
| 327 | TableColumn(field='z2', title='Normalized Average Energy (mA*s)') |
| 328 | ] |
| 329 | dt = DataTable( |
| 330 | source=s2, columns=columns, width=1300, height=60, editable=True) |
| 331 | |
| 332 | plot_title = file_path[file_path.rfind('/') + 1:-4] + tag |
| 333 | output_file("%s/%s.html" % (mon_info['data_path'], plot_title)) |
Omar El Ayach | f4f132b | 2018-01-26 03:37:08 +0000 | [diff] [blame] | 334 | TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save') |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 335 | # Create a new plot with the datatable above |
| 336 | plot = figure( |
| 337 | plot_width=1300, |
| 338 | plot_height=700, |
| 339 | title=plot_title, |
| 340 | tools=TOOLS, |
Omar El Ayach | f4f132b | 2018-01-26 03:37:08 +0000 | [diff] [blame] | 341 | output_backend="webgl") |
| 342 | plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width")) |
| 343 | plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height")) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 344 | plot.line('x0', 'y0', source=source, line_width=2) |
| 345 | plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color') |
| 346 | plot.xaxis.axis_label = 'Time (s)' |
| 347 | plot.yaxis.axis_label = 'Current (mA)' |
| 348 | plot.title.text_font_size = {'value': '15pt'} |
| 349 | |
| 350 | #Callback Java scripting |
| 351 | source.callback = CustomJS( |
| 352 | args=dict(mytable=dt), |
| 353 | code=""" |
| 354 | var inds = cb_obj.get('selected')['1d'].indices; |
| 355 | var d1 = cb_obj.get('data'); |
| 356 | var d2 = mytable.get('source').get('data'); |
| 357 | ym = 0 |
| 358 | ts = 0 |
| 359 | d2['x0'] = [] |
| 360 | d2['y0'] = [] |
| 361 | d2['z1'] = [] |
| 362 | d2['z2'] = [] |
| 363 | d2['z0'] = [] |
| 364 | min=max=d1['x0'][inds[0]] |
| 365 | if (inds.length==0) {return;} |
| 366 | for (i = 0; i < inds.length; i++) { |
| 367 | ym += d1['y0'][inds[i]] |
| 368 | d1['color'][inds[i]] = "red" |
| 369 | if (d1['x0'][inds[i]] < min) { |
| 370 | min = d1['x0'][inds[i]]} |
| 371 | if (d1['x0'][inds[i]] > max) { |
| 372 | max = d1['x0'][inds[i]]} |
| 373 | } |
| 374 | ym /= inds.length |
| 375 | ts = max - min |
| 376 | dx0 = Math.round(ym*4.2*100.0)/100.0 |
| 377 | dy0 = Math.round(ym*100.0)/100.0 |
| 378 | dz1 = Math.round(ym*4.2*ts*100.0)/100.0 |
| 379 | dz2 = Math.round(ym*ts*100.0)/100.0 |
| 380 | dz0 = Math.round(ts*1000.0)/1000.0 |
| 381 | d2['z0'].push(dz0) |
| 382 | d2['x0'].push(dx0) |
| 383 | d2['y0'].push(dy0) |
| 384 | d2['z1'].push(dz1) |
| 385 | d2['z2'].push(dz2) |
| 386 | mytable.trigger('change'); |
| 387 | """) |
| 388 | |
| 389 | #Layout the plot and the datatable bar |
| 390 | l = layout([[dt], [plot]]) |
| 391 | save(l) |
| 392 | return [plot, dt] |
| 393 | |
| 394 | |
Qi Jiang | 43c80e1 | 2017-11-04 03:05:21 +0000 | [diff] [blame] | 395 | def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10): |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 396 | """Function to change the DTIM setting in the phone. |
| 397 | |
| 398 | Args: |
| 399 | ad: the target android device, AndroidDevice object |
| 400 | gEnableModulatedDTIM: Modulated DTIM, int |
| 401 | gMaxLIModulatedDTIM: Maximum modulated DTIM, int |
| 402 | """ |
| 403 | serial = ad.serial |
| 404 | ini_file_phone = 'vendor/firmware/wlan/qca_cld/WCNSS_qcom_cfg.ini' |
| 405 | ini_file_local = 'local_ini_file.ini' |
| 406 | ini_pull_cmd = 'adb -s %s pull %s %s' % (serial, ini_file_phone, |
| 407 | ini_file_local) |
| 408 | ini_push_cmd = 'adb -s %s push %s %s' % (serial, ini_file_local, |
| 409 | ini_file_phone) |
| 410 | utils.exe_cmd(ini_pull_cmd) |
| 411 | |
| 412 | with open(ini_file_local, 'r') as fin: |
| 413 | for line in fin: |
| 414 | if 'gEnableModulatedDTIM=' in line: |
| 415 | gEDTIM_old = line.strip('gEnableModulatedDTIM=').strip('\n') |
| 416 | if 'gMaxLIModulatedDTIM=' in line: |
| 417 | gMDTIM_old = line.strip('gMaxLIModulatedDTIM=').strip('\n') |
Qi Jiang | 43c80e1 | 2017-11-04 03:05:21 +0000 | [diff] [blame] | 418 | if int(gEDTIM_old) == gEnableModulatedDTIM and int( |
| 419 | gMDTIM_old) == gMaxLIModulatedDTIM: |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 420 | ad.log.info('Current DTIM is already the desired value,' |
| 421 | 'no need to reset it') |
| 422 | return |
| 423 | |
| 424 | gE_old = 'gEnableModulatedDTIM=' + gEDTIM_old |
| 425 | gM_old = 'gMaxLIModulatedDTIM=' + gMDTIM_old |
| 426 | gE_new = 'gEnableModulatedDTIM=' + str(gEnableModulatedDTIM) |
| 427 | gM_new = 'gMaxLIModulatedDTIM=' + str(gMaxLIModulatedDTIM) |
| 428 | |
| 429 | sed_gE = 'sed -i \'s/%s/%s/g\' %s' % (gE_old, gE_new, ini_file_local) |
| 430 | sed_gM = 'sed -i \'s/%s/%s/g\' %s' % (gM_old, gM_new, ini_file_local) |
| 431 | utils.exe_cmd(sed_gE) |
| 432 | utils.exe_cmd(sed_gM) |
| 433 | |
| 434 | utils.exe_cmd('adb -s {} root'.format(serial)) |
| 435 | cmd_out = utils.exe_cmd('adb -s {} remount'.format(serial)) |
| 436 | if ("Permission denied").encode() in cmd_out: |
| 437 | ad.log.info('Need to disable verity first and reboot') |
| 438 | utils.exe_cmd('adb -s {} disable-verity'.format(serial)) |
| 439 | time.sleep(1) |
| 440 | ad.reboot() |
| 441 | ad.log.info('Verity disabled and device back from reboot') |
| 442 | utils.exe_cmd('adb -s {} root'.format(serial)) |
| 443 | utils.exe_cmd('adb -s {} remount'.format(serial)) |
| 444 | time.sleep(1) |
| 445 | utils.exe_cmd(ini_push_cmd) |
| 446 | ad.log.info('ini file changes checked in and rebooting...') |
| 447 | ad.reboot() |
| 448 | ad.log.info('DTIM updated and device back from reboot') |
| 449 | |
| 450 | |
Qi Jiang | dc073e5 | 2017-12-19 19:12:35 +0000 | [diff] [blame] | 451 | def ap_setup(ap, network, bandwidth=80): |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 452 | """Set up the whirlwind AP with provided network info. |
| 453 | |
| 454 | Args: |
| 455 | ap: access_point object of the AP |
| 456 | network: dict with information of the network, including ssid, password |
| 457 | bssid, channel etc. |
Qi Jiang | dc073e5 | 2017-12-19 19:12:35 +0000 | [diff] [blame] | 458 | bandwidth: the operation bandwidth for the AP, default 80MHz |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 459 | Returns: |
| 460 | brconfigs: the bridge interface configs |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 461 | """ |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 462 | log = logging.getLogger() |
| 463 | bss_settings = [] |
| 464 | ssid = network[wutils.WifiEnums.SSID_KEY] |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 465 | if "password" in network.keys(): |
| 466 | password = network["password"] |
| 467 | security = hostapd_security.Security( |
| 468 | security_mode="wpa", password=password) |
| 469 | else: |
| 470 | security = hostapd_security.Security(security_mode=None, password=None) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 471 | channel = network["channel"] |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 472 | config = hostapd_ap_preset.create_ap_preset( |
| 473 | channel=channel, |
| 474 | ssid=ssid, |
| 475 | security=security, |
| 476 | bss_settings=bss_settings, |
Qi Jiang | dc073e5 | 2017-12-19 19:12:35 +0000 | [diff] [blame] | 477 | vht_bandwidth=bandwidth, |
| 478 | profile_name='whirlwind', |
| 479 | iface_wlan_2g=ap.wlan_2g, |
| 480 | iface_wlan_5g=ap.wlan_5g) |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 481 | config_bridge = ap.generate_bridge_configs(channel) |
| 482 | brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1], |
| 483 | config_bridge[2]) |
| 484 | ap.bridge.startup(brconfigs) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 485 | ap.start_ap(config) |
| 486 | log.info("AP started on channel {} with SSID {}".format(channel, ssid)) |
Qi Jiang | 7f6dc98 | 2018-02-24 01:29:29 +0000 | [diff] [blame] | 487 | return brconfigs |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 488 | |
| 489 | |
Omar El Ayach | c01e1ef | 2018-02-28 04:52:49 +0000 | [diff] [blame] | 490 | def bokeh_plot(data_sets, |
| 491 | legends, |
| 492 | fig_property, |
| 493 | shaded_region=None, |
| 494 | output_file_path=None): |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 495 | """Plot bokeh figs. |
| 496 | Args: |
| 497 | data_sets: data sets including lists of x_data and lists of y_data |
| 498 | ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]] |
| 499 | legends: list of legend for each curve |
| 500 | fig_property: dict containing the plot property, including title, |
| 501 | lables, linewidth, circle size, etc. |
Omar El Ayach | c01e1ef | 2018-02-28 04:52:49 +0000 | [diff] [blame] | 502 | shaded_region: optional dict containing data for plot shading |
| 503 | output_file_path: optional path at which to save figure |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 504 | Returns: |
| 505 | plot: bokeh plot figure object |
| 506 | """ |
Omar El Ayach | f4f132b | 2018-01-26 03:37:08 +0000 | [diff] [blame] | 507 | TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save') |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 508 | plot = figure( |
| 509 | plot_width=1300, |
| 510 | plot_height=700, |
| 511 | title=fig_property['title'], |
| 512 | tools=TOOLS, |
Omar El Ayach | f4f132b | 2018-01-26 03:37:08 +0000 | [diff] [blame] | 513 | output_backend="webgl") |
| 514 | plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width")) |
| 515 | plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height")) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 516 | colors = [ |
| 517 | 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy', |
| 518 | 'yellow', 'darkred', 'goldenrod' |
| 519 | ] |
Omar El Ayach | c01e1ef | 2018-02-28 04:52:49 +0000 | [diff] [blame] | 520 | if shaded_region: |
| 521 | band_x = shaded_region["x_vector"] |
| 522 | band_x.extend(shaded_region["x_vector"][::-1]) |
| 523 | band_y = shaded_region["lower_limit"] |
| 524 | band_y.extend(shaded_region["upper_limit"][::-1]) |
| 525 | plot.patch( |
| 526 | band_x, band_y, color='#7570B3', line_alpha=0.1, fill_alpha=0.1) |
| 527 | |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 528 | for x_data, y_data, legend in zip(data_sets[0], data_sets[1], legends): |
| 529 | index_now = legends.index(legend) |
| 530 | color = colors[index_now % len(colors)] |
| 531 | plot.line( |
| 532 | x_data, y_data, legend=str(legend), line_width=3, color=color) |
| 533 | plot.circle( |
| 534 | x_data, y_data, size=10, legend=str(legend), fill_color=color) |
Omar El Ayach | c01e1ef | 2018-02-28 04:52:49 +0000 | [diff] [blame] | 535 | |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 536 | #Plot properties |
| 537 | plot.xaxis.axis_label = fig_property['x_label'] |
| 538 | plot.yaxis.axis_label = fig_property['y_label'] |
| 539 | plot.legend.location = "top_right" |
| 540 | plot.legend.click_policy = "hide" |
| 541 | plot.title.text_font_size = {'value': '15pt'} |
Omar El Ayach | ded6423 | 2017-12-08 00:53:12 +0000 | [diff] [blame] | 542 | if output_file_path is not None: |
| 543 | output_file(output_file_path) |
| 544 | save(plot) |
Qi Jiang | 73b2535 | 2017-08-02 19:56:31 +0000 | [diff] [blame] | 545 | return plot |
| 546 | |
| 547 | |
| 548 | def run_iperf_client_nonblocking(ad, server_host, extra_args=""): |
| 549 | """Start iperf client on the device with nohup. |
| 550 | |
| 551 | Return status as true if iperf client start successfully. |
| 552 | And data flow information as results. |
| 553 | |
| 554 | Args: |
| 555 | ad: the android device under test |
| 556 | server_host: Address of the iperf server. |
| 557 | extra_args: A string representing extra arguments for iperf client, |
| 558 | e.g. "-i 1 -t 30". |
| 559 | |
| 560 | """ |
| 561 | log = logging.getLogger() |
| 562 | ad.adb.shell_nb("nohup iperf3 -c {} {} &".format(server_host, extra_args)) |
| 563 | log.info("IPerf client started") |
| 564 | |
| 565 | |
| 566 | def get_wifi_rssi(ad): |
| 567 | """Get the RSSI of the device. |
| 568 | |
| 569 | Args: |
| 570 | ad: the android device under test |
| 571 | Returns: |
| 572 | RSSI: the rssi level of the device |
| 573 | """ |
| 574 | RSSI = ad.droid.wifiGetConnectionInfo()['rssi'] |
| 575 | return RSSI |
Qi Jiang | 5073338 | 2017-08-22 01:11:58 +0000 | [diff] [blame] | 576 | |
| 577 | |
| 578 | def get_phone_ip(ad): |
| 579 | """Get the WiFi IP address of the phone. |
| 580 | |
| 581 | Args: |
| 582 | ad: the android device under test |
| 583 | Returns: |
| 584 | IP: IP address of the phone for WiFi, as a string |
| 585 | """ |
| 586 | IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0] |
| 587 | |
| 588 | return IP |
| 589 | |
| 590 | |
| 591 | def get_phone_mac(ad): |
| 592 | """Get the WiFi MAC address of the phone. |
| 593 | |
| 594 | Args: |
| 595 | ad: the android device under test |
| 596 | Returns: |
| 597 | mac: MAC address of the phone for WiFi, as a string |
| 598 | """ |
| 599 | mac = ad.droid.wifiGetConnectionInfo()["mac_address"] |
| 600 | |
| 601 | return mac |
| 602 | |
| 603 | |
| 604 | def get_phone_ipv6(ad): |
| 605 | """Get the WiFi IPV6 address of the phone. |
| 606 | |
| 607 | Args: |
| 608 | ad: the android device under test |
| 609 | Returns: |
| 610 | IPv6: IPv6 address of the phone for WiFi, as a string |
| 611 | """ |
| 612 | IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6] |
| 613 | |
| 614 | return IPv6 |
Daniel Barros | fefd8c3 | 2017-08-04 15:36:40 -0700 | [diff] [blame] | 615 | |
| 616 | |
| 617 | def get_if_addr6(intf, address_type): |
| 618 | """Returns the Ipv6 address from a given local interface. |
| 619 | |
| 620 | Returns the desired IPv6 address from the interface 'intf' in human |
| 621 | readable form. The address type is indicated by the IPv6 constants like |
| 622 | IPV6_ADDR_LINKLOCAL, IPV6_ADDR_GLOBAL, etc. If no address is found, |
| 623 | None is returned. |
| 624 | |
| 625 | Args: |
| 626 | intf: desired interface name |
| 627 | address_type: addrees typle like LINKLOCAL or GLOBAL |
| 628 | |
| 629 | Returns: |
| 630 | Ipv6 address of the specified interface in human readable format |
| 631 | """ |
| 632 | for if_list in scapy.in6_getifaddr(): |
| 633 | if if_list[2] == intf and if_list[1] == address_type: |
| 634 | return if_list[0] |
| 635 | |
| 636 | return None |
Daniel Barros | ebbf91a | 2017-10-04 01:08:40 +0000 | [diff] [blame] | 637 | |
| 638 | |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 639 | @utils.timeout(60) |
| 640 | def wait_for_dhcp(intf): |
| 641 | """Wait the DHCP address assigned to desired interface. |
| 642 | |
| 643 | Getting DHCP address takes time and the wait time isn't constant. Utilizing |
| 644 | utils.timeout to keep trying until success |
| 645 | |
| 646 | Args: |
| 647 | intf: desired interface name |
| 648 | Returns: |
| 649 | ip: ip address of the desired interface name |
| 650 | Raise: |
| 651 | TimeoutError: After timeout, if no DHCP assigned, raise |
| 652 | """ |
| 653 | log = logging.getLogger() |
| 654 | reset_host_interface(intf) |
| 655 | ip = '0.0.0.0' |
| 656 | while ip == '0.0.0.0': |
| 657 | ip = scapy.get_if_addr(intf) |
Qi Jiang | 0ded53a | 2018-02-03 06:23:11 +0000 | [diff] [blame] | 658 | log.info('DHCP address assigned to {} as {}'.format(intf, ip)) |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 659 | return ip |
| 660 | |
| 661 | |
| 662 | def reset_host_interface(intf): |
| 663 | """Reset the host interface. |
| 664 | |
| 665 | Args: |
| 666 | intf: the desired interface to reset |
| 667 | """ |
| 668 | log = logging.getLogger() |
| 669 | intf_down_cmd = 'ifconfig %s down' % intf |
| 670 | intf_up_cmd = 'ifconfig %s up' % intf |
| 671 | try: |
| 672 | job.run(intf_down_cmd) |
Qi Jiang | 0ded53a | 2018-02-03 06:23:11 +0000 | [diff] [blame] | 673 | time.sleep(10) |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 674 | job.run(intf_up_cmd) |
Qi Jiang | caca55e | 2017-10-10 23:10:40 +0000 | [diff] [blame] | 675 | log.info('{} has been reset'.format(intf)) |
| 676 | except job.Error: |
| 677 | raise Exception('No such interface') |
| 678 | |
| 679 | |
Daniel Barros | ebbf91a | 2017-10-04 01:08:40 +0000 | [diff] [blame] | 680 | def create_pkt_config(test_class): |
| 681 | """Creates the config for generating multicast packets |
| 682 | |
| 683 | Args: |
| 684 | test_class: object with all networking paramters |
| 685 | |
| 686 | Returns: |
| 687 | Dictionary with the multicast packet config |
| 688 | """ |
| 689 | addr_type = (scapy.IPV6_ADDR_LINKLOCAL |
| 690 | if test_class.ipv6_src_type == 'LINK_LOCAL' else |
| 691 | scapy.IPV6_ADDR_GLOBAL) |
| 692 | |
| 693 | mac_dst = test_class.mac_dst |
| 694 | if GET_FROM_PHONE in test_class.mac_dst: |
| 695 | mac_dst = get_phone_mac(test_class.dut) |
| 696 | |
| 697 | ipv4_dst = test_class.ipv4_dst |
| 698 | if GET_FROM_PHONE in test_class.ipv4_dst: |
| 699 | ipv4_dst = get_phone_ip(test_class.dut) |
| 700 | |
| 701 | ipv6_dst = test_class.ipv6_dst |
| 702 | if GET_FROM_PHONE in test_class.ipv6_dst: |
| 703 | ipv6_dst = get_phone_ipv6(test_class.dut) |
| 704 | |
| 705 | ipv4_gw = test_class.ipv4_gwt |
| 706 | if GET_FROM_AP in test_class.ipv4_gwt: |
| 707 | ipv4_gw = test_class.access_point.ssh_settings.hostname |
| 708 | |
| 709 | pkt_gen_config = { |
| 710 | 'interf': test_class.pkt_sender.interface, |
| 711 | 'subnet_mask': test_class.sub_mask, |
| 712 | 'src_mac': test_class.mac_src, |
| 713 | 'dst_mac': mac_dst, |
| 714 | 'src_ipv4': test_class.ipv4_src, |
| 715 | 'dst_ipv4': ipv4_dst, |
| 716 | 'src_ipv6': test_class.ipv6_src, |
| 717 | 'src_ipv6_type': addr_type, |
| 718 | 'dst_ipv6': ipv6_dst, |
| 719 | 'gw_ipv4': ipv4_gw |
| 720 | } |
| 721 | return pkt_gen_config |
| 722 | |
| 723 | |
| 724 | def create_monsoon_info(test_class): |
| 725 | """Creates the config dictionary for monsoon |
| 726 | |
| 727 | Args: |
| 728 | test_class: object with all the parameters |
| 729 | |
| 730 | Returns: |
| 731 | Dictionary with the monsoon packet config |
| 732 | """ |
| 733 | mon_info = { |
| 734 | 'dut': test_class.mon, |
| 735 | 'freq': test_class.mon_freq, |
| 736 | 'duration': test_class.mon_duration, |
| 737 | 'offset': test_class.mon_offset, |
| 738 | 'data_path': test_class.mon_data_path |
| 739 | } |
| 740 | return mon_info |
Daniel Barros | 4376030 | 2017-12-06 04:16:03 +0000 | [diff] [blame] | 741 | |
| 742 | |
| 743 | def setup_phone_wireless(test_class, |
| 744 | bt_on, |
| 745 | wifi_on, |
| 746 | screen_status, |
| 747 | network=None, |
| 748 | regular_mode=False): |
| 749 | """Sets the phone in rock-bottom and in the desired wireless mode |
| 750 | |
| 751 | Args: |
| 752 | test_class: the specific test class where test is running |
| 753 | bt_on: Enable/Disable BT |
| 754 | wifi_on: Enable/Disable WiFi |
| 755 | screen_status: screen ON or OFF |
| 756 | network: a dict of information for the WiFi network to connect |
| 757 | regular_mode: enable cellular data (i.e., disable airplane mode) |
| 758 | """ |
| 759 | # Initialize the dut to rock-bottom state |
| 760 | dut_rockbottom(test_class.dut) |
Qi Jiang | 93b2d5b | 2018-02-25 03:30:46 +0000 | [diff] [blame] | 761 | brconfigs = None |
Daniel Barros | 4376030 | 2017-12-06 04:16:03 +0000 | [diff] [blame] | 762 | time.sleep(1) |
| 763 | |
| 764 | if regular_mode: |
| 765 | test_class.dut.droid.connectivityToggleAirplaneMode(False) |
| 766 | utils.set_mobile_data_always_on(test_class.dut, True) |
| 767 | time.sleep(2) |
| 768 | |
| 769 | # Turn ON/OFF BT |
| 770 | if bt_on == 'ON': |
| 771 | enable_bluetooth(test_class.dut.droid, test_class.dut.ed) |
| 772 | test_class.dut.log.info('BT is ON') |
| 773 | else: |
| 774 | disable_bluetooth(test_class.dut.droid) |
| 775 | test_class.dut.droid.bluetoothDisableBLE() |
| 776 | test_class.dut.log.info('BT is OFF') |
| 777 | time.sleep(2) |
| 778 | |
| 779 | # Turn ON/OFF Wifi |
| 780 | if wifi_on == 'ON': |
| 781 | wutils.wifi_toggle_state(test_class.dut, True) |
| 782 | test_class.dut.log.info('WiFi is ON') |
| 783 | if network: |
| 784 | # Set attenuation and connect to AP |
| 785 | for attn in range(test_class.num_atten): |
| 786 | test_class.attenuators[attn].set_atten( |
| 787 | test_class.atten_level['zero_atten'][attn]) |
| 788 | test_class.log.info('Set attenuation level to all zero') |
Qi Jiang | 93b2d5b | 2018-02-25 03:30:46 +0000 | [diff] [blame] | 789 | brconfigs = ap_setup(test_class.access_point, network) |
Daniel Barros | 4376030 | 2017-12-06 04:16:03 +0000 | [diff] [blame] | 790 | wutils.wifi_connect(test_class.dut, network) |
| 791 | else: |
| 792 | wutils.wifi_toggle_state(test_class.dut, False) |
| 793 | test_class.dut.log.info('WiFi is OFF') |
| 794 | time.sleep(1) |
| 795 | |
| 796 | # Set the desired screen status |
| 797 | if screen_status == 'OFF': |
| 798 | test_class.dut.droid.goToSleepNow() |
| 799 | test_class.dut.log.info('Screen is OFF') |
| 800 | time.sleep(1) |
Qi Jiang | 93b2d5b | 2018-02-25 03:30:46 +0000 | [diff] [blame] | 801 | |
| 802 | if brconfigs: |
| 803 | return brconfigs |
| 804 | else: |
| 805 | return None |