1#!/usr/bin/python3.4
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import queue
18
19from acts import asserts
20from acts.test_decorators import test_tracker_info
21from acts.test_utils.wifi import wifi_test_utils as wutils
22from acts.test_utils.wifi.rtt import rtt_const as rconsts
23from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
24from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
25
26
27class RangeApSupporting11McTest(RttBaseTest):
28  """Test class for RTT ranging to Access Points which support IEEE 802.11mc"""
29
30  # Number of RTT iterations
31  NUM_ITER = 10
32
33  # Time gap (in seconds) between iterations
34  TIME_BETWEEN_ITERATIONS = 0
35
36  def __init__(self, controllers):
37    RttBaseTest.__init__(self, controllers)
38
39  #############################################################################
40
41  @test_tracker_info(uuid="6705270f-924b-4bef-b50a-0f0a7eb9ce52")
42  def test_rtt_80211mc_supporting_aps(self):
43    """Scan for APs and perform RTT only to those which support 802.11mc"""
44    dut = self.android_devices[0]
45    rtt_supporting_aps = rutils.select_best_scan_results(
46      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
47      select_count=2)
48    dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
49    events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
50                                self.TIME_BETWEEN_ITERATIONS)
51    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
52                                   self.rtt_reference_distance_margin_mm,
53                                   self.rtt_min_expected_rssi_dbm,
54                                   self.lci_reference, self.lcr_reference)
55    dut.log.debug("Stats=%s", stats)
56
57    for bssid, stat in stats.items():
58      asserts.assert_true(stat['num_no_results'] == 0,
59                          "Missing (timed-out) results", extras=stats)
60      asserts.assert_false(stat['any_lci_mismatch'],
61                           "LCI mismatch", extras=stats)
62      asserts.assert_false(stat['any_lcr_mismatch'],
63                           "LCR mismatch", extras=stats)
64      asserts.assert_false(stat['invalid_num_attempted'],
65                           "Invalid (0) number of attempts", extras=stats)
66      asserts.assert_false(stat['invalid_num_successful'],
67                           "Invalid (0) number of successes", extras=stats)
68      asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
69                          extras=stats)
70      asserts.assert_true(stat['num_failures'] <=
71              self.rtt_max_failure_rate_two_sided_rtt_percentage
72                          * stat['num_results'] / 100,
73              "Failure rate is too high", extras=stats)
74      asserts.assert_true(stat['num_range_out_of_margin'] <=
75              self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
76                          * stat['num_success_results'] / 100,
77              "Results exceeding error margin rate is too high", extras=stats)
78    asserts.explicit_pass("RTT test done", extras=stats)
79
80  #########################################################################
81  #
82  # LEGACY API test code
83  #
84  #########################################################################
85
86  @test_tracker_info(uuid="18be9737-2f03-4e35-9a23-f722dea7b82d")
87  def test_legacy_rtt_80211mc_supporting_aps(self):
88    """Scan for APs and perform RTT only to those which support 802.11mc - using
89    the LEGACY API!"""
90    dut = self.android_devices[0]
91    rtt_supporting_aps = rutils.select_best_scan_results(
92      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
93      select_count=2)
94    dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
95
96    rtt_configs = []
97    for ap in rtt_supporting_aps:
98      rtt_configs.append(self.rtt_config_from_scan_result(ap))
99    dut.log.debug("RTT configs=%s", rtt_configs)
100
101    results = []
102    num_missing = 0
103    num_failed_aborted = 0
104    for i in range(self.NUM_ITER):
105        idx = dut.droid.wifiRttStartRanging(rtt_configs)
106        event = None
107        try:
108          events = dut.ed.pop_events("WifiRttRanging%d" % idx, 30)
109          dut.log.debug("Event=%s", events)
110          for event in events:
111            if rconsts.EVENT_CB_RANGING_KEY_RESULTS in event["data"]:
112              results.append(
113                  event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS])
114            else:
115              self.log.info("RTT failed/aborted - %s", event)
116              results.append([])
117              num_failed_aborted = num_failed_aborted + 1
118        except queue.Empty:
119          self.log.debug("Waiting for RTT event timed out.")
120          results.append([])
121          num_missing = num_missing + 1
122
123    # basic error checking:
124    # 1. no missing
125    # 2. no full failed/aborted (i.e. operation not even tried)
126    # 3. overall (all BSSIDs) success rate > threshold
127    asserts.assert_equal(num_missing, 0,
128                         "Missing results (timeout waiting for event)",
129                         extras={"data":results})
130    asserts.assert_equal(num_failed_aborted, 0,
131                         "Failed or aborted operations (not tried)",
132                         extras={"data":results})
133
134    num_results = 0
135    num_errors = 0
136    for result_group in results:
137      num_results = num_results + len(result_group)
138      for result in result_group:
139        if result["status"] != 0:
140          num_errors = num_errors + 1
141
142    extras = [results, {"num_results": num_results, "num_errors": num_errors}]
143    asserts.assert_true(
144      num_errors <= self.rtt_max_failure_rate_two_sided_rtt_percentage
145        * num_results / 100,
146      "Failure rate is too high", extras={"data":extras})
147    asserts.explicit_pass("RTT test done", extras={"data": extras})
148
149  def rtt_config_from_scan_result(self, scan_result):
150    """Creates an Rtt configuration based on the scan result of a network.
151    """
152    WifiEnums = wutils.WifiEnums
153    ScanResult = WifiEnums.ScanResult
154    RttParam = WifiEnums.RttParam
155    RttBW = WifiEnums.RttBW
156    RttPreamble = WifiEnums.RttPreamble
157    RttType = WifiEnums.RttType
158
159    scan_result_channel_width_to_rtt = {
160      ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
161      ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
162      ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
163      ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
164      ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
165    }
166    p = {}
167    freq = scan_result[RttParam.frequency]
168    p[RttParam.frequency] = freq
169    p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
170    if freq > 5000:
171      p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
172    else:
173      p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
174    cf0 = scan_result[RttParam.center_freq0]
175    if cf0 > 0:
176      p[RttParam.center_freq0] = cf0
177    cf1 = scan_result[RttParam.center_freq1]
178    if cf1 > 0:
179      p[RttParam.center_freq1] = cf1
180    cw = scan_result["channelWidth"]
181    p[RttParam.channel_width] = cw
182    p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
183    if scan_result["is80211McRTTResponder"]:
184      p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
185    else:
186      p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
187    return p
188