1#!/usr/bin/env python3
2#
3#   Copyright 2022 - 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
17from enum import IntEnum, unique
18
19
20@unique
21class ApReachability(IntEnum):
22    """Neighbor Report AP Reachability values.
23
24    See IEEE 802.11-2020 Figure 9-172.
25    """
26    NOT_REACHABLE = 1
27    UNKNOWN = 2
28    REACHABLE = 3
29
30
31class BssidInformationCapabilities:
32    """Representation of Neighbor Report BSSID Information Capabilities.
33
34    See IEEE 802.11-2020 Figure 9-338 and 9.4.1.4.
35    """
36
37    def __init__(self,
38                 spectrum_management: bool = False,
39                 qos: bool = False,
40                 apsd: bool = False,
41                 radio_measurement: bool = False):
42        """Create a capabilities object.
43
44        Args:
45            spectrum_management: whether spectrum management is required.
46            qos: whether QoS is implemented.
47            apsd: whether APSD is implemented.
48            radio_measurement: whether radio measurement is activated.
49        """
50        self._spectrum_management = spectrum_management
51        self._qos = qos
52        self._apsd = apsd
53        self._radio_measurement = radio_measurement
54
55    def __index__(self) -> int:
56        """Convert to numeric representation of the field's bits."""
57        return self.spectrum_management << 5 \
58            | self.qos << 4 \
59            | self.apsd << 3 \
60            | self.radio_measurement << 2
61
62    @property
63    def spectrum_management(self) -> bool:
64        return self._spectrum_management
65
66    @property
67    def qos(self) -> bool:
68        return self._qos
69
70    @property
71    def apsd(self) -> bool:
72        return self._apsd
73
74    @property
75    def radio_measurement(self) -> bool:
76        return self._radio_measurement
77
78
79class BssidInformation:
80    """Representation of Neighbor Report BSSID Information field.
81
82    BssidInformation contains info about a neighboring AP, to be included in a
83    neighbor report element. See IEEE 802.11-2020 Figure 9-337.
84    """
85
86    def __init__(self,
87                 ap_reachability: ApReachability = ApReachability.UNKNOWN,
88                 security: bool = False,
89                 key_scope: bool = False,
90                 capabilities:
91                 BssidInformationCapabilities = BssidInformationCapabilities(),
92                 mobility_domain: bool = False,
93                 high_throughput: bool = False,
94                 very_high_throughput: bool = False,
95                 ftm: bool = False):
96        """Create a BSSID Information object for a neighboring AP.
97
98        Args:
99            ap_reachability: whether this AP is reachable by the STA that
100                requested the neighbor report.
101            security: whether this AP is known to support the same security
102                provisioning as used by the STA in its current association.
103            key_scope: whether this AP is known to have the same
104                authenticator as the AP sending the report.
105            capabilities: selected capabilities of this AP.
106            mobility_domain: whether the AP is including an MDE in its beacon
107                frames and the contents of that MDE are identical to the MDE
108                advertised by the AP sending the report.
109            high_throughput: whether the AP is an HT AP including the HT
110                Capabilities element in its Beacons, and that the contents of
111                that HT capabilities element are identical to the HT
112                capabilities element advertised by the AP sending the report.
113            very_high_throughput: whether the AP is a VHT AP and the VHT
114                capabilities element, if included as a subelement, is
115                identical in content to the VHT capabilities element included
116                in the AP’s beacon.
117            ftm: whether the AP is known to have the Fine Timing Measurement
118                Responder extended capability.
119        """
120        self._ap_reachability = ap_reachability
121        self._security = security
122        self._key_scope = key_scope
123        self._capabilities = capabilities
124        self._mobility_domain = mobility_domain
125        self._high_throughput = high_throughput
126        self._very_high_throughput = very_high_throughput
127        self._ftm = ftm
128
129    def __index__(self) -> int:
130        """Convert to numeric representation of the field's bits."""
131        return self._ap_reachability << 30 \
132            | self.security << 29 \
133            | self.key_scope << 28 \
134            | int(self.capabilities) << 22 \
135            | self.mobility_domain << 21 \
136            | self.high_throughput << 20 \
137            | self.very_high_throughput << 19 \
138            | self.ftm << 18
139
140    @property
141    def security(self) -> bool:
142        return self._security
143
144    @property
145    def key_scope(self) -> bool:
146        return self._key_scope
147
148    @property
149    def capabilities(self) -> BssidInformationCapabilities:
150        return self._capabilities
151
152    @property
153    def mobility_domain(self) -> bool:
154        return self._mobility_domain
155
156    @property
157    def high_throughput(self) -> bool:
158        return self._high_throughput
159
160    @property
161    def very_high_throughput(self) -> bool:
162        return self._very_high_throughput
163
164    @property
165    def ftm(self) -> bool:
166        return self._ftm
167
168
169@unique
170class PhyType(IntEnum):
171    """PHY type values, see dot11PhyType in 802.11-2020 Annex C."""
172    DSSS = 2
173    OFDM = 4
174    HRDSS = 5
175    ERP = 6
176    HT = 7
177    DMG = 8
178    VHT = 9
179    TVHT = 10
180    S1G = 11
181    CDMG = 12
182    CMMG = 13
183
184
185class NeighborReportElement:
186    """Representation of Neighbor Report element.
187
188    See IEEE 802.11-2020 9.4.2.36.
189    """
190
191    def __init__(self, bssid: str, bssid_information: BssidInformation,
192                 operating_class: int, channel_number: int, phy_type: PhyType):
193        """Create a neighbor report element.
194
195        Args:
196            bssid: MAC address of the neighbor.
197            bssid_information: BSSID Information of the neigbor.
198            operating_class: operating class of the neighbor.
199            channel_number: channel number of the neighbor.
200            phy_type: dot11PhyType of the neighbor.
201        """
202        self._bssid = bssid
203        self._bssid_information = bssid_information
204
205        # Operating Class, IEEE 802.11-2020 Annex E.
206        self._operating_class = operating_class
207
208        self._channel_number = channel_number
209
210        # PHY Type, IEEE 802.11-2020 Annex C.
211        self._phy_type = phy_type
212
213    @property
214    def bssid(self) -> str:
215        return self._bssid
216
217    @property
218    def bssid_information(self) -> BssidInformation:
219        return self._bssid_information
220
221    @property
222    def operating_class(self) -> int:
223        return self._operating_class
224
225    @property
226    def channel_number(self) -> int:
227        return self._channel_number
228
229    @property
230    def phy_type(self) -> PhyType:
231        return self._phy_type
232