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