• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.wifi.util;
17 
18 import android.net.MacAddress;
19 import android.net.wifi.MloLink;
20 import android.net.wifi.ScanResult;
21 import android.net.wifi.ScanResult.InformationElement;
22 import android.net.wifi.WifiAnnotations.Cipher;
23 import android.net.wifi.WifiAnnotations.KeyMgmt;
24 import android.net.wifi.WifiAnnotations.Protocol;
25 import android.net.wifi.WifiScanner;
26 import android.net.wifi.nl80211.NativeScanResult;
27 import android.net.wifi.nl80211.WifiNl80211Manager;
28 import android.net.wifi.util.HexEncoding;
29 import android.util.Log;
30 
31 import com.android.server.wifi.ByteBufferReader;
32 import com.android.server.wifi.MboOceConstants;
33 import com.android.server.wifi.hotspot2.NetworkDetail;
34 import com.android.server.wifi.hotspot2.anqp.Constants;
35 
36 import java.nio.BufferUnderflowException;
37 import java.nio.ByteBuffer;
38 import java.nio.ByteOrder;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.BitSet;
42 import java.util.List;
43 import java.util.Locale;
44 
45 public class InformationElementUtil {
46     private static final String TAG = "InformationElementUtil";
47     private static final boolean DBG = false;
48 
49     /** Converts InformationElement to hex string */
toHexString(InformationElement e)50     public static String toHexString(InformationElement e) {
51         StringBuilder sb = new StringBuilder();
52         sb.append(HexEncoding.encode(new byte[]{(byte) e.id}));
53         if (e.id == InformationElement.EID_EXTENSION_PRESENT) {
54             sb.append(HexEncoding.encode(new byte[]{(byte) e.idExt}));
55         }
56         sb.append(HexEncoding.encode(new byte[]{(byte) e.bytes.length}));
57         sb.append(HexEncoding.encode(e.bytes));
58         return sb.toString();
59     }
60 
61     /** Parses information elements from hex string */
parseInformationElements(String data)62     public static InformationElement[] parseInformationElements(String data) {
63         if (data == null) {
64             return new InformationElement[0];
65         }
66         return parseInformationElements(HexEncoding.decode(data));
67     }
68 
parseInformationElements(byte[] bytes)69     public static InformationElement[] parseInformationElements(byte[] bytes) {
70         if (bytes == null) {
71             return new InformationElement[0];
72         }
73         ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
74 
75         ArrayList<InformationElement> infoElements = new ArrayList<>();
76         boolean found_ssid = false;
77         while (data.remaining() > 1) {
78             int eid = data.get() & Constants.BYTE_MASK;
79             int eidExt = 0;
80             int elementLength = data.get() & Constants.BYTE_MASK;
81 
82             if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
83                     && found_ssid)) {
84                 // APs often pad the data with bytes that happen to match that of the EID_SSID
85                 // marker.  This is not due to a known issue for APs to incorrectly send the SSID
86                 // name multiple times.
87                 break;
88             }
89             if (eid == InformationElement.EID_SSID) {
90                 found_ssid = true;
91             } else if (eid == InformationElement.EID_EXTENSION_PRESENT) {
92                 if (elementLength == 0) {
93                     // Malformed IE, skipping
94                     break;
95                 }
96                 eidExt = data.get() & Constants.BYTE_MASK;
97                 elementLength--;
98             }
99 
100             InformationElement ie = new InformationElement();
101             ie.id = eid;
102             ie.idExt = eidExt;
103             ie.bytes = new byte[elementLength];
104             data.get(ie.bytes);
105             infoElements.add(ie);
106         }
107         return infoElements.toArray(new InformationElement[infoElements.size()]);
108     }
109 
110     /**
111      * Parse and retrieve the Roaming Consortium Information Element from the list of IEs.
112      *
113      * @param ies List of IEs to retrieve from
114      * @return {@link RoamingConsortium}
115      */
getRoamingConsortiumIE(InformationElement[] ies)116     public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) {
117         RoamingConsortium roamingConsortium = new RoamingConsortium();
118         if (ies != null) {
119             for (InformationElement ie : ies) {
120                 if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) {
121                     try {
122                         roamingConsortium.from(ie);
123                     } catch (RuntimeException e) {
124                         Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage());
125                     }
126                 }
127             }
128         }
129         return roamingConsortium;
130     }
131 
132     /**
133      * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs.
134      *
135      * @param ies List of IEs to retrieve from
136      * @return {@link Vsa}
137      */
getHS2VendorSpecificIE(InformationElement[] ies)138     public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) {
139         Vsa vsa = new Vsa();
140         if (ies != null) {
141             for (InformationElement ie : ies) {
142                 if (ie.id == InformationElement.EID_VSA) {
143                     try {
144                         vsa.from(ie);
145                     } catch (RuntimeException e) {
146                         Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage());
147                     }
148                 }
149             }
150         }
151         return vsa;
152     }
153 
154     /**
155      * Parse and retrieve all Vendor Specific Information Elements from the list of IEs.
156      *
157      * @param ies List of IEs to retrieve from
158      * @return List of {@link Vsa}
159      */
getVendorSpecificIE(InformationElement[] ies)160     public static List<Vsa> getVendorSpecificIE(InformationElement[] ies) {
161         List<Vsa> vsas = new ArrayList<>();
162         if (ies != null) {
163             for (InformationElement ie : ies) {
164                 if (ie.id == InformationElement.EID_VSA) {
165                     try {
166                         Vsa vsa = new Vsa();
167                         vsa.from(ie);
168                         vsas.add(vsa);
169                     } catch (RuntimeException e) {
170                         Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage());
171                     }
172                 }
173             }
174         }
175         return vsas;
176     }
177 
178     /**
179      * Parse and retrieve the Interworking information element from the list of IEs.
180      *
181      * @param ies List of IEs to retrieve from
182      * @return {@link Interworking}
183      */
getInterworkingIE(InformationElement[] ies)184     public static Interworking getInterworkingIE(InformationElement[] ies) {
185         Interworking interworking = new Interworking();
186         if (ies != null) {
187             for (InformationElement ie : ies) {
188                 if (ie.id == InformationElement.EID_INTERWORKING) {
189                     try {
190                         interworking.from(ie);
191                     } catch (RuntimeException e) {
192                         Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage());
193                     }
194                 }
195             }
196         }
197         return interworking;
198     }
199 
200     public static class BssLoad {
201         public static final int INVALID = -1;
202         public static final int MAX_CHANNEL_UTILIZATION = 255;
203         public static final int MIN_CHANNEL_UTILIZATION = 0;
204         public static final int CHANNEL_UTILIZATION_SCALE = 256;
205         public int stationCount = INVALID;
206         public int channelUtilization = INVALID;
207         public int capacity = INVALID;
208 
from(InformationElement ie)209         public void from(InformationElement ie) {
210             if (ie.id != InformationElement.EID_BSS_LOAD) {
211                 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
212             }
213             if (ie.bytes.length != 5) {
214                 throw new IllegalArgumentException("BSS Load element length is not 5: "
215                                                    + ie.bytes.length);
216             }
217             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
218             stationCount = data.getShort() & Constants.SHORT_MASK;
219             channelUtilization = data.get() & Constants.BYTE_MASK;
220             capacity = data.getShort() & Constants.SHORT_MASK;
221         }
222     }
223 
224     /**
225      * Rnr: represents the Reduced Neighbor Report (RNR) IE
226      * As described by IEEE 802.11 Specification Section 9.4.2.170
227      */
228     public static class Rnr {
229         private static final int TBTT_INFO_COUNT_OFFSET = 0;
230         private static final int TBTT_INFO_COUNT_MASK = 0xF0;
231         private static final int TBTT_INFO_COUNT_SHIFT = 4;
232         private static final int TBTT_INFO_LENGTH_OFFSET = 1;
233         private static final int TBTT_INFO_OP_CLASS_OFFSET = 2;
234         private static final int TBTT_INFO_CHANNEL_OFFSET = 3;
235         private static final int TBTT_INFO_SET_START_OFFSET = 4;
236         private static final int MLD_ID_START_OFFSET = 0;
237         private static final int LINK_ID_START_OFFSET = 1;
238         private static final int LINK_ID_MASK = 0x0F;
239 
240         private boolean mPresent = false;
241         private List<MloLink> mAffiliatedMloLinks = new ArrayList<>();
242 
243         /**
244          * Returns whether the RNR Information Element is present.
245          */
isPresent()246         public boolean isPresent() {
247             return mPresent;
248         }
249 
250         /**
251          * Returns the list of the affiliated MLO links
252          */
getAffiliatedMloLinks()253         public List<MloLink> getAffiliatedMloLinks() {
254             return mAffiliatedMloLinks;
255         }
256 
257         /**
258          * Parse RNR Operation IE
259          *
260          * RNR format as described in IEEE 802.11 specs, Section 9.4.2.170
261          *
262          *              | ElementID | Length | Neighbor AP Information Fields |
263          * Octets:            1          1             variable
264          *
265          *
266          * Where Neighbor AP Information Fields is one or more Neighbor AP Information Field as,
267          *
268          *               | Header | Operating Class | Channel | TBTT Information Set |
269          * Octets:            2            1            1           variable
270          *
271          *
272          * The Header subfield is described as follows,
273          *
274          *            | Type  | Filtered AP | Reserved | Count | Length |
275          * Bits:         2          1           1          4       8
276          *
277          *
278          * Information Set is one or more TBTT Information fields, which is described as,
279          *
280          *         | Offset | BSSID  | Short-SSID | BSS Params | 20MHz PSD | MLD Params|
281          * Octets:     1      0 or 6    0 or 4        0 or 1      0 or 1      0 or 3
282          *
283          *
284          * The MLD Params are described as,
285          *       | MLD ID | Link ID | BSS Change Count | Reserved |
286          * Bits:     8        4              8              4
287          *
288          * Note: InformationElement.bytes has 'Element ID' and 'Length'
289          *       stripped off already
290          *
291          */
from(InformationElement ie)292         public void from(InformationElement ie) {
293             if (ie.id != InformationElement.EID_RNR) {
294                 throw new IllegalArgumentException("Element id is not RNR");
295             }
296 
297             int startOffset = 0;
298             while (ie.bytes.length > startOffset + TBTT_INFO_SET_START_OFFSET) {
299                 int tbttInfoCount =
300                         ie.bytes[startOffset + TBTT_INFO_COUNT_OFFSET] & TBTT_INFO_COUNT_MASK;
301                 tbttInfoCount >>= TBTT_INFO_COUNT_SHIFT;
302                 tbttInfoCount++;
303 
304                 int tbttInfoLen =
305                         ie.bytes[startOffset + TBTT_INFO_LENGTH_OFFSET] & Constants.BYTE_MASK;
306                 int tbttInfoStartOffset = startOffset + TBTT_INFO_SET_START_OFFSET;
307 
308                 // Only handle TBTT info with MLD Info
309                 if (tbttInfoLen == 4 || tbttInfoLen >= 16) {
310                     // Make sure length allows for this TBTT Info
311                     if (ie.bytes.length < startOffset + TBTT_INFO_SET_START_OFFSET
312                             + tbttInfoLen * tbttInfoCount) {
313                         if (DBG) {
314                             Log.w(TAG, "Invalid RNR len, not enough for TBTT Info: "
315                                     + ie.bytes.length + "/" + tbttInfoLen + "/" + tbttInfoCount);
316                         }
317                         // Skipping parsing of the IE
318                         return;
319                     }
320 
321                     int mldStartOffset;
322                     int bssidOffset;
323 
324                     if (tbttInfoLen == 4) {
325                         mldStartOffset = 1;
326                         bssidOffset = -1;
327                     } else {
328                         mldStartOffset = 13;
329                         bssidOffset = 1;
330                     }
331 
332                     int opClass = ie.bytes[startOffset + TBTT_INFO_OP_CLASS_OFFSET]
333                             & Constants.BYTE_MASK;
334                     int channel = ie.bytes[startOffset + TBTT_INFO_CHANNEL_OFFSET]
335                             & Constants.BYTE_MASK;
336                     int band = ScanResult.getBandFromOpClass(opClass, channel);
337                     if (band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
338                         if (DBG) {
339                             Log.w(TAG, "Invalid op class/channel in RNR TBTT Info: "
340                                     + opClass + "/" + channel);
341                         }
342                         // Skipping parsing of the IE
343                         return;
344                     }
345                     for (int i = 0; i < tbttInfoCount; i++) {
346                         int mldId = ie.bytes[tbttInfoStartOffset + mldStartOffset
347                                 + MLD_ID_START_OFFSET] & Constants.BYTE_MASK;
348                         if (mldId == 0) {
349                             //This is an affiliated link
350                             int linkId = ie.bytes[tbttInfoStartOffset + mldStartOffset
351                                     + LINK_ID_START_OFFSET] & LINK_ID_MASK;
352                             MloLink link = new MloLink();
353                             link.setLinkId(linkId);
354                             link.setBand(band);
355                             link.setChannel(channel);
356                             if (bssidOffset != -1) {
357                                 int macAddressStart = tbttInfoStartOffset + bssidOffset;
358                                 link.setApMacAddress(MacAddress.fromBytes(
359                                         Arrays.copyOfRange(ie.bytes,
360                                                 macAddressStart, macAddressStart + 6)));
361                             }
362 
363                             mAffiliatedMloLinks.add(link);
364                         }
365                         tbttInfoStartOffset += tbttInfoLen;
366                     }
367                 }
368 
369                 startOffset += TBTT_INFO_SET_START_OFFSET + (tbttInfoCount * tbttInfoLen);
370             }
371 
372             // Done with parsing
373             mPresent = true;
374         }
375     }
376 
377     public static class HtOperation {
378         private static final int HT_OPERATION_IE_LEN = 22;
379         private boolean mPresent = false;
380         private int mSecondChannelOffset = 0;
381 
382         /**
383          * returns if HT Operation IE present in the message.
384          */
isPresent()385         public boolean isPresent() {
386             return mPresent;
387         }
388 
389         /**
390          * Returns channel width if it is 20 or 40MHz
391          * Results will be invalid if channel width greater than 40MHz
392          * So caller should only call this method if VHT Operation IE is not present,
393          * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED.
394          */
getChannelWidth()395         public int getChannelWidth() {
396             if (mSecondChannelOffset != 0) {
397                 return ScanResult.CHANNEL_WIDTH_40MHZ;
398             } else {
399                 return ScanResult.CHANNEL_WIDTH_20MHZ;
400             }
401         }
402 
403         /**
404          * Returns channel Center frequency (for 20/40 MHz channels only)
405          * Results will be invalid for larger channel width,
406          * So, caller should only call this method if VHT Operation IE is not present,
407          * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED.
408          */
getCenterFreq0(int primaryFrequency)409         public int getCenterFreq0(int primaryFrequency) {
410             if (mSecondChannelOffset != 0) {
411                 //40 MHz
412                 if (mSecondChannelOffset == 1) {
413                     return primaryFrequency + 10;
414                 } else if (mSecondChannelOffset == 3) {
415                     return primaryFrequency - 10;
416                 } else {
417                     Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset);
418                     return 0;
419                 }
420             } else {
421                 //20 MHz
422                 return primaryFrequency;
423             }
424         }
425 
426         /**
427          * Parse the HT Operation IE to read the fields of interest.
428          */
from(InformationElement ie)429         public void from(InformationElement ie) {
430             if (ie.id != InformationElement.EID_HT_OPERATION) {
431                 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
432             }
433             if (ie.bytes.length < HT_OPERATION_IE_LEN) {
434                 throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length);
435             }
436             mPresent = true;
437             mSecondChannelOffset = ie.bytes[1] & 0x3;
438         }
439     }
440 
441     public static class VhtOperation {
442         private static final int VHT_OPERATION_IE_LEN = 5;
443         private boolean mPresent = false;
444         private int mChannelMode = 0;
445         private int mCenterFreqIndex1 = 0;
446         private int mCenterFreqIndex2 = 0;
447 
448         /**
449          * returns if VHT Operation IE present in the message.
450          */
isPresent()451         public boolean isPresent() {
452             return mPresent;
453         }
454 
455         /**
456          * Returns channel width if it is above 40MHz,
457          * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that
458          * channel width should be obtained from the HT Operation IE via
459          * HtOperation.getChannelWidth().
460          */
getChannelWidth()461         public int getChannelWidth() {
462             if (mChannelMode == 0) {
463                 // 20 or 40MHz
464                 return ScanResult.UNSPECIFIED;
465             } else if (mCenterFreqIndex2 == 0) {
466                 // No secondary channel
467                 return ScanResult.CHANNEL_WIDTH_80MHZ;
468             } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) {
469                 // Primary and secondary channels adjacent
470                 return ScanResult.CHANNEL_WIDTH_160MHZ;
471             } else {
472                 // Primary and secondary channels not adjacent
473                 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
474             }
475         }
476 
477         /**
478          * Returns center frequency of primary channel (if channel width greater than 40MHz),
479          * otherwise, it returns zero to indicate that center frequency should be obtained from
480          * the HT Operation IE via HtOperation.getCenterFreq0().
481          */
getCenterFreq0()482         public int getCenterFreq0() {
483             if (mCenterFreqIndex1 == 0 || mChannelMode == 0) {
484                 return 0;
485             } else {
486                 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex1,
487                         WifiScanner.WIFI_BAND_5_GHZ);
488             }
489         }
490 
491          /**
492          * Returns center frequency of secondary channel if exists (channel width greater than
493          * 40MHz), otherwise, it returns zero.
494          * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz
495          * channels.
496          */
getCenterFreq1()497         public int getCenterFreq1() {
498             if (mCenterFreqIndex2 == 0 || mChannelMode == 0) {
499                 return 0;
500             } else {
501                 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex2,
502                         WifiScanner.WIFI_BAND_5_GHZ);
503             }
504         }
505 
506         /**
507          * Parse the VHT Operation IE to read the fields of interest.
508          */
from(InformationElement ie)509         public void from(InformationElement ie) {
510             if (ie.id != InformationElement.EID_VHT_OPERATION) {
511                 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
512             }
513             if (ie.bytes.length < VHT_OPERATION_IE_LEN) {
514                 throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length);
515             }
516             mPresent = true;
517             mChannelMode = ie.bytes[0] & Constants.BYTE_MASK;
518             mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
519             mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
520         }
521     }
522 
523     /**
524      * HeOperation: represents the HE Operation IE
525      */
526     public static class HeOperation {
527 
528         private static final int HE_OPERATION_BASIC_LENGTH = 6;
529         private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40;
530         private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02;
531         private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03;
532         private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80;
533         private static final int VHT_OPERATION_INFO_START_INDEX = 6;
534         private static final int HE_BW_80_80_160 = 3;
535 
536         private boolean mPresent = false;
537         private boolean mVhtInfoPresent = false;
538         private boolean m6GhzInfoPresent = false;
539         private int mChannelWidth;
540         private int mPrimaryChannel;
541         private int mCenterFreqSeg0;
542         private int mCenterFreqSeg1;
543         private InformationElement mVhtInfo = null;
544 
545         /**
546          * Returns whether the HE Information Element is present.
547          */
isPresent()548         public boolean isPresent() {
549             return mPresent;
550         }
551 
552         /**
553          * Returns whether VHT Information field is present.
554          */
isVhtInfoPresent()555         public boolean isVhtInfoPresent() {
556             return mVhtInfoPresent;
557         }
558 
559         /**
560          * Returns the VHT Information Element if it exists
561          * otherwise, return null.
562          */
getVhtInfoElement()563         public InformationElement getVhtInfoElement() {
564             return mVhtInfo;
565         }
566 
567         /**
568          * Returns whether the 6GHz information field is present.
569          */
is6GhzInfoPresent()570         public boolean is6GhzInfoPresent() {
571             return m6GhzInfoPresent;
572         }
573 
574         /**
575          * Returns the Channel BW
576          * Only applicable to 6GHz band
577          */
getChannelWidth()578         public int getChannelWidth() {
579             if (!m6GhzInfoPresent) {
580                 return ScanResult.UNSPECIFIED;
581             } else if (mChannelWidth == 0) {
582                 return ScanResult.CHANNEL_WIDTH_20MHZ;
583             } else if (mChannelWidth == 1) {
584                 return ScanResult.CHANNEL_WIDTH_40MHZ;
585             } else if (mChannelWidth == 2) {
586                 return ScanResult.CHANNEL_WIDTH_80MHZ;
587             } else if (Math.abs(mCenterFreqSeg1 - mCenterFreqSeg0) == 8) {
588                 return ScanResult.CHANNEL_WIDTH_160MHZ;
589             } else {
590                 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
591             }
592         }
593 
594         /**
595          * Returns the primary channel frequency
596          * Only applicable for 6GHz channels
597          */
getPrimaryFreq()598         public int getPrimaryFreq() {
599             return ScanResult.convertChannelToFrequencyMhzIfSupported(mPrimaryChannel,
600                         WifiScanner.WIFI_BAND_6_GHZ);
601         }
602 
603         /**
604          * Returns the center frequency for the primary channel
605          * Only applicable to 6GHz channels
606          */
getCenterFreq0()607         public int getCenterFreq0() {
608             if (m6GhzInfoPresent) {
609                 if (mCenterFreqSeg0 == 0) {
610                     return 0;
611                 } else {
612                     return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0,
613                             WifiScanner.WIFI_BAND_6_GHZ);
614                 }
615             } else {
616                 return 0;
617             }
618         }
619 
620         /**
621          * Returns the center frequency for the secondary channel
622          * Only applicable to 6GHz channels
623          */
getCenterFreq1()624         public int getCenterFreq1() {
625             if (m6GhzInfoPresent) {
626                 if (mCenterFreqSeg1 == 0) {
627                     return 0;
628                 } else {
629                     return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1,
630                             WifiScanner.WIFI_BAND_6_GHZ);
631                 }
632             } else {
633                 return 0;
634             }
635         }
636 
637         /** Parse HE Operation IE */
from(InformationElement ie)638         public void from(InformationElement ie) {
639             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
640                     || ie.idExt != InformationElement.EID_EXT_HE_OPERATION) {
641                 throw new IllegalArgumentException("Element id is not HE_OPERATION");
642             }
643 
644             // Make sure the byte array length is at least the fixed size
645             if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) {
646                 if (DBG) {
647                     Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
648                 }
649                 // Skipping parsing of the IE
650                 return;
651             }
652 
653             mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0;
654             m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0;
655             boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0;
656             int expectedLen = HE_OPERATION_BASIC_LENGTH + (mVhtInfoPresent ? 3 : 0)
657                     + (coHostedBssPresent ? 1 : 0) + (m6GhzInfoPresent ? 5 : 0);
658 
659             // Make sure the byte array length is at least fitting the known parameters
660             if (ie.bytes.length < expectedLen) {
661                 if (DBG) {
662                     Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
663                 }
664                 // Skipping parsing of the IE
665                 return;
666             }
667 
668             // Passed all checks, IE is ready for decoding
669             mPresent = true;
670 
671             if (mVhtInfoPresent) {
672                 mVhtInfo = new InformationElement();
673                 mVhtInfo.id = InformationElement.EID_VHT_OPERATION;
674                 mVhtInfo.bytes = new byte[5];
675                 System.arraycopy(ie.bytes, VHT_OPERATION_INFO_START_INDEX, mVhtInfo.bytes, 0, 3);
676             }
677 
678             if (m6GhzInfoPresent) {
679                 int startIndx = VHT_OPERATION_INFO_START_INDEX + (mVhtInfoPresent ? 3 : 0)
680                         + (coHostedBssPresent ? 1 : 0);
681 
682                 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK;
683                 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK;
684                 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK;
685                 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK;
686             }
687         }
688     }
689 
690     /**
691      * EhtOperation: represents the EHT Operation IE
692      */
693     public static class EhtOperation {
694         private boolean mPresent = false;
695 
696         /**
697          * Returns whether the EHT Information Element is present.
698          */
isPresent()699         public boolean isPresent() {
700             return mPresent;
701         }
702 
703         /** Parse EHT Operation IE */
from(InformationElement ie)704         public void from(InformationElement ie) {
705             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
706                     || ie.idExt != InformationElement.EID_EXT_EHT_OPERATION) {
707                 throw new IllegalArgumentException("Element id is not EHT_OPERATION");
708             }
709 
710             mPresent = true;
711 
712             //TODO put more functionality for parsing the IE
713         }
714     }
715 
716     /**
717      * HtCapabilities: represents the HT Capabilities IE
718      */
719     public static class HtCapabilities {
720         private int mMaxNumberSpatialStreams  = 1;
721         private boolean mPresent = false;
722         /** Returns whether HT Capabilities IE is present */
isPresent()723         public boolean isPresent() {
724             return mPresent;
725         }
726         /**
727          * Returns max number of spatial streams if HT Capabilities IE is found and parsed,
728          * or 1 otherwise
729          */
getMaxNumberSpatialStreams()730         public int getMaxNumberSpatialStreams() {
731             return mMaxNumberSpatialStreams;
732         }
733 
734         /** Parse HT Capabilities IE */
from(InformationElement ie)735         public void from(InformationElement ie) {
736             if (ie.id != InformationElement.EID_HT_CAPABILITIES) {
737                 throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id);
738             }
739             if (ie.bytes.length < 26) {
740                 if (DBG) {
741                     Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length);
742                 }
743                 return;
744             }
745             int stream1 = ie.bytes[3] & Constants.BYTE_MASK;
746             int stream2 = ie.bytes[4] & Constants.BYTE_MASK;
747             int stream3 = ie.bytes[5] & Constants.BYTE_MASK;
748             int stream4 = ie.bytes[6] & Constants.BYTE_MASK;
749             if (DBG) {
750                 Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4));
751                 Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3));
752                 Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2));
753                 Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1));
754             }
755             mMaxNumberSpatialStreams = (stream4 > 0) ? 4 :
756                     ((stream3 > 0) ? 3 :
757                     ((stream2 > 0) ? 2 : 1));
758             mPresent = true;
759         }
760     }
761 
762     /**
763      * VhtCapabilities: represents the VHT Capabilities IE
764      */
765     public static class VhtCapabilities {
766         private int mMaxNumberSpatialStreams = 1;
767         private boolean mPresent = false;
768         /** Returns whether VHT Capabilities IE is present */
isPresent()769         public boolean isPresent() {
770             return mPresent;
771         }
772         /**
773          * Returns max number of spatial streams if VHT Capabilities IE is found and parsed,
774          * or 1 otherwise
775          */
getMaxNumberSpatialStreams()776         public int getMaxNumberSpatialStreams() {
777             return mMaxNumberSpatialStreams;
778         }
779         /** Parse VHT Capabilities IE */
from(InformationElement ie)780         public void from(InformationElement ie) {
781             if (ie.id != InformationElement.EID_VHT_CAPABILITIES) {
782                 throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id);
783             }
784             if (ie.bytes.length < 12) {
785                 if (DBG) {
786                     Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length);
787                 }
788                 return;
789             }
790             int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8)
791                     + (ie.bytes[4] & Constants.BYTE_MASK);
792             mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
793             mPresent = true;
794         }
795     }
796 
797     /**
798      * HeCapabilities: represents the HE Capabilities IE
799      */
800     public static class HeCapabilities {
801         private int mMaxNumberSpatialStreams = 1;
802         private boolean mPresent = false;
803         /** Returns whether HE Capabilities IE is present */
isPresent()804         public boolean isPresent() {
805             return mPresent;
806         }
807         /**
808          * Returns max number of spatial streams if HE Capabilities IE is found and parsed,
809          * or 1 otherwise
810          */
getMaxNumberSpatialStreams()811         public int getMaxNumberSpatialStreams() {
812             return mMaxNumberSpatialStreams;
813         }
814         /** Parse HE Capabilities IE */
from(InformationElement ie)815         public void from(InformationElement ie) {
816             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
817                     || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) {
818                 throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id);
819             }
820             if (ie.bytes.length < 21) {
821                 if (DBG) {
822                     Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length);
823                 }
824                 return;
825             }
826             int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8)
827                     + (ie.bytes[17] & Constants.BYTE_MASK);
828             mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
829             mPresent = true;
830         }
831     }
832 
833     /**
834      * EhtCapabilities: represents the EHT Capabilities IE
835      */
836     public static class EhtCapabilities {
837         private boolean mPresent = false;
838         /** Returns whether HE Capabilities IE is present */
isPresent()839         public boolean isPresent() {
840             return mPresent;
841         }
842 
843         /** Parse EHT Capabilities IE */
from(InformationElement ie)844         public void from(InformationElement ie) {
845             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
846                     || ie.idExt != InformationElement.EID_EXT_EHT_CAPABILITIES) {
847                 throw new IllegalArgumentException("Element id is not EHT_CAPABILITIES: " + ie.id);
848             }
849             mPresent = true;
850 
851             //TODO Add code to parse the IE
852         }
853     }
854 
855     /**
856      * MultiLink: represents the Multi-Link IE
857      * as described in IEEE 802.11be Specification Section 9.4.2.312
858      */
859     public static class MultiLink {
860         private static final int CONTROL_FIELD_LEN = 2;
861         private static final int BASIC_COMMON_INFO_FIELD_MIN_LEN = 7;
862         private static final int BASIC_LINK_INFO_FIELD_MIN_LEN = 0;
863         private static final int BASIC_IE_MIN_LEN = CONTROL_FIELD_LEN
864                 + BASIC_COMMON_INFO_FIELD_MIN_LEN
865                 + BASIC_LINK_INFO_FIELD_MIN_LEN;
866 
867         // Control field constants
868         private static final int IE_TYPE_OFFSET = 0;
869         private static final int IE_TYPE_MASK = 0x07;
870         public static final int TYPE_BASIC = 0;
871         public static final int LINK_ID_PRESENT_OFFSET = 0;
872         public static final int LINK_ID_PRESENT_MASK = 0x10;
873 
874 
875         // Common info field constants
876         private static final int COMMON_FIELD_START_INDEX = CONTROL_FIELD_LEN;
877         private static final int BASIC_IE_COMMON_INFO_LEN_OFFSET = 0;
878         private static final int BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET = 1;
879         private static final int BASIC_IE_COMMOM_LINK_ID_OFFSET = 7;
880         private static final int BASIC_IE_COMMOM_LINK_ID_MASK = 0x0F;
881 
882         // Per-STA sub-element constants
883         private static final int PER_STA_SUB_ELEMENT_ID = 0;
884         private static final int PER_STA_SUB_ELEMENT_MIN_LEN = 5;
885         private static final int PER_STA_SUB_ELEMENT_LINK_ID_OFFSET = 2;
886         private static final int PER_STA_SUB_ELEMENT_LINK_ID_MASK = 0x0F;
887         private static final int PER_STA_SUB_ELEMENT_STA_INFO_OFFSET = 4;
888         private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET = 2;
889         private static final int PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK = 0x20;
890         private static final int PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET = 1;
891 
892         private boolean mPresent = false;
893         private int mLinkId = MloLink.INVALID_MLO_LINK_ID;
894         private MacAddress mMldMacAddress = null;
895         private List<MloLink> mAffiliatedLinks = new ArrayList<>();
896 
897         /** Returns whether Multi-Link IE is present */
isPresent()898         public boolean isPresent() {
899             return mPresent;
900         }
901 
902         /** Returns the MLD MAC Address */
getMldMacAddress()903         public MacAddress getMldMacAddress() {
904             return mMldMacAddress;
905         }
906 
907         /** Return the link id */
getLinkId()908         public int getLinkId() {
909             return mLinkId;
910         }
911 
912         /** Return the affiliated links */
getAffiliatedLinks()913         public List<MloLink> getAffiliatedLinks() {
914             return new ArrayList<MloLink>(mAffiliatedLinks);
915         }
916 
917         /**
918          * Parse Common Info field in Multi-Link Operation IE
919          *
920          * Common Info filed as described in IEEE 802.11 specs, Section 9.4.2.312,
921          *
922          *        | Len | MLD Address | Link Id | BSS Change count | MedSync | EML Cap | MLD Cap |
923          * Octets:   1        6          0 or 1        0 or 1         0 or 2    0 or 2    0 or 2
924          *
925          */
parseCommonInfoField(InformationElement ie)926         private int parseCommonInfoField(InformationElement ie) {
927             int commonInfoLength = ie.bytes[COMMON_FIELD_START_INDEX
928                     + BASIC_IE_COMMON_INFO_LEN_OFFSET] & Constants.BYTE_MASK;
929             if (commonInfoLength < BASIC_COMMON_INFO_FIELD_MIN_LEN) {
930                 if (DBG) {
931                     Log.w(TAG, "Invalid Common Info field length: " + commonInfoLength);
932                 }
933                 // Skipping parsing of the IE
934                 return 0;
935             }
936 
937             boolean isLinkIdInfoPresent = (ie.bytes[LINK_ID_PRESENT_OFFSET]
938                     & LINK_ID_PRESENT_MASK) != 0;
939             if (isLinkIdInfoPresent) {
940                 if (ie.bytes.length < BASIC_IE_MIN_LEN + 1 /*Link Id info */) {
941                     if (DBG) {
942                         Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length);
943                     }
944                     // Skipping parsing of the IE
945                     return 0;
946                 }
947 
948                 mLinkId = ie.bytes[COMMON_FIELD_START_INDEX
949                         + BASIC_IE_COMMOM_LINK_ID_OFFSET] & BASIC_IE_COMMOM_LINK_ID_MASK;
950             }
951 
952             int macAddressStart = COMMON_FIELD_START_INDEX + BASIC_IE_COMMON_MLD_MAC_ADDRESS_OFFSET;
953             mMldMacAddress = MacAddress.fromBytes(
954                     Arrays.copyOfRange(ie.bytes, macAddressStart, macAddressStart + 6));
955 
956             return commonInfoLength;
957         }
958 
959         /**
960          * Parse Link Info field in Multi-Link Operation IE
961          *
962          * Link Info filed as described in IEEE 802.11 specs, Section 9.4.2.312,
963          *
964          *        | ID | Len | STA Control | STA Info | STA Profile |
965          * Octets:  1     1        2         variable    variable
966          *
967          * where STA Control subfield is described as,
968          *
969          *      | LinkId | Complete | MAC | Beacon Interval | DTIM | NSTR Link | NSTR Bitmap | R |
970          * Bits:    4          1       1          1             1        1            1        6
971          *
972          */
parseLinkInfoField(InformationElement ie, int startOffset)973         private boolean parseLinkInfoField(InformationElement ie, int startOffset) {
974             // Check if Link Info field is present
975             while (ie.bytes.length >= startOffset + PER_STA_SUB_ELEMENT_MIN_LEN) {
976                 int subElementId = ie.bytes[startOffset] & Constants.BYTE_MASK;
977                 int subElementLen = ie.bytes[startOffset + 1] & Constants.BYTE_MASK;
978                 if (ie.bytes.length < startOffset + subElementLen) {
979                     if (DBG) {
980                         Log.w(TAG, "Invalid sub-element length: " + subElementLen);
981                     }
982                     // Skipping parsing of the IE
983                     return false;
984                 }
985                 if (subElementId != PER_STA_SUB_ELEMENT_ID) {
986                     // Skip this subelement, could be an unsupported one
987                     startOffset += subElementLen;
988                     continue;
989                 }
990 
991                 MloLink link = new MloLink();
992                 link.setLinkId(ie.bytes[startOffset + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET]
993                         & PER_STA_SUB_ELEMENT_LINK_ID_MASK);
994 
995                 int staInfoLength = ie.bytes[startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET]
996                         & Constants.BYTE_MASK;
997                 if (subElementLen < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) {
998                     if (DBG) {
999                         Log.w(TAG, "Invalid sta info length: " + staInfoLength);
1000                     }
1001                     // Skipping parsing of the IE
1002                     return false;
1003                 }
1004 
1005                 // Check if MAC Address is present
1006                 if ((ie.bytes[startOffset + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET]
1007                         & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK) != 0) {
1008                     if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) {
1009                         if (DBG) {
1010                             Log.w(TAG, "Invalid sta info length: " + staInfoLength);
1011                         }
1012                         // Skipping parsing of the IE
1013                         return false;
1014                     }
1015 
1016                     int macAddressOffset = startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET
1017                             + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET;
1018                     link.setApMacAddress(MacAddress.fromBytes(Arrays.copyOfRange(ie.bytes,
1019                             macAddressOffset, macAddressOffset + 6)));
1020                 }
1021 
1022                 mAffiliatedLinks.add(link);
1023 
1024                 // Done with this sub-element
1025                 startOffset += subElementLen;
1026             }
1027 
1028             return true;
1029         }
1030 
1031         /**
1032          * Parse Multi-Link Operation IE
1033          *
1034          * Multi-Link IE format as described in IEEE 802.11 specs, Section 9.4.2.312
1035          *
1036          *              | ElementID | Length | ExtendedID | Control | Common Info | Link Info |
1037          * Octets:            1          1         1          2        Variable     variable
1038          *
1039          *
1040          * Where Control field is described as,
1041          *
1042          *         | Type | Reserved | Presence Bitmap |
1043          * Bits:      3        1            12
1044          *
1045          * Where the Presence Bitmap subfield is described as,
1046          *
1047          *        | LinkId | BSS change count | MedSync | EML cap | MLD cap | Reserved |
1048          * Bits:      1            1               1         1         1         7
1049          *
1050          *
1051          *
1052          * Note: InformationElement.bytes has 'Element ID', 'Length', and 'Extended ID'
1053          *       stripped off already
1054          *
1055          */
from(InformationElement ie)1056         public void from(InformationElement ie) {
1057             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
1058                     || ie.idExt != InformationElement.EID_EXT_MULTI_LINK) {
1059                 throw new IllegalArgumentException("Element id is not Multi-Link: " + ie.id);
1060             }
1061 
1062             // Make sure the byte array length is at least the Control field size
1063             if (ie.bytes.length < CONTROL_FIELD_LEN) {
1064                 if (DBG) {
1065                     Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length);
1066                 }
1067                 // Skipping parsing of the IE
1068                 return;
1069             }
1070 
1071             // Check on IE type
1072             // Note only the BASIC type is allowed to be received from AP
1073             int type = ie.bytes[IE_TYPE_OFFSET] & IE_TYPE_MASK;
1074             if (type != TYPE_BASIC) {
1075                 if (DBG) {
1076                     Log.w(TAG, "Invalid/Unsupported Multi-Link IE type: " + type);
1077                 }
1078                 // Skipping parsing of the IE
1079                 return;
1080             }
1081 
1082             // Check length
1083             if (ie.bytes.length < BASIC_IE_MIN_LEN) {
1084                 if (DBG) {
1085                     Log.w(TAG, "Invalid Multi-Link IE len: " + ie.bytes.length);
1086                 }
1087                 // Skipping parsing of the IE
1088                 return;
1089             }
1090 
1091             int commonInfoLength = parseCommonInfoField(ie);
1092             if (commonInfoLength == 0) {
1093                 return;
1094             }
1095 
1096             if (!parseLinkInfoField(ie, CONTROL_FIELD_LEN + commonInfoLength)) {
1097                 return;
1098             }
1099 
1100             mPresent = true;
1101         }
1102     }
1103 
parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)1104     private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) {
1105         int maxNumberSpatialStreams = 1;
1106         for (int i = 8; i >= 1; --i) {
1107             int streamMap = mcsMapToStreamMap(mcsMap, i);
1108             // 3 means unsupported
1109             if (streamMap != 3) {
1110                 maxNumberSpatialStreams = i;
1111                 break;
1112             }
1113         }
1114         if (DBG) {
1115             for (int i = 8; i >= 1; --i) {
1116                 int streamMap = mcsMapToStreamMap(mcsMap, i);
1117                 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap);
1118             }
1119         }
1120         return maxNumberSpatialStreams;
1121     }
1122 
mcsMapToStreamMap(int mcsMap, int i)1123     private static int mcsMapToStreamMap(int mcsMap, int i) {
1124         return (mcsMap >> ((i - 1) * 2)) & 0x3;
1125     }
1126 
1127     public static class Interworking {
1128         public NetworkDetail.Ant ant = null;
1129         public boolean internet = false;
1130         public long hessid = 0L;
1131 
from(InformationElement ie)1132         public void from(InformationElement ie) {
1133             if (ie.id != InformationElement.EID_INTERWORKING) {
1134                 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
1135             }
1136             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1137             int anOptions = data.get() & Constants.BYTE_MASK;
1138             ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
1139             internet = (anOptions & 0x10) != 0;
1140             // There are only three possible lengths for the Interworking IE:
1141             // Len 1: Access Network Options only
1142             // Len 3: Access Network Options & Venue Info
1143             // Len 7: Access Network Options & HESSID
1144             // Len 9: Access Network Options, Venue Info, & HESSID
1145             if (ie.bytes.length != 1
1146                     && ie.bytes.length != 3
1147                     && ie.bytes.length != 7
1148                     && ie.bytes.length != 9) {
1149                 throw new IllegalArgumentException(
1150                         "Bad Interworking element length: " + ie.bytes.length);
1151             }
1152 
1153             if (ie.bytes.length == 3 || ie.bytes.length == 9) {
1154                 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2);
1155             }
1156 
1157             if (ie.bytes.length == 7 || ie.bytes.length == 9) {
1158                 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
1159             }
1160         }
1161     }
1162 
1163     public static class RoamingConsortium {
1164         public int anqpOICount = 0;
1165 
1166         private long[] roamingConsortiums = null;
1167 
getRoamingConsortiums()1168         public long[] getRoamingConsortiums() {
1169             return roamingConsortiums;
1170         }
1171 
from(InformationElement ie)1172         public void from(InformationElement ie) {
1173             if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
1174                 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
1175                         + ie.id);
1176             }
1177             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1178             anqpOICount = data.get() & Constants.BYTE_MASK;
1179 
1180             int oi12Length = data.get() & Constants.BYTE_MASK;
1181             int oi1Length = oi12Length & Constants.NIBBLE_MASK;
1182             int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
1183             int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
1184             int oiCount = 0;
1185             if (oi1Length > 0) {
1186                 oiCount++;
1187                 if (oi2Length > 0) {
1188                     oiCount++;
1189                     if (oi3Length > 0) {
1190                         oiCount++;
1191                     }
1192                 }
1193             }
1194             roamingConsortiums = new long[oiCount];
1195             if (oi1Length > 0 && roamingConsortiums.length > 0) {
1196                 roamingConsortiums[0] =
1197                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
1198             }
1199             if (oi2Length > 0 && roamingConsortiums.length > 1) {
1200                 roamingConsortiums[1] =
1201                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
1202             }
1203             if (oi3Length > 0 && roamingConsortiums.length > 2) {
1204                 roamingConsortiums[2] =
1205                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
1206             }
1207         }
1208     }
1209 
1210     public static class Vsa {
1211         private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04;
1212         private static final int ANQP_PPS_MO_ID_BIT = 0x02;
1213         private static final int OUI_WFA_ALLIANCE = 0x506F9a;
1214         private static final int OUI_TYPE_HS20 = 0x10;
1215         private static final int OUI_TYPE_MBO_OCE = 0x16;
1216 
1217         public NetworkDetail.HSRelease hsRelease = null;
1218         public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
1219 
1220         public boolean IsMboCapable = false;
1221         public boolean IsMboApCellularDataAware = false;
1222         public boolean IsOceCapable = false;
1223         public int mboAssociationDisallowedReasonCode =
1224                 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT;
1225         public byte[] oui;
1226 
parseVsaMboOce(InformationElement ie)1227         private void parseVsaMboOce(InformationElement ie) {
1228             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1229 
1230             // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying
1231             // MBO-OCE OUI type.
1232             data.getInt();
1233 
1234             while (data.remaining() > 1) {
1235                 int attrId = data.get() & Constants.BYTE_MASK;
1236                 int attrLen = data.get() & Constants.BYTE_MASK;
1237 
1238                 if ((attrLen == 0) || (attrLen > data.remaining())) {
1239                     return;
1240                 }
1241                 byte[] attrBytes = new byte[attrLen];
1242                 data.get(attrBytes);
1243                 switch (attrId) {
1244                     case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION:
1245                         IsMboCapable = true;
1246                         IsMboApCellularDataAware = (attrBytes[0]
1247                                 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0;
1248                         break;
1249                     case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED:
1250                         mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK;
1251                         break;
1252                     case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION:
1253                         IsOceCapable = true;
1254                         break;
1255                     default:
1256                         break;
1257                 }
1258             }
1259             if (DBG) {
1260                 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: "
1261                         + IsMboApCellularDataAware + " AssocDisAllowRC: "
1262                         + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable);
1263             }
1264         }
1265 
parseVsaHs20(InformationElement ie)1266         private void parseVsaHs20(InformationElement ie) {
1267             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1268             if (ie.bytes.length >= 5) {
1269                 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying
1270                 // HS20 OUI type.
1271                 data.getInt();
1272 
1273                 int hsConf = data.get() & Constants.BYTE_MASK;
1274                 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
1275                     case 0:
1276                         hsRelease = NetworkDetail.HSRelease.R1;
1277                         break;
1278                     case 1:
1279                         hsRelease = NetworkDetail.HSRelease.R2;
1280                         break;
1281                     case 2:
1282                         hsRelease = NetworkDetail.HSRelease.R3;
1283                         break;
1284                     default:
1285                         hsRelease = NetworkDetail.HSRelease.Unknown;
1286                         break;
1287                 }
1288                 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) {
1289                     // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication
1290                     // element, the size of the element is 5 bytes, and 2 bytes are optionally added
1291                     // for each optional field; ANQP PPS MO ID and ANQP Domain ID present.
1292                     int expectedSize = 7;
1293                     if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) {
1294                         expectedSize += 2;
1295                         if (ie.bytes.length < expectedSize) {
1296                             throw new IllegalArgumentException(
1297                                     "HS20 indication element too short: " + ie.bytes.length);
1298                         }
1299                         data.getShort(); // Skip 2 bytes
1300                     }
1301                     if (ie.bytes.length < expectedSize) {
1302                         throw new IllegalArgumentException(
1303                                 "HS20 indication element too short: " + ie.bytes.length);
1304                     }
1305                     anqpDomainID = data.getShort() & Constants.SHORT_MASK;
1306                 }
1307             }
1308         }
1309 
1310         /**
1311          * Parse the vendor specific information element to build
1312          * InformationElemmentUtil.vsa object.
1313          *
1314          * @param ie -- Information Element
1315          */
from(InformationElement ie)1316         public void from(InformationElement ie) {
1317             if (ie.bytes.length < 3) {
1318                 if (DBG) {
1319                     Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length);
1320                 }
1321                 return;
1322             }
1323 
1324             oui = Arrays.copyOfRange(ie.bytes, 0, 3);
1325             int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16)
1326                        | ((ie.bytes[1] & Constants.BYTE_MASK) << 8)
1327                        |  ((ie.bytes[2] & Constants.BYTE_MASK)));
1328 
1329             if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) {
1330                 int ouiType = ie.bytes[3];
1331                 switch (ouiType) {
1332                     case OUI_TYPE_HS20:
1333                         parseVsaHs20(ie);
1334                         break;
1335                     case OUI_TYPE_MBO_OCE:
1336                         parseVsaMboOce(ie);
1337                         break;
1338                     default:
1339                         break;
1340                 }
1341             }
1342         }
1343     }
1344 
1345     /**
1346      * This IE contained a bit field indicating the capabilities being advertised by the STA.
1347      * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
1348      *
1349      * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
1350      * bit.
1351      *
1352      * Here is the wire format of this IE:
1353      * | Element ID | Length | Capabilities |
1354      *       1           1          n
1355      */
1356     public static class ExtendedCapabilities {
1357         private static final int RTT_RESP_ENABLE_BIT = 70;
1358         private static final int SSID_UTF8_BIT = 48;
1359 
1360         public BitSet capabilitiesBitSet;
1361 
1362         /**
1363          * @return true if SSID should be interpreted using UTF-8 encoding
1364          */
isStrictUtf8()1365         public boolean isStrictUtf8() {
1366             return capabilitiesBitSet.get(SSID_UTF8_BIT);
1367         }
1368 
1369         /**
1370          * @return true if 802.11 MC RTT Response is enabled
1371          */
is80211McRTTResponder()1372         public boolean is80211McRTTResponder() {
1373             return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
1374         }
1375 
ExtendedCapabilities()1376         public ExtendedCapabilities() {
1377             capabilitiesBitSet = new BitSet();
1378         }
1379 
ExtendedCapabilities(ExtendedCapabilities other)1380         public ExtendedCapabilities(ExtendedCapabilities other) {
1381             capabilitiesBitSet = other.capabilitiesBitSet;
1382         }
1383 
1384         /**
1385          * Parse an ExtendedCapabilities from the IE containing raw bytes.
1386          *
1387          * @param ie The Information element data
1388          */
from(InformationElement ie)1389         public void from(InformationElement ie) {
1390             capabilitiesBitSet = BitSet.valueOf(ie.bytes);
1391         }
1392     }
1393 
1394     /**
1395      * parse beacon to build the capabilities
1396      *
1397      * This class is used to build the capabilities string of the scan results coming
1398      * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
1399      * and builds the ScanResult.capabilities String in a way that mirrors the values returned
1400      * by wpa_supplicant.
1401      */
1402     public static class Capabilities {
1403         private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
1404         private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000;
1405         private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
1406         private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50;
1407         private static final short RSNE_VERSION = 0x0001;
1408 
1409         private static final int WPA_AKM_EAP = 0x01f25000;
1410         private static final int WPA_AKM_PSK = 0x02f25000;
1411 
1412         private static final int RSN_AKM_EAP = 0x01ac0f00;
1413         private static final int RSN_AKM_PSK = 0x02ac0f00;
1414         private static final int RSN_AKM_FT_EAP = 0x03ac0f00;
1415         private static final int RSN_AKM_FT_PSK = 0x04ac0f00;
1416         private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00;
1417         private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00;
1418         private static final int RSN_AKM_SAE = 0x08ac0f00;
1419         private static final int RSN_AKM_FT_SAE = 0x09ac0f00;
1420         private static final int RSN_AKM_OWE = 0x12ac0f00;
1421         private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00;
1422         private static final int RSN_OSEN = 0x019a6f50;
1423         private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00;
1424         private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00;
1425         private static final int RSN_AKM_DPP = 0x029a6f50;
1426 
1427         private static final int WPA_CIPHER_NONE = 0x00f25000;
1428         private static final int WPA_CIPHER_TKIP = 0x02f25000;
1429         private static final int WPA_CIPHER_CCMP = 0x04f25000;
1430 
1431         private static final int RSN_CIPHER_NONE = 0x00ac0f00;
1432         private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
1433         private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
1434         private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
1435         private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00;
1436         private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00;
1437         private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00;
1438         private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00;
1439         private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00;
1440 
1441         // RSN capability bit definition
1442         private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6;
1443         private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7;
1444 
1445         public List<Integer> protocol;
1446         public List<List<Integer>> keyManagement;
1447         public List<List<Integer>> pairwiseCipher;
1448         public List<Integer> groupCipher;
1449         public List<Integer> groupManagementCipher;
1450         public boolean isESS;
1451         public boolean isIBSS;
1452         public boolean isPrivacy;
1453         public boolean isWPS;
1454         public boolean isManagementFrameProtectionRequired;
1455         public boolean isManagementFrameProtectionCapable;
1456 
Capabilities()1457         public Capabilities() {
1458         }
1459 
1460         // RSNE format (size unit: byte)
1461         //
1462         // | Element ID | Length | Version | Group Data Cipher Suite |
1463         //      1           1         2                 4
1464         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
1465         //              2                            4 * m
1466         // | AKM Suite Count | AKM Suite List | RSN Capabilities |
1467         //          2               4 * n               2
1468         // | PMKID Count | PMKID List | Group Management Cipher Suite |
1469         //        2          16 * s                 4
1470         //
1471         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1472         //       stripped off already
parseRsnElement(InformationElement ie)1473         private void parseRsnElement(InformationElement ie) {
1474             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1475 
1476             try {
1477                 // version
1478                 if (buf.getShort() != RSNE_VERSION) {
1479                     // incorrect version
1480                     return;
1481                 }
1482 
1483                 // found the RSNE IE, hence start building the capability string
1484                 protocol.add(ScanResult.PROTOCOL_RSN);
1485 
1486                 // group data cipher suite
1487                 groupCipher.add(parseRsnCipher(buf.getInt()));
1488 
1489                 // pairwise cipher suite count
1490                 short cipherCount = buf.getShort();
1491                 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
1492                 // pairwise cipher suite list
1493                 for (int i = 0; i < cipherCount; i++) {
1494                     rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
1495                 }
1496                 pairwiseCipher.add(rsnPairwiseCipher);
1497 
1498                 // AKM
1499                 // AKM suite count
1500                 short akmCount = buf.getShort();
1501                 ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
1502 
1503                 for (int i = 0; i < akmCount; i++) {
1504                     int akm = buf.getInt();
1505                     switch (akm) {
1506                         case RSN_AKM_EAP:
1507                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1508                             break;
1509                         case RSN_AKM_PSK:
1510                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
1511                             break;
1512                         case RSN_AKM_FT_EAP:
1513                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
1514                             break;
1515                         case RSN_AKM_FT_PSK:
1516                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
1517                             break;
1518                         case RSN_AKM_EAP_SHA256:
1519                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
1520                             break;
1521                         case RSN_AKM_PSK_SHA256:
1522                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
1523                             break;
1524                         case RSN_AKM_SAE:
1525                             rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE);
1526                             break;
1527                         case RSN_AKM_FT_SAE:
1528                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE);
1529                             break;
1530                         case RSN_AKM_OWE:
1531                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE);
1532                             break;
1533                         case RSN_AKM_EAP_SUITE_B_192:
1534                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192);
1535                             break;
1536                         case RSN_OSEN:
1537                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN);
1538                             break;
1539                         case RSN_AKM_EAP_FILS_SHA256:
1540                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256);
1541                             break;
1542                         case RSN_AKM_EAP_FILS_SHA384:
1543                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384);
1544                             break;
1545                         case RSN_AKM_DPP:
1546                             rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP);
1547                             break;
1548                         default:
1549                             rsnKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN);
1550                             break;
1551                     }
1552                 }
1553                 // Default AKM
1554                 if (rsnKeyManagement.isEmpty()) {
1555                     rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1556                 }
1557                 keyManagement.add(rsnKeyManagement);
1558 
1559                 // RSN capabilities (optional),
1560                 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016
1561                 if (buf.remaining() < 2) return;
1562                 int rsnCaps = buf.getShort();
1563 
1564                 if (buf.remaining() < 2) return;
1565                 // PMKID, it's not used, drop it if exists (optional).
1566                 int rsnPmkIdCount = buf.getShort();
1567                 for (int i = 0; i < rsnPmkIdCount; i++) {
1568                     // Each PMKID element length in the PMKID List is 16 bytes
1569                     byte[] tmp = new byte[16];
1570                     buf.get(tmp);
1571                 }
1572 
1573                 // Group management cipher suite (optional).
1574                 if (buf.remaining() < 4) return;
1575                 groupManagementCipher.add(parseRsnCipher(buf.getInt()));
1576                 isManagementFrameProtectionRequired = !groupManagementCipher.isEmpty()
1577                         && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps);
1578                 isManagementFrameProtectionCapable = !groupManagementCipher.isEmpty()
1579                         && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps);
1580             } catch (BufferUnderflowException e) {
1581                 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
1582             }
1583         }
1584 
parseWpaCipher(int cipher)1585         private static @Cipher int parseWpaCipher(int cipher) {
1586             switch (cipher) {
1587                 case WPA_CIPHER_NONE:
1588                     return ScanResult.CIPHER_NONE;
1589                 case WPA_CIPHER_TKIP:
1590                     return ScanResult.CIPHER_TKIP;
1591                 case WPA_CIPHER_CCMP:
1592                     return ScanResult.CIPHER_CCMP;
1593                 default:
1594                     Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
1595                             + Integer.toHexString(cipher));
1596                     return ScanResult.CIPHER_NONE;
1597             }
1598         }
1599 
parseRsnCipher(int cipher)1600         private static @Cipher int parseRsnCipher(int cipher) {
1601             switch (cipher) {
1602                 case RSN_CIPHER_NONE:
1603                     return ScanResult.CIPHER_NONE;
1604                 case RSN_CIPHER_TKIP:
1605                     return ScanResult.CIPHER_TKIP;
1606                 case RSN_CIPHER_CCMP:
1607                     return ScanResult.CIPHER_CCMP;
1608                 case RSN_CIPHER_GCMP_256:
1609                     return ScanResult.CIPHER_GCMP_256;
1610                 case RSN_CIPHER_NO_GROUP_ADDRESSED:
1611                     return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
1612                 case RSN_CIPHER_GCMP_128:
1613                     return ScanResult.CIPHER_GCMP_128;
1614                 case RSN_CIPHER_BIP_GMAC_128:
1615                     return ScanResult.CIPHER_BIP_GMAC_128;
1616                 case RSN_CIPHER_BIP_GMAC_256:
1617                     return ScanResult.CIPHER_BIP_GMAC_256;
1618                 case RSN_CIPHER_BIP_CMAC_256:
1619                     return ScanResult.CIPHER_BIP_CMAC_256;
1620                 default:
1621                     Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
1622                             + Integer.toHexString(cipher));
1623                     return ScanResult.CIPHER_NONE;
1624             }
1625         }
1626 
isWpsElement(InformationElement ie)1627         private static boolean isWpsElement(InformationElement ie) {
1628             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1629             try {
1630                 // WPS OUI and type
1631                 return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
1632             } catch (BufferUnderflowException e) {
1633                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1634                 return false;
1635             }
1636         }
1637 
isWpaOneElement(InformationElement ie)1638         private static boolean isWpaOneElement(InformationElement ie) {
1639             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1640 
1641             try {
1642                 // WPA OUI and type
1643                 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
1644             } catch (BufferUnderflowException e) {
1645                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1646                 return false;
1647             }
1648         }
1649 
1650         // WPA type 1 format (size unit: byte)
1651         //
1652         // | Element ID | Length | OUI | Type | Version |
1653         //      1           1       3     1        2
1654         // | Group Data Cipher Suite |
1655         //             4
1656         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
1657         //              2                            4 * m
1658         // | AKM Suite Count | AKM Suite List |
1659         //          2               4 * n
1660         //
1661         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1662         //       stripped off already
1663         //
parseWpaOneElement(InformationElement ie)1664         private void parseWpaOneElement(InformationElement ie) {
1665             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1666 
1667             try {
1668                 // skip WPA OUI and type parsing. isWpaOneElement() should have
1669                 // been called for verification before we reach here.
1670                 buf.getInt();
1671 
1672                 // version
1673                 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) {
1674                     // incorrect version
1675                     return;
1676                 }
1677 
1678                 // start building the string
1679                 protocol.add(ScanResult.PROTOCOL_WPA);
1680 
1681                 // group data cipher suite
1682                 groupCipher.add(parseWpaCipher(buf.getInt()));
1683 
1684                 // pairwise cipher suite count
1685                 short cipherCount = buf.getShort();
1686                 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
1687                 // pairwise chipher suite list
1688                 for (int i = 0; i < cipherCount; i++) {
1689                     wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
1690                 }
1691                 pairwiseCipher.add(wpaPairwiseCipher);
1692 
1693                 // AKM
1694                 // AKM suite count
1695                 short akmCount = buf.getShort();
1696                 ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
1697 
1698                 // AKM suite list
1699                 for (int i = 0; i < akmCount; i++) {
1700                     int akm = buf.getInt();
1701                     switch (akm) {
1702                         case WPA_AKM_EAP:
1703                             wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1704                             break;
1705                         case WPA_AKM_PSK:
1706                             wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
1707                             break;
1708                         default:
1709                             wpaKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN);
1710                             break;
1711                     }
1712                 }
1713                 // Default AKM
1714                 if (wpaKeyManagement.isEmpty()) {
1715                     wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1716                 }
1717                 keyManagement.add(wpaKeyManagement);
1718             } catch (BufferUnderflowException e) {
1719                 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
1720             }
1721         }
1722 
1723         /**
1724          * Parse the Information Element and the 16-bit Capability Information field
1725          * to build the InformationElemmentUtil.capabilities object.
1726          *
1727          * @param ies            -- Information Element array
1728          * @param beaconCap      -- 16-bit Beacon Capability Information field
1729          * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device
1730          * @param freq           -- Frequency on which frame/beacon was transmitted.
1731          *                          Some parsing may be affected such as DMG parameters in
1732          *                          DMG (60GHz) beacon.
1733          */
1734 
from(InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq)1735         public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported,
1736                 int freq) {
1737             protocol = new ArrayList<>();
1738             keyManagement = new ArrayList<>();
1739             groupCipher = new ArrayList<>();
1740             pairwiseCipher = new ArrayList<>();
1741             groupManagementCipher = new ArrayList<>();
1742 
1743             if (ies == null) {
1744                 return;
1745             }
1746             isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0;
1747             if (ScanResult.is60GHz(freq)) {
1748                 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */
1749                 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS)
1750                         == NativeScanResult.BSS_CAPABILITY_DMG_ESS) {
1751                     isESS = true;
1752                 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) {
1753                     isIBSS = true;
1754                 }
1755             } else {
1756                 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0;
1757                 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0;
1758             }
1759             for (InformationElement ie : ies) {
1760                 WifiNl80211Manager.OemSecurityType oemSecurityType =
1761                         WifiNl80211Manager.parseOemSecurityTypeElement(
1762                         ie.id, ie.idExt, ie.bytes);
1763                 if (oemSecurityType != null
1764                         && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) {
1765                     protocol.add(oemSecurityType.protocol);
1766                     keyManagement.add(oemSecurityType.keyManagement);
1767                     pairwiseCipher.add(oemSecurityType.pairwiseCipher);
1768                     groupCipher.add(oemSecurityType.groupCipher);
1769                 }
1770 
1771                 if (ie.id == InformationElement.EID_RSN) {
1772                     parseRsnElement(ie);
1773                 }
1774 
1775                 if (ie.id == InformationElement.EID_VSA) {
1776                     if (isWpaOneElement(ie)) {
1777                         parseWpaOneElement(ie);
1778                     }
1779                     if (isWpsElement(ie)) {
1780                         // TODO(b/62134557): parse WPS IE to provide finer granularity information.
1781                         isWPS = true;
1782                     }
1783                     if (isOweSupported && isOweElement(ie)) {
1784                         /* From RFC 8110: Once the client and AP have finished 802.11 association,
1785                            they then complete the Diffie-Hellman key exchange and create a Pairwise
1786                            Master Key (PMK) and its associated identifier, PMKID [IEEE802.11].
1787                            Upon completion of 802.11 association, the AP initiates the 4-way
1788                            handshake to the client using the PMK generated above.  The 4-way
1789                            handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation
1790                            Key (KCK), and a Message Integrity Code (MIC) to use for protection
1791                            of the frames that define the 4-way handshake.
1792 
1793                            We check if OWE is supported here because we are adding the OWE
1794                            capabilities to the Open BSS. Non-supporting devices need to see this
1795                            open network and ignore this element. Supporting devices need to hide
1796                            the Open BSS of OWE in transition mode and connect to the Hidden one.
1797                         */
1798                         protocol.add(ScanResult.PROTOCOL_RSN);
1799                         groupCipher.add(ScanResult.CIPHER_CCMP);
1800                         ArrayList<Integer> owePairwiseCipher = new ArrayList<>();
1801                         owePairwiseCipher.add(ScanResult.CIPHER_CCMP);
1802                         pairwiseCipher.add(owePairwiseCipher);
1803                         ArrayList<Integer> oweKeyManagement = new ArrayList<>();
1804                         oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION);
1805                         keyManagement.add(oweKeyManagement);
1806                     }
1807                 }
1808             }
1809         }
1810 
isOweElement(InformationElement ie)1811         private static boolean isOweElement(InformationElement ie) {
1812             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1813             try {
1814                 // OWE OUI and type
1815                 return (buf.getInt() == OWE_VENDOR_OUI_TYPE);
1816             } catch (BufferUnderflowException e) {
1817                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1818                 return false;
1819             }
1820         }
1821 
protocolToString(@rotocol int protocol)1822         private String protocolToString(@Protocol int protocol) {
1823             switch (protocol) {
1824                 case ScanResult.PROTOCOL_NONE:
1825                     return "None";
1826                 case ScanResult.PROTOCOL_WPA:
1827                     return "WPA";
1828                 case ScanResult.PROTOCOL_RSN:
1829                     return "RSN";
1830                 case ScanResult.PROTOCOL_OSEN:
1831                     return "OSEN";
1832                 case ScanResult.PROTOCOL_WAPI:
1833                     return "WAPI";
1834                 default:
1835                     return "?";
1836             }
1837         }
1838 
keyManagementToString(@eyMgmt int akm)1839         private String keyManagementToString(@KeyMgmt int akm) {
1840             switch (akm) {
1841                 case ScanResult.KEY_MGMT_NONE:
1842                     return "None";
1843                 case ScanResult.KEY_MGMT_PSK:
1844                     return "PSK";
1845                 case ScanResult.KEY_MGMT_EAP:
1846                     return "EAP/SHA1";
1847                 case ScanResult.KEY_MGMT_FT_EAP:
1848                     return "FT/EAP";
1849                 case ScanResult.KEY_MGMT_FT_PSK:
1850                     return "FT/PSK";
1851                 case ScanResult.KEY_MGMT_EAP_SHA256:
1852                     return "EAP/SHA256";
1853                 case ScanResult.KEY_MGMT_PSK_SHA256:
1854                     return "PSK-SHA256";
1855                 case ScanResult.KEY_MGMT_OWE:
1856                     return "OWE";
1857                 case ScanResult.KEY_MGMT_OWE_TRANSITION:
1858                     return "OWE_TRANSITION";
1859                 case ScanResult.KEY_MGMT_SAE:
1860                     return "SAE";
1861                 case ScanResult.KEY_MGMT_FT_SAE:
1862                     return "FT/SAE";
1863                 case ScanResult.KEY_MGMT_EAP_SUITE_B_192:
1864                     return "EAP_SUITE_B_192";
1865                 case ScanResult.KEY_MGMT_OSEN:
1866                     return "OSEN";
1867                 case ScanResult.KEY_MGMT_WAPI_PSK:
1868                     return "WAPI-PSK";
1869                 case ScanResult.KEY_MGMT_WAPI_CERT:
1870                     return "WAPI-CERT";
1871                 case ScanResult.KEY_MGMT_FILS_SHA256:
1872                     return "EAP-FILS-SHA256";
1873                 case ScanResult.KEY_MGMT_FILS_SHA384:
1874                     return "EAP-FILS-SHA384";
1875                 case ScanResult.KEY_MGMT_DPP:
1876                     return "DPP";
1877                 default:
1878                     return "?";
1879             }
1880         }
1881 
cipherToString(@ipher int cipher)1882         private String cipherToString(@Cipher int cipher) {
1883             switch (cipher) {
1884                 case ScanResult.CIPHER_NONE:
1885                     return "None";
1886                 case ScanResult.CIPHER_CCMP:
1887                     return "CCMP";
1888                 case ScanResult.CIPHER_GCMP_256:
1889                     return "GCMP-256";
1890                 case ScanResult.CIPHER_TKIP:
1891                     return "TKIP";
1892                 case ScanResult.CIPHER_SMS4:
1893                     return "SMS4";
1894                 default:
1895                     return "?";
1896             }
1897         }
1898 
1899         /**
1900          * Build the ScanResult.capabilities String.
1901          *
1902          * @return security string that mirrors what wpa_supplicant generates
1903          */
generateCapabilitiesString()1904         public String generateCapabilitiesString() {
1905             StringBuilder capabilities = new StringBuilder();
1906             // private Beacon without an RSNE or WPA IE, hence WEP0
1907             boolean isWEP = (protocol.isEmpty()) && isPrivacy;
1908 
1909             if (isWEP) {
1910                 capabilities.append("[WEP]");
1911             }
1912             for (int i = 0; i < protocol.size(); i++) {
1913                 String capability = generateCapabilitiesStringPerProtocol(i);
1914                 // add duplicate capabilities for WPA2 for backward compatibility:
1915                 // duplicate "RSN" entries as "WPA2"
1916                 String capWpa2 = generateWPA2CapabilitiesString(capability, i);
1917                 capabilities.append(capWpa2);
1918                 capabilities.append(capability);
1919             }
1920             if (isESS) {
1921                 capabilities.append("[ESS]");
1922             }
1923             if (isIBSS) {
1924                 capabilities.append("[IBSS]");
1925             }
1926             if (isWPS) {
1927                 capabilities.append("[WPS]");
1928             }
1929             if (!groupManagementCipher.isEmpty()) {
1930                 if (isManagementFrameProtectionRequired) {
1931                     capabilities.append("[MFPR]");
1932                 }
1933                 if (isManagementFrameProtectionCapable) {
1934                     capabilities.append("[MFPC]");
1935                 }
1936             }
1937 
1938             return capabilities.toString();
1939         }
1940 
1941         /**
1942          * Build the Capability String for one protocol
1943          * @param index: index number of the protocol
1944          * @return security string for one protocol
1945          */
generateCapabilitiesStringPerProtocol(int index)1946         private String generateCapabilitiesStringPerProtocol(int index) {
1947             StringBuilder capability = new StringBuilder();
1948             capability.append("[").append(protocolToString(protocol.get(index)));
1949 
1950             if (index < keyManagement.size()) {
1951                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1952                     capability.append((j == 0) ? "-" : "+").append(
1953                             keyManagementToString(keyManagement.get(index).get(j)));
1954                 }
1955             }
1956             if (index < pairwiseCipher.size()) {
1957                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1958                     capability.append((j == 0) ? "-" : "+").append(
1959                             cipherToString(pairwiseCipher.get(index).get(j)));
1960                 }
1961             }
1962             capability.append("]");
1963             return capability.toString();
1964         }
1965 
1966         /**
1967          * Build the duplicate Capability String for WPA2
1968          * @param cap: original capability String
1969          * @param index: index number of the protocol
1970          * @return security string for WPA2, empty String if protocol is not WPA2
1971          */
generateWPA2CapabilitiesString(String cap, int index)1972         private String generateWPA2CapabilitiesString(String cap, int index) {
1973             StringBuilder capWpa2 = new StringBuilder();
1974             // if not WPA2, return empty String
1975             if (cap.contains("EAP_SUITE_B_192")
1976                     || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP")
1977                     && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) {
1978                 return "";
1979             }
1980             capWpa2.append("[").append("WPA2");
1981             if (index < keyManagement.size()) {
1982                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1983                     capWpa2.append((j == 0) ? "-" : "+").append(
1984                             keyManagementToString(keyManagement.get(index).get(j)));
1985                     // WPA3/WPA2 transition mode
1986                     if (cap.contains("SAE")) {
1987                         break;
1988                     }
1989                 }
1990             }
1991             if (index < pairwiseCipher.size()) {
1992                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1993                     capWpa2.append((j == 0) ? "-" : "+").append(
1994                             cipherToString(pairwiseCipher.get(index).get(j)));
1995                 }
1996             }
1997             capWpa2.append("]");
1998             return capWpa2.toString();
1999         }
2000     }
2001 
2002 
2003     /**
2004      * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
2005      * only be present in scan results that are derived from a Beacon Frame, not from the more
2006      * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
2007      */
2008     public static class TrafficIndicationMap {
2009         private static final int MAX_TIM_LENGTH = 254;
2010         private boolean mValid = false;
2011         public int mLength = 0;
2012         public int mDtimCount = -1;
2013         //Negative DTIM Period means no TIM element was given this frame.
2014         public int mDtimPeriod = -1;
2015         public int mBitmapControl = 0;
2016 
2017         /**
2018          * Is this a valid TIM information element.
2019          */
isValid()2020         public boolean isValid() {
2021             return mValid;
2022         }
2023 
2024         // Traffic Indication Map format (size unit: byte)
2025         //
2026         //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
2027         //      1          1          1            1               1                1 - 251
2028         //
2029         // Note: InformationElement.bytes has 'Element ID' and 'Length'
2030         //       stripped off already
2031         //
from(InformationElement ie)2032         public void from(InformationElement ie) {
2033             mValid = false;
2034             if (ie == null || ie.bytes == null) return;
2035             mLength = ie.bytes.length;
2036             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2037             try {
2038                 mDtimCount = data.get() & Constants.BYTE_MASK;
2039                 mDtimPeriod = data.get() & Constants.BYTE_MASK;
2040                 mBitmapControl = data.get() & Constants.BYTE_MASK;
2041                 //A valid TIM element must have atleast one more byte
2042                 data.get();
2043             } catch (BufferUnderflowException e) {
2044                 return;
2045             }
2046             if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
2047                 mValid = true;
2048             }
2049         }
2050     }
2051 
2052     /**
2053      * This util class determines the 802.11 standard (a/b/g/n/ac/ax/be) being used
2054      */
2055     public static class WifiMode {
2056         public static final int MODE_UNDEFINED = 0; // Unknown/undefined
2057         public static final int MODE_11A = 1;       // 802.11a
2058         public static final int MODE_11B = 2;       // 802.11b
2059         public static final int MODE_11G = 3;       // 802.11g
2060         public static final int MODE_11N = 4;       // 802.11n
2061         public static final int MODE_11AC = 5;      // 802.11ac
2062         public static final int MODE_11AX = 6;      // 802.11ax
2063         public static final int MODE_11BE = 7;      // 802.11be
2064         //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
2065 
2066         /**
2067          * Use frequency, max supported rate, and the existence of EHT, HE, VHT, HT & ERP fields in
2068          * scan result to determine the 802.11 Wifi standard being used.
2069          */
determineMode(int frequency, int maxRate, boolean foundEht, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)2070         public static int determineMode(int frequency, int maxRate, boolean foundEht,
2071                 boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp) {
2072             if (foundEht) {
2073                 return MODE_11BE;
2074             } else if (foundHe) {
2075                 return MODE_11AX;
2076             } else if (!ScanResult.is24GHz(frequency) && foundVht) {
2077                 // Do not include subset of VHT on 2.4 GHz vendor extension
2078                 // in consideration for reporting VHT.
2079                 return MODE_11AC;
2080             } else if (foundHt) {
2081                 return MODE_11N;
2082             } else if (foundErp) {
2083                 return MODE_11G;
2084             } else if (ScanResult.is24GHz(frequency)) {
2085                 if (maxRate < 24000000) {
2086                     return MODE_11B;
2087                 } else {
2088                     return MODE_11G;
2089                 }
2090             } else {
2091                 return MODE_11A;
2092             }
2093         }
2094 
2095         /**
2096          * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC/AX/BE>
2097          */
toString(int mode)2098         public static String toString(int mode) {
2099             switch(mode) {
2100                 case MODE_11A:
2101                     return "MODE_11A";
2102                 case MODE_11B:
2103                     return "MODE_11B";
2104                 case MODE_11G:
2105                     return "MODE_11G";
2106                 case MODE_11N:
2107                     return "MODE_11N";
2108                 case MODE_11AC:
2109                     return "MODE_11AC";
2110                 case MODE_11AX:
2111                     return "MODE_11AX";
2112                 case MODE_11BE:
2113                     return "MODE_11BE";
2114                 default:
2115                     return "MODE_UNDEFINED";
2116             }
2117         }
2118     }
2119 
2120     /**
2121      * Parser for both the Supported Rates & Extended Supported Rates Information Elements
2122      */
2123     public static class SupportedRates {
2124         public static final int MASK = 0x7F; // 0111 1111
2125         public boolean mValid = false;
2126         public ArrayList<Integer> mRates;
2127 
SupportedRates()2128         public SupportedRates() {
2129             mRates = new ArrayList<Integer>();
2130         }
2131 
2132         /**
2133          * Is this a valid Supported Rates information element.
2134          */
isValid()2135         public boolean isValid() {
2136             return mValid;
2137         }
2138 
2139         /**
2140          * get the Rate in bits/s from associated byteval
2141          */
getRateFromByte(int byteVal)2142         public static int getRateFromByte(int byteVal) {
2143             byteVal &= MASK;
2144             switch(byteVal) {
2145                 case 2:
2146                     return 1000000;
2147                 case 4:
2148                     return 2000000;
2149                 case 11:
2150                     return 5500000;
2151                 case 12:
2152                     return 6000000;
2153                 case 18:
2154                     return 9000000;
2155                 case 22:
2156                     return 11000000;
2157                 case 24:
2158                     return 12000000;
2159                 case 36:
2160                     return 18000000;
2161                 case 44:
2162                     return 22000000;
2163                 case 48:
2164                     return 24000000;
2165                 case 66:
2166                     return 33000000;
2167                 case 72:
2168                     return 36000000;
2169                 case 96:
2170                     return 48000000;
2171                 case 108:
2172                     return 54000000;
2173                 default:
2174                     //ERROR UNKNOWN RATE
2175                     return -1;
2176             }
2177         }
2178 
2179         // Supported Rates format (size unit: byte)
2180         //
2181         //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
2182         //      1          1          1 - 8
2183         //
2184         // Note: InformationElement.bytes has 'Element ID' and 'Length'
2185         //       stripped off already
2186         //
from(InformationElement ie)2187         public void from(InformationElement ie) {
2188             mValid = false;
2189             if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
2190                 return;
2191             }
2192             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2193             try {
2194                 for (int i = 0; i < ie.bytes.length; i++) {
2195                     int rate = getRateFromByte(data.get());
2196                     if (rate > 0) {
2197                         mRates.add(rate);
2198                     } else {
2199                         return;
2200                     }
2201                 }
2202             } catch (BufferUnderflowException e) {
2203                 return;
2204             }
2205             mValid = true;
2206             return;
2207         }
2208 
2209         /**
2210          * Lists the rates in a human readable string
2211          */
toString()2212         public String toString() {
2213             StringBuilder sbuf = new StringBuilder();
2214             for (Integer rate : mRates) {
2215                 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
2216             }
2217             return sbuf.toString();
2218         }
2219     }
2220 
2221     /**
2222      * This util class determines country related information in beacon/probe response
2223      */
2224     public static class Country {
2225         private boolean mValid = false;
2226         public String mCountryCode = "00";
2227 
2228         /**
2229          * Parse the Information Element Country Information field. Note that element ID and length
2230          * fields are already removed.
2231          *
2232          * Country IE format (size unit: byte)
2233          *
2234          * ElementID | Length | country string | triplet | padding
2235          *      1          1          3            Q*x       0 or 1
2236          * First two bytes of country string are country code
2237          * See 802.11 spec dot11CountryString definition.
2238          */
from(InformationElement ie)2239         public void from(InformationElement ie) {
2240             mValid = false;
2241             if (ie == null || ie.bytes == null || ie.bytes.length < 3) return;
2242             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
2243             try {
2244                 char letter1 = (char) (data.get() & Constants.BYTE_MASK);
2245                 char letter2 = (char) (data.get() & Constants.BYTE_MASK);
2246                 char letter3 = (char) (data.get() & Constants.BYTE_MASK);
2247                 // See 802.11 spec dot11CountryString definition.
2248                 // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively.
2249                 mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I')
2250                         && Character.isLetterOrDigit((int) letter1)
2251                         && Character.isLetterOrDigit((int) letter2);
2252                 if (mValid) {
2253                     mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US);
2254                 }
2255             } catch (BufferUnderflowException e) {
2256                 return;
2257             }
2258         }
2259 
2260         /**
2261          * Is this a valid country information element.
2262          */
isValid()2263         public boolean isValid() {
2264             return mValid;
2265         }
2266 
2267         /**
2268          * @return country code indicated in beacon/probe response frames
2269          */
getCountryCode()2270         public String getCountryCode() {
2271             return mCountryCode;
2272         }
2273     }
2274 }
2275