• 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.wifi.ScanResult;
19 import android.net.wifi.ScanResult.InformationElement;
20 import android.net.wifi.WifiAnnotations.Cipher;
21 import android.net.wifi.WifiAnnotations.KeyMgmt;
22 import android.net.wifi.WifiAnnotations.Protocol;
23 import android.net.wifi.WifiScanner;
24 import android.net.wifi.nl80211.NativeScanResult;
25 import android.net.wifi.nl80211.WifiNl80211Manager;
26 import android.util.Log;
27 
28 import com.android.server.wifi.ByteBufferReader;
29 import com.android.server.wifi.MboOceConstants;
30 import com.android.server.wifi.hotspot2.NetworkDetail;
31 import com.android.server.wifi.hotspot2.anqp.Constants;
32 
33 import java.nio.BufferUnderflowException;
34 import java.nio.ByteBuffer;
35 import java.nio.ByteOrder;
36 import java.util.ArrayList;
37 import java.util.BitSet;
38 import java.util.List;
39 
40 public class InformationElementUtil {
41     private static final String TAG = "InformationElementUtil";
42     private static final boolean DBG = false;
parseInformationElements(byte[] bytes)43     public static InformationElement[] parseInformationElements(byte[] bytes) {
44         if (bytes == null) {
45             return new InformationElement[0];
46         }
47         ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
48 
49         ArrayList<InformationElement> infoElements = new ArrayList<>();
50         boolean found_ssid = false;
51         while (data.remaining() > 1) {
52             int eid = data.get() & Constants.BYTE_MASK;
53             int eidExt = 0;
54             int elementLength = data.get() & Constants.BYTE_MASK;
55 
56             if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
57                     && found_ssid)) {
58                 // APs often pad the data with bytes that happen to match that of the EID_SSID
59                 // marker.  This is not due to a known issue for APs to incorrectly send the SSID
60                 // name multiple times.
61                 break;
62             }
63             if (eid == InformationElement.EID_SSID) {
64                 found_ssid = true;
65             } else if (eid == InformationElement.EID_EXTENSION_PRESENT) {
66                 if (elementLength == 0) {
67                     // Malformed IE, skipping
68                     break;
69                 }
70                 eidExt = data.get() & Constants.BYTE_MASK;
71                 elementLength--;
72             }
73 
74             InformationElement ie = new InformationElement();
75             ie.id = eid;
76             ie.idExt = eidExt;
77             ie.bytes = new byte[elementLength];
78             data.get(ie.bytes);
79             infoElements.add(ie);
80         }
81         return infoElements.toArray(new InformationElement[infoElements.size()]);
82     }
83 
84     /**
85      * Parse and retrieve the Roaming Consortium Information Element from the list of IEs.
86      *
87      * @param ies List of IEs to retrieve from
88      * @return {@link RoamingConsortium}
89      */
getRoamingConsortiumIE(InformationElement[] ies)90     public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) {
91         RoamingConsortium roamingConsortium = new RoamingConsortium();
92         if (ies != null) {
93             for (InformationElement ie : ies) {
94                 if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) {
95                     try {
96                         roamingConsortium.from(ie);
97                     } catch (RuntimeException e) {
98                         Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage());
99                     }
100                 }
101             }
102         }
103         return roamingConsortium;
104     }
105 
106     /**
107      * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs.
108      *
109      * @param ies List of IEs to retrieve from
110      * @return {@link Vsa}
111      */
getHS2VendorSpecificIE(InformationElement[] ies)112     public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) {
113         Vsa vsa = new Vsa();
114         if (ies != null) {
115             for (InformationElement ie : ies) {
116                 if (ie.id == InformationElement.EID_VSA) {
117                     try {
118                         vsa.from(ie);
119                     } catch (RuntimeException e) {
120                         Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage());
121                     }
122                 }
123             }
124         }
125         return vsa;
126     }
127 
128     /**
129      * Parse and retrieve the Interworking information element from the list of IEs.
130      *
131      * @param ies List of IEs to retrieve from
132      * @return {@link Interworking}
133      */
getInterworkingIE(InformationElement[] ies)134     public static Interworking getInterworkingIE(InformationElement[] ies) {
135         Interworking interworking = new Interworking();
136         if (ies != null) {
137             for (InformationElement ie : ies) {
138                 if (ie.id == InformationElement.EID_INTERWORKING) {
139                     try {
140                         interworking.from(ie);
141                     } catch (RuntimeException e) {
142                         Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage());
143                     }
144                 }
145             }
146         }
147         return interworking;
148     }
149 
150     public static class BssLoad {
151         public static final int INVALID = -1;
152         public static final int MAX_CHANNEL_UTILIZATION = 255;
153         public static final int MIN_CHANNEL_UTILIZATION = 0;
154         public static final int CHANNEL_UTILIZATION_SCALE = 256;
155         public int stationCount = INVALID;
156         public int channelUtilization = INVALID;
157         public int capacity = INVALID;
158 
from(InformationElement ie)159         public void from(InformationElement ie) {
160             if (ie.id != InformationElement.EID_BSS_LOAD) {
161                 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id);
162             }
163             if (ie.bytes.length != 5) {
164                 throw new IllegalArgumentException("BSS Load element length is not 5: "
165                                                    + ie.bytes.length);
166             }
167             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
168             stationCount = data.getShort() & Constants.SHORT_MASK;
169             channelUtilization = data.get() & Constants.BYTE_MASK;
170             capacity = data.getShort() & Constants.SHORT_MASK;
171         }
172     }
173 
174     public static class HtOperation {
175         private static final int HT_OPERATION_IE_LEN = 22;
176         private boolean mPresent = false;
177         private int mSecondChannelOffset = 0;
178 
179         /**
180          * returns if HT Operation IE present in the message.
181          */
isPresent()182         public boolean isPresent() {
183             return mPresent;
184         }
185 
186         /**
187          * Returns channel width if it is 20 or 40MHz
188          * Results will be invalid if channel width greater than 40MHz
189          * So caller should only call this method if VHT Operation IE is not present,
190          * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED.
191          */
getChannelWidth()192         public int getChannelWidth() {
193             if (mSecondChannelOffset != 0) {
194                 return ScanResult.CHANNEL_WIDTH_40MHZ;
195             } else {
196                 return ScanResult.CHANNEL_WIDTH_20MHZ;
197             }
198         }
199 
200         /**
201          * Returns channel Center frequency (for 20/40 MHz channels only)
202          * Results will be invalid for larger channel width,
203          * So, caller should only call this method if VHT Operation IE is not present,
204          * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED.
205          */
getCenterFreq0(int primaryFrequency)206         public int getCenterFreq0(int primaryFrequency) {
207             if (mSecondChannelOffset != 0) {
208                 //40 MHz
209                 if (mSecondChannelOffset == 1) {
210                     return primaryFrequency + 10;
211                 } else if (mSecondChannelOffset == 3) {
212                     return primaryFrequency - 10;
213                 } else {
214                     Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset);
215                     return 0;
216                 }
217             } else {
218                 //20 MHz
219                 return primaryFrequency;
220             }
221         }
222 
223         /**
224          * Parse the HT Operation IE to read the fields of interest.
225          */
from(InformationElement ie)226         public void from(InformationElement ie) {
227             if (ie.id != InformationElement.EID_HT_OPERATION) {
228                 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id);
229             }
230             if (ie.bytes.length < HT_OPERATION_IE_LEN) {
231                 throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length);
232             }
233             mPresent = true;
234             mSecondChannelOffset = ie.bytes[1] & 0x3;
235         }
236     }
237 
238     public static class VhtOperation {
239         private static final int VHT_OPERATION_IE_LEN = 5;
240         private boolean mPresent = false;
241         private int mChannelMode = 0;
242         private int mCenterFreqIndex1 = 0;
243         private int mCenterFreqIndex2 = 0;
244 
245         /**
246          * returns if VHT Operation IE present in the message.
247          */
isPresent()248         public boolean isPresent() {
249             return mPresent;
250         }
251 
252         /**
253          * Returns channel width if it is above 40MHz,
254          * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that
255          * channel width should be obtained from the HT Operation IE via
256          * HtOperation.getChannelWidth().
257          */
getChannelWidth()258         public int getChannelWidth() {
259             if (mChannelMode == 0) {
260                 // 20 or 40MHz
261                 return ScanResult.UNSPECIFIED;
262             } else if (mCenterFreqIndex2 == 0) {
263                 // No secondary channel
264                 return ScanResult.CHANNEL_WIDTH_80MHZ;
265             } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) {
266                 // Primary and secondary channels adjacent
267                 return ScanResult.CHANNEL_WIDTH_160MHZ;
268             } else {
269                 // Primary and secondary channels not adjacent
270                 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
271             }
272         }
273 
274         /**
275          * Returns center frequency of primary channel (if channel width greater than 40MHz),
276          * otherwise, it returns zero to indicate that center frequency should be obtained from
277          * the HT Operation IE via HtOperation.getCenterFreq0().
278          */
getCenterFreq0()279         public int getCenterFreq0() {
280             if (mCenterFreqIndex1 == 0 || mChannelMode == 0) {
281                 return 0;
282             } else {
283                 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex1,
284                         WifiScanner.WIFI_BAND_5_GHZ);
285             }
286         }
287 
288          /**
289          * Returns center frequency of secondary channel if exists (channel width greater than
290          * 40MHz), otherwise, it returns zero.
291          * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz
292          * channels.
293          */
getCenterFreq1()294         public int getCenterFreq1() {
295             if (mCenterFreqIndex2 == 0 || mChannelMode == 0) {
296                 return 0;
297             } else {
298                 return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqIndex2,
299                         WifiScanner.WIFI_BAND_5_GHZ);
300             }
301         }
302 
303         /**
304          * Parse the VHT Operation IE to read the fields of interest.
305          */
from(InformationElement ie)306         public void from(InformationElement ie) {
307             if (ie.id != InformationElement.EID_VHT_OPERATION) {
308                 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id);
309             }
310             if (ie.bytes.length < VHT_OPERATION_IE_LEN) {
311                 throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length);
312             }
313             mPresent = true;
314             mChannelMode = ie.bytes[0] & Constants.BYTE_MASK;
315             mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK;
316             mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK;
317         }
318     }
319 
320     /**
321      * HeOperation: represents the HE Operation IE
322      */
323     public static class HeOperation {
324 
325         private static final int HE_OPERATION_BASIC_LENGTH = 6;
326         private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40;
327         private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02;
328         private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03;
329         private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80;
330         private static final int VHT_OPERATION_INFO_START_INDEX = 6;
331         private static final int HE_BW_80_80_160 = 3;
332 
333         private boolean mPresent = false;
334         private boolean mVhtInfoPresent = false;
335         private boolean m6GhzInfoPresent = false;
336         private int mChannelWidth;
337         private int mPrimaryChannel;
338         private int mCenterFreqSeg0;
339         private int mCenterFreqSeg1;
340         private InformationElement mVhtInfo = null;
341 
342         /**
343          * Returns whether the HE Information Element is present.
344          */
isPresent()345         public boolean isPresent() {
346             return mPresent;
347         }
348 
349         /**
350          * Returns whether VHT Information field is present.
351          */
isVhtInfoPresent()352         public boolean isVhtInfoPresent() {
353             return mVhtInfoPresent;
354         }
355 
356         /**
357          * Returns the VHT Information Element if it exists
358          * otherwise, return null.
359          */
getVhtInfoElement()360         public InformationElement getVhtInfoElement() {
361             return mVhtInfo;
362         }
363 
364         /**
365          * Returns whether the 6GHz information field is present.
366          */
is6GhzInfoPresent()367         public boolean is6GhzInfoPresent() {
368             return m6GhzInfoPresent;
369         }
370 
371         /**
372          * Returns the Channel BW
373          * Only applicable to 6GHz band
374          */
getChannelWidth()375         public int getChannelWidth() {
376             if (!m6GhzInfoPresent) {
377                 return ScanResult.UNSPECIFIED;
378             } else if (mChannelWidth == 0) {
379                 return ScanResult.CHANNEL_WIDTH_20MHZ;
380             } else if (mChannelWidth == 1) {
381                 return ScanResult.CHANNEL_WIDTH_40MHZ;
382             } else if (mChannelWidth == 2) {
383                 return ScanResult.CHANNEL_WIDTH_80MHZ;
384             } else if (Math.abs(mCenterFreqSeg1 - mCenterFreqSeg0) == 8) {
385                 return ScanResult.CHANNEL_WIDTH_160MHZ;
386             } else {
387                 return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
388             }
389         }
390 
391         /**
392          * Returns the primary channel frequency
393          * Only applicable for 6GHz channels
394          */
getPrimaryFreq()395         public int getPrimaryFreq() {
396             return ScanResult.convertChannelToFrequencyMhzIfSupported(mPrimaryChannel,
397                         WifiScanner.WIFI_BAND_6_GHZ);
398         }
399 
400         /**
401          * Returns the center frequency for the primary channel
402          * Only applicable to 6GHz channels
403          */
getCenterFreq0()404         public int getCenterFreq0() {
405             if (m6GhzInfoPresent) {
406                 if (mCenterFreqSeg0 == 0) {
407                     return 0;
408                 } else {
409                     return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg0,
410                             WifiScanner.WIFI_BAND_6_GHZ);
411                 }
412             } else {
413                 return 0;
414             }
415         }
416 
417         /**
418          * Returns the center frequency for the secondary channel
419          * Only applicable to 6GHz channels
420          */
getCenterFreq1()421         public int getCenterFreq1() {
422             if (m6GhzInfoPresent) {
423                 if (mCenterFreqSeg1 == 0) {
424                     return 0;
425                 } else {
426                     return ScanResult.convertChannelToFrequencyMhzIfSupported(mCenterFreqSeg1,
427                             WifiScanner.WIFI_BAND_6_GHZ);
428                 }
429             } else {
430                 return 0;
431             }
432         }
433 
434         /** Parse HE Operation IE */
from(InformationElement ie)435         public void from(InformationElement ie) {
436             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
437                     || ie.idExt != InformationElement.EID_EXT_HE_OPERATION) {
438                 throw new IllegalArgumentException("Element id is not HE_OPERATION");
439             }
440 
441             // Make sure the byte array length is at least the fixed size
442             if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) {
443                 if (DBG) {
444                     Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
445                 }
446                 // Skipping parsing of the IE
447                 return;
448             }
449 
450             mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0;
451             m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0;
452             boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0;
453             int expectedLen = HE_OPERATION_BASIC_LENGTH + (mVhtInfoPresent ? 3 : 0)
454                     + (coHostedBssPresent ? 1 : 0) + (m6GhzInfoPresent ? 5 : 0);
455 
456             // Make sure the byte array length is at least fitting the known parameters
457             if (ie.bytes.length < expectedLen) {
458                 if (DBG) {
459                     Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
460                 }
461                 // Skipping parsing of the IE
462                 return;
463             }
464 
465             // Passed all checks, IE is ready for decoding
466             mPresent = true;
467 
468             if (mVhtInfoPresent) {
469                 mVhtInfo = new InformationElement();
470                 mVhtInfo.id = InformationElement.EID_VHT_OPERATION;
471                 mVhtInfo.bytes = new byte[5];
472                 System.arraycopy(ie.bytes, VHT_OPERATION_INFO_START_INDEX, mVhtInfo.bytes, 0, 3);
473             }
474 
475             if (m6GhzInfoPresent) {
476                 int startIndx = VHT_OPERATION_INFO_START_INDEX + (mVhtInfoPresent ? 3 : 0)
477                         + (coHostedBssPresent ? 1 : 0);
478 
479                 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK;
480                 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK;
481                 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK;
482                 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK;
483             }
484         }
485     }
486 
487     /**
488      * HtCapabilities: represents the HT Capabilities IE
489      */
490     public static class HtCapabilities {
491         private int mMaxNumberSpatialStreams  = 1;
492         private boolean mPresent = false;
493         /** Returns whether HT Capabilities IE is present */
isPresent()494         public boolean isPresent() {
495             return mPresent;
496         }
497         /**
498          * Returns max number of spatial streams if HT Capabilities IE is found and parsed,
499          * or 1 otherwise
500          */
getMaxNumberSpatialStreams()501         public int getMaxNumberSpatialStreams() {
502             return mMaxNumberSpatialStreams;
503         }
504 
505         /** Parse HT Capabilities IE */
from(InformationElement ie)506         public void from(InformationElement ie) {
507             if (ie.id != InformationElement.EID_HT_CAPABILITIES) {
508                 throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id);
509             }
510             if (ie.bytes.length < 26) {
511                 if (DBG) {
512                     Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length);
513                 }
514                 return;
515             }
516             int stream1 = ie.bytes[3] & Constants.BYTE_MASK;
517             int stream2 = ie.bytes[4] & Constants.BYTE_MASK;
518             int stream3 = ie.bytes[5] & Constants.BYTE_MASK;
519             int stream4 = ie.bytes[6] & Constants.BYTE_MASK;
520             if (DBG) {
521                 Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4));
522                 Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3));
523                 Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2));
524                 Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1));
525             }
526             mMaxNumberSpatialStreams = (stream4 > 0) ? 4 :
527                     ((stream3 > 0) ? 3 :
528                     ((stream2 > 0) ? 2 : 1));
529             mPresent = true;
530         }
531     }
532 
533     /**
534      * VhtCapabilities: represents the VHT Capabilities IE
535      */
536     public static class VhtCapabilities {
537         private int mMaxNumberSpatialStreams = 1;
538         private boolean mPresent = false;
539         /** Returns whether VHT Capabilities IE is present */
isPresent()540         public boolean isPresent() {
541             return mPresent;
542         }
543         /**
544          * Returns max number of spatial streams if VHT Capabilities IE is found and parsed,
545          * or 1 otherwise
546          */
getMaxNumberSpatialStreams()547         public int getMaxNumberSpatialStreams() {
548             return mMaxNumberSpatialStreams;
549         }
550         /** Parse VHT Capabilities IE */
from(InformationElement ie)551         public void from(InformationElement ie) {
552             if (ie.id != InformationElement.EID_VHT_CAPABILITIES) {
553                 throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id);
554             }
555             if (ie.bytes.length < 12) {
556                 if (DBG) {
557                     Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length);
558                 }
559                 return;
560             }
561             int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8)
562                     + (ie.bytes[4] & Constants.BYTE_MASK);
563             mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
564             mPresent = true;
565         }
566     }
567 
568     /**
569      * HeCapabilities: represents the HE Capabilities IE
570      */
571     public static class HeCapabilities {
572         private int mMaxNumberSpatialStreams = 1;
573         private boolean mPresent = false;
574         /** Returns whether HE Capabilities IE is present */
isPresent()575         public boolean isPresent() {
576             return mPresent;
577         }
578         /**
579          * Returns max number of spatial streams if HE Capabilities IE is found and parsed,
580          * or 1 otherwise
581          */
getMaxNumberSpatialStreams()582         public int getMaxNumberSpatialStreams() {
583             return mMaxNumberSpatialStreams;
584         }
585         /** Parse HE Capabilities IE */
from(InformationElement ie)586         public void from(InformationElement ie) {
587             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
588                     || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) {
589                 throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id);
590             }
591             if (ie.bytes.length < 21) {
592                 if (DBG) {
593                     Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length);
594                 }
595                 return;
596             }
597             int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8)
598                     + (ie.bytes[17] & Constants.BYTE_MASK);
599             mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
600             mPresent = true;
601         }
602     }
603 
parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap)604     private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) {
605         int maxNumberSpatialStreams = 1;
606         for (int i = 8; i >= 1; --i) {
607             int streamMap = mcsMapToStreamMap(mcsMap, i);
608             // 3 means unsupported
609             if (streamMap != 3) {
610                 maxNumberSpatialStreams = i;
611                 break;
612             }
613         }
614         if (DBG) {
615             for (int i = 8; i >= 1; --i) {
616                 int streamMap = mcsMapToStreamMap(mcsMap, i);
617                 Log.d(TAG, "Rx MCS set " + i + " : " + streamMap);
618             }
619         }
620         return maxNumberSpatialStreams;
621     }
622 
mcsMapToStreamMap(int mcsMap, int i)623     private static int mcsMapToStreamMap(int mcsMap, int i) {
624         return (mcsMap >> ((i - 1) * 2)) & 0x3;
625     }
626 
627     public static class Interworking {
628         public NetworkDetail.Ant ant = null;
629         public boolean internet = false;
630         public long hessid = 0L;
631 
from(InformationElement ie)632         public void from(InformationElement ie) {
633             if (ie.id != InformationElement.EID_INTERWORKING) {
634                 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id);
635             }
636             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
637             int anOptions = data.get() & Constants.BYTE_MASK;
638             ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
639             internet = (anOptions & 0x10) != 0;
640             // There are only three possible lengths for the Interworking IE:
641             // Len 1: Access Network Options only
642             // Len 3: Access Network Options & Venue Info
643             // Len 7: Access Network Options & HESSID
644             // Len 9: Access Network Options, Venue Info, & HESSID
645             if (ie.bytes.length != 1
646                     && ie.bytes.length != 3
647                     && ie.bytes.length != 7
648                     && ie.bytes.length != 9) {
649                 throw new IllegalArgumentException(
650                         "Bad Interworking element length: " + ie.bytes.length);
651             }
652 
653             if (ie.bytes.length == 3 || ie.bytes.length == 9) {
654                 int venueInfo = (int) ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 2);
655             }
656 
657             if (ie.bytes.length == 7 || ie.bytes.length == 9) {
658                 hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
659             }
660         }
661     }
662 
663     public static class RoamingConsortium {
664         public int anqpOICount = 0;
665 
666         private long[] roamingConsortiums = null;
667 
getRoamingConsortiums()668         public long[] getRoamingConsortiums() {
669             return roamingConsortiums;
670         }
671 
from(InformationElement ie)672         public void from(InformationElement ie) {
673             if (ie.id != InformationElement.EID_ROAMING_CONSORTIUM) {
674                 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : "
675                         + ie.id);
676             }
677             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
678             anqpOICount = data.get() & Constants.BYTE_MASK;
679 
680             int oi12Length = data.get() & Constants.BYTE_MASK;
681             int oi1Length = oi12Length & Constants.NIBBLE_MASK;
682             int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK;
683             int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length;
684             int oiCount = 0;
685             if (oi1Length > 0) {
686                 oiCount++;
687                 if (oi2Length > 0) {
688                     oiCount++;
689                     if (oi3Length > 0) {
690                         oiCount++;
691                     }
692                 }
693             }
694             roamingConsortiums = new long[oiCount];
695             if (oi1Length > 0 && roamingConsortiums.length > 0) {
696                 roamingConsortiums[0] =
697                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
698             }
699             if (oi2Length > 0 && roamingConsortiums.length > 1) {
700                 roamingConsortiums[1] =
701                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
702             }
703             if (oi3Length > 0 && roamingConsortiums.length > 2) {
704                 roamingConsortiums[2] =
705                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
706             }
707         }
708     }
709 
710     public static class Vsa {
711         private static final int ANQP_DOMAIN_ID_PRESENT_BIT = 0x04;
712         private static final int ANQP_PPS_MO_ID_BIT = 0x02;
713         private static final int OUI_WFA_ALLIANCE = 0x506F9a;
714         private static final int OUI_TYPE_HS20 = 0x10;
715         private static final int OUI_TYPE_MBO_OCE = 0x16;
716 
717         public NetworkDetail.HSRelease hsRelease = null;
718         public int anqpDomainID = 0;    // No domain ID treated the same as a 0; unique info per AP.
719 
720         public boolean IsMboCapable = false;
721         public boolean IsMboApCellularDataAware = false;
722         public boolean IsOceCapable = false;
723         public int mboAssociationDisallowedReasonCode =
724                 MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT;
725 
parseVsaMboOce(InformationElement ie)726         private void parseVsaMboOce(InformationElement ie) {
727             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
728 
729             // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying
730             // MBO-OCE OUI type.
731             data.getInt();
732 
733             while (data.remaining() > 1) {
734                 int attrId = data.get() & Constants.BYTE_MASK;
735                 int attrLen = data.get() & Constants.BYTE_MASK;
736 
737                 if ((attrLen == 0) || (attrLen > data.remaining())) {
738                     return;
739                 }
740                 byte[] attrBytes = new byte[attrLen];
741                 data.get(attrBytes);
742                 switch (attrId) {
743                     case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION:
744                         IsMboCapable = true;
745                         IsMboApCellularDataAware = (attrBytes[0]
746                                 & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0;
747                         break;
748                     case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED:
749                         mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK;
750                         break;
751                     case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION:
752                         IsOceCapable = true;
753                         break;
754                     default:
755                         break;
756                 }
757             }
758             if (DBG) {
759                 Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: "
760                         + IsMboApCellularDataAware + " AssocDisAllowRC: "
761                         + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable);
762             }
763         }
764 
parseVsaHs20(InformationElement ie)765         private void parseVsaHs20(InformationElement ie) {
766             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
767             if (ie.bytes.length >= 5) {
768                 // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying
769                 // HS20 OUI type.
770                 data.getInt();
771 
772                 int hsConf = data.get() & Constants.BYTE_MASK;
773                 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
774                     case 0:
775                         hsRelease = NetworkDetail.HSRelease.R1;
776                         break;
777                     case 1:
778                         hsRelease = NetworkDetail.HSRelease.R2;
779                         break;
780                     case 2:
781                         hsRelease = NetworkDetail.HSRelease.R3;
782                         break;
783                     default:
784                         hsRelease = NetworkDetail.HSRelease.Unknown;
785                         break;
786                 }
787                 if ((hsConf & ANQP_DOMAIN_ID_PRESENT_BIT) != 0) {
788                     // According to Hotspot 2.0 Specification v3.0 section 3.1.1 HS2.0 Indication
789                     // element, the size of the element is 5 bytes, and 2 bytes are optionally added
790                     // for each optional field; ANQP PPS MO ID and ANQP Domain ID present.
791                     int expectedSize = 7;
792                     if ((hsConf & ANQP_PPS_MO_ID_BIT) != 0) {
793                         expectedSize += 2;
794                         if (ie.bytes.length < expectedSize) {
795                             throw new IllegalArgumentException(
796                                     "HS20 indication element too short: " + ie.bytes.length);
797                         }
798                         data.getShort(); // Skip 2 bytes
799                     }
800                     if (ie.bytes.length < expectedSize) {
801                         throw new IllegalArgumentException(
802                                 "HS20 indication element too short: " + ie.bytes.length);
803                     }
804                     anqpDomainID = data.getShort() & Constants.SHORT_MASK;
805                 }
806             }
807         }
808 
809         /**
810          * Parse the vendor specific information element to build
811          * InformationElemmentUtil.vsa object.
812          *
813          * @param ie -- Information Element
814          */
from(InformationElement ie)815         public void from(InformationElement ie) {
816             if (ie.bytes.length < 3) {
817                 if (DBG) {
818                     Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length);
819                 }
820                 return;
821             }
822 
823             int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16)
824                        | ((ie.bytes[1] & Constants.BYTE_MASK) << 8)
825                        |  ((ie.bytes[2] & Constants.BYTE_MASK)));
826 
827             if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) {
828                 int ouiType = ie.bytes[3];
829                 switch (ouiType) {
830                     case OUI_TYPE_HS20:
831                         parseVsaHs20(ie);
832                         break;
833                     case OUI_TYPE_MBO_OCE:
834                         parseVsaMboOce(ie);
835                         break;
836                     default:
837                         break;
838                 }
839             }
840         }
841     }
842 
843     /**
844      * This IE contained a bit field indicating the capabilities being advertised by the STA.
845      * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
846      *
847      * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
848      * bit.
849      *
850      * Here is the wire format of this IE:
851      * | Element ID | Length | Capabilities |
852      *       1           1          n
853      */
854     public static class ExtendedCapabilities {
855         private static final int RTT_RESP_ENABLE_BIT = 70;
856         private static final int SSID_UTF8_BIT = 48;
857 
858         public BitSet capabilitiesBitSet;
859 
860         /**
861          * @return true if SSID should be interpreted using UTF-8 encoding
862          */
isStrictUtf8()863         public boolean isStrictUtf8() {
864             return capabilitiesBitSet.get(SSID_UTF8_BIT);
865         }
866 
867         /**
868          * @return true if 802.11 MC RTT Response is enabled
869          */
is80211McRTTResponder()870         public boolean is80211McRTTResponder() {
871             return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
872         }
873 
ExtendedCapabilities()874         public ExtendedCapabilities() {
875             capabilitiesBitSet = new BitSet();
876         }
877 
ExtendedCapabilities(ExtendedCapabilities other)878         public ExtendedCapabilities(ExtendedCapabilities other) {
879             capabilitiesBitSet = other.capabilitiesBitSet;
880         }
881 
882         /**
883          * Parse an ExtendedCapabilities from the IE containing raw bytes.
884          *
885          * @param ie The Information element data
886          */
from(InformationElement ie)887         public void from(InformationElement ie) {
888             capabilitiesBitSet = BitSet.valueOf(ie.bytes);
889         }
890     }
891 
892     /**
893      * parse beacon to build the capabilities
894      *
895      * This class is used to build the capabilities string of the scan results coming
896      * from HAL. It parses the ieee beacon's capability field, WPA and RSNE IE as per spec,
897      * and builds the ScanResult.capabilities String in a way that mirrors the values returned
898      * by wpa_supplicant.
899      */
900     public static class Capabilities {
901         private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
902         private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000;
903         private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
904         private static final int OWE_VENDOR_OUI_TYPE = 0x1c9a6f50;
905         private static final short RSNE_VERSION = 0x0001;
906 
907         private static final int WPA_AKM_EAP = 0x01f25000;
908         private static final int WPA_AKM_PSK = 0x02f25000;
909 
910         private static final int RSN_AKM_EAP = 0x01ac0f00;
911         private static final int RSN_AKM_PSK = 0x02ac0f00;
912         private static final int RSN_AKM_FT_EAP = 0x03ac0f00;
913         private static final int RSN_AKM_FT_PSK = 0x04ac0f00;
914         private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00;
915         private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00;
916         private static final int RSN_AKM_SAE = 0x08ac0f00;
917         private static final int RSN_AKM_FT_SAE = 0x09ac0f00;
918         private static final int RSN_AKM_OWE = 0x12ac0f00;
919         private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00;
920         private static final int RSN_OSEN = 0x019a6f50;
921         private static final int RSN_AKM_EAP_FILS_SHA256 = 0x0eac0f00;
922         private static final int RSN_AKM_EAP_FILS_SHA384 = 0x0fac0f00;
923 
924         private static final int WPA_CIPHER_NONE = 0x00f25000;
925         private static final int WPA_CIPHER_TKIP = 0x02f25000;
926         private static final int WPA_CIPHER_CCMP = 0x04f25000;
927 
928         private static final int RSN_CIPHER_NONE = 0x00ac0f00;
929         private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
930         private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
931         private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
932         private static final int RSN_CIPHER_GCMP_256 = 0x09ac0f00;
933         private static final int RSN_CIPHER_GCMP_128 = 0x08ac0f00;
934         private static final int RSN_CIPHER_BIP_GMAC_128 = 0x0bac0f00;
935         private static final int RSN_CIPHER_BIP_GMAC_256 = 0x0cac0f00;
936         private static final int RSN_CIPHER_BIP_CMAC_256 = 0x0dac0f00;
937 
938         // RSN capability bit definition
939         private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED = 1 << 6;
940         private static final int RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE = 1 << 7;
941 
942         public List<Integer> protocol;
943         public List<List<Integer>> keyManagement;
944         public List<List<Integer>> pairwiseCipher;
945         public List<Integer> groupCipher;
946         public List<Integer> groupManagementCipher;
947         public boolean isESS;
948         public boolean isIBSS;
949         public boolean isPrivacy;
950         public boolean isWPS;
951         public boolean isManagementFrameProtectionRequired;
952         public boolean isManagementFrameProtectionCapable;
953 
Capabilities()954         public Capabilities() {
955         }
956 
957         // RSNE format (size unit: byte)
958         //
959         // | Element ID | Length | Version | Group Data Cipher Suite |
960         //      1           1         2                 4
961         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
962         //              2                            4 * m
963         // | AKM Suite Count | AKM Suite List | RSN Capabilities |
964         //          2               4 * n               2
965         // | PMKID Count | PMKID List | Group Management Cipher Suite |
966         //        2          16 * s                 4
967         //
968         // Note: InformationElement.bytes has 'Element ID' and 'Length'
969         //       stripped off already
parseRsnElement(InformationElement ie)970         private void parseRsnElement(InformationElement ie) {
971             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
972 
973             try {
974                 // version
975                 if (buf.getShort() != RSNE_VERSION) {
976                     // incorrect version
977                     return;
978                 }
979 
980                 // found the RSNE IE, hence start building the capability string
981                 protocol.add(ScanResult.PROTOCOL_RSN);
982 
983                 // group data cipher suite
984                 groupCipher.add(parseRsnCipher(buf.getInt()));
985 
986                 // pairwise cipher suite count
987                 short cipherCount = buf.getShort();
988                 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
989                 // pairwise cipher suite list
990                 for (int i = 0; i < cipherCount; i++) {
991                     rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
992                 }
993                 pairwiseCipher.add(rsnPairwiseCipher);
994 
995                 // AKM
996                 // AKM suite count
997                 short akmCount = buf.getShort();
998                 ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
999 
1000                 for (int i = 0; i < akmCount; i++) {
1001                     int akm = buf.getInt();
1002                     switch (akm) {
1003                         case RSN_AKM_EAP:
1004                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1005                             break;
1006                         case RSN_AKM_PSK:
1007                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
1008                             break;
1009                         case RSN_AKM_FT_EAP:
1010                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
1011                             break;
1012                         case RSN_AKM_FT_PSK:
1013                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
1014                             break;
1015                         case RSN_AKM_EAP_SHA256:
1016                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
1017                             break;
1018                         case RSN_AKM_PSK_SHA256:
1019                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
1020                             break;
1021                         case RSN_AKM_SAE:
1022                             rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE);
1023                             break;
1024                         case RSN_AKM_FT_SAE:
1025                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE);
1026                             break;
1027                         case RSN_AKM_OWE:
1028                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE);
1029                             break;
1030                         case RSN_AKM_EAP_SUITE_B_192:
1031                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192);
1032                             break;
1033                         case RSN_OSEN:
1034                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN);
1035                             break;
1036                         case RSN_AKM_EAP_FILS_SHA256:
1037                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256);
1038                             break;
1039                         case RSN_AKM_EAP_FILS_SHA384:
1040                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384);
1041                             break;
1042                         default:
1043                             rsnKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN);
1044                             break;
1045                     }
1046                 }
1047                 // Default AKM
1048                 if (rsnKeyManagement.isEmpty()) {
1049                     rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1050                 }
1051                 keyManagement.add(rsnKeyManagement);
1052 
1053                 // RSN capabilities (optional),
1054                 // see section 9.4.2.25 - RSNE - In IEEE Std 802.11-2016
1055                 if (buf.remaining() < 2) return;
1056                 int rsnCaps = buf.getShort();
1057 
1058                 if (buf.remaining() < 2) return;
1059                 // PMKID, it's not used, drop it if exists (optional).
1060                 int rsnPmkIdCount = buf.getShort();
1061                 for (int i = 0; i < rsnPmkIdCount; i++) {
1062                     // Each PMKID element length in the PMKID List is 16 bytes
1063                     byte[] tmp = new byte[16];
1064                     buf.get(tmp);
1065                 }
1066 
1067                 // Group management cipher suite (optional).
1068                 if (buf.remaining() < 4) return;
1069                 groupManagementCipher.add(parseRsnCipher(buf.getInt()));
1070                 isManagementFrameProtectionRequired = !groupManagementCipher.isEmpty()
1071                         && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_REQUIRED & rsnCaps);
1072                 isManagementFrameProtectionCapable = !groupManagementCipher.isEmpty()
1073                         && 0 != (RSN_CAP_MANAGEMENT_FRAME_PROTECTION_CAPABLE & rsnCaps);
1074             } catch (BufferUnderflowException e) {
1075                 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
1076             }
1077         }
1078 
parseWpaCipher(int cipher)1079         private static @Cipher int parseWpaCipher(int cipher) {
1080             switch (cipher) {
1081                 case WPA_CIPHER_NONE:
1082                     return ScanResult.CIPHER_NONE;
1083                 case WPA_CIPHER_TKIP:
1084                     return ScanResult.CIPHER_TKIP;
1085                 case WPA_CIPHER_CCMP:
1086                     return ScanResult.CIPHER_CCMP;
1087                 default:
1088                     Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
1089                             + Integer.toHexString(cipher));
1090                     return ScanResult.CIPHER_NONE;
1091             }
1092         }
1093 
parseRsnCipher(int cipher)1094         private static @Cipher int parseRsnCipher(int cipher) {
1095             switch (cipher) {
1096                 case RSN_CIPHER_NONE:
1097                     return ScanResult.CIPHER_NONE;
1098                 case RSN_CIPHER_TKIP:
1099                     return ScanResult.CIPHER_TKIP;
1100                 case RSN_CIPHER_CCMP:
1101                     return ScanResult.CIPHER_CCMP;
1102                 case RSN_CIPHER_GCMP_256:
1103                     return ScanResult.CIPHER_GCMP_256;
1104                 case RSN_CIPHER_NO_GROUP_ADDRESSED:
1105                     return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
1106                 case RSN_CIPHER_GCMP_128:
1107                     return ScanResult.CIPHER_GCMP_128;
1108                 case RSN_CIPHER_BIP_GMAC_128:
1109                     return ScanResult.CIPHER_BIP_GMAC_128;
1110                 case RSN_CIPHER_BIP_GMAC_256:
1111                     return ScanResult.CIPHER_BIP_GMAC_256;
1112                 case RSN_CIPHER_BIP_CMAC_256:
1113                     return ScanResult.CIPHER_BIP_CMAC_256;
1114                 default:
1115                     Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
1116                             + Integer.toHexString(cipher));
1117                     return ScanResult.CIPHER_NONE;
1118             }
1119         }
1120 
isWpsElement(InformationElement ie)1121         private static boolean isWpsElement(InformationElement ie) {
1122             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1123             try {
1124                 // WPS OUI and type
1125                 return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
1126             } catch (BufferUnderflowException e) {
1127                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1128                 return false;
1129             }
1130         }
1131 
isWpaOneElement(InformationElement ie)1132         private static boolean isWpaOneElement(InformationElement ie) {
1133             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1134 
1135             try {
1136                 // WPA OUI and type
1137                 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
1138             } catch (BufferUnderflowException e) {
1139                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1140                 return false;
1141             }
1142         }
1143 
1144         // WPA type 1 format (size unit: byte)
1145         //
1146         // | Element ID | Length | OUI | Type | Version |
1147         //      1           1       3     1        2
1148         // | Group Data Cipher Suite |
1149         //             4
1150         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
1151         //              2                            4 * m
1152         // | AKM Suite Count | AKM Suite List |
1153         //          2               4 * n
1154         //
1155         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1156         //       stripped off already
1157         //
parseWpaOneElement(InformationElement ie)1158         private void parseWpaOneElement(InformationElement ie) {
1159             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1160 
1161             try {
1162                 // skip WPA OUI and type parsing. isWpaOneElement() should have
1163                 // been called for verification before we reach here.
1164                 buf.getInt();
1165 
1166                 // version
1167                 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) {
1168                     // incorrect version
1169                     return;
1170                 }
1171 
1172                 // start building the string
1173                 protocol.add(ScanResult.PROTOCOL_WPA);
1174 
1175                 // group data cipher suite
1176                 groupCipher.add(parseWpaCipher(buf.getInt()));
1177 
1178                 // pairwise cipher suite count
1179                 short cipherCount = buf.getShort();
1180                 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
1181                 // pairwise chipher suite list
1182                 for (int i = 0; i < cipherCount; i++) {
1183                     wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
1184                 }
1185                 pairwiseCipher.add(wpaPairwiseCipher);
1186 
1187                 // AKM
1188                 // AKM suite count
1189                 short akmCount = buf.getShort();
1190                 ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
1191 
1192                 // AKM suite list
1193                 for (int i = 0; i < akmCount; i++) {
1194                     int akm = buf.getInt();
1195                     switch (akm) {
1196                         case WPA_AKM_EAP:
1197                             wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1198                             break;
1199                         case WPA_AKM_PSK:
1200                             wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
1201                             break;
1202                         default:
1203                             wpaKeyManagement.add(ScanResult.KEY_MGMT_UNKNOWN);
1204                             break;
1205                     }
1206                 }
1207                 // Default AKM
1208                 if (wpaKeyManagement.isEmpty()) {
1209                     wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1210                 }
1211                 keyManagement.add(wpaKeyManagement);
1212             } catch (BufferUnderflowException e) {
1213                 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
1214             }
1215         }
1216 
1217         /**
1218          * Parse the Information Element and the 16-bit Capability Information field
1219          * to build the InformationElemmentUtil.capabilities object.
1220          *
1221          * @param ies            -- Information Element array
1222          * @param beaconCap      -- 16-bit Beacon Capability Information field
1223          * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device
1224          * @param freq           -- Frequency on which frame/beacon was transmitted.
1225          *                          Some parsing may be affected such as DMG parameters in
1226          *                          DMG (60GHz) beacon.
1227          */
1228 
from(InformationElement[] ies, int beaconCap, boolean isOweSupported, int freq)1229         public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported,
1230                 int freq) {
1231             protocol = new ArrayList<>();
1232             keyManagement = new ArrayList<>();
1233             groupCipher = new ArrayList<>();
1234             pairwiseCipher = new ArrayList<>();
1235             groupManagementCipher = new ArrayList<>();
1236 
1237             if (ies == null) {
1238                 return;
1239             }
1240             isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0;
1241             if (ScanResult.is60GHz(freq)) {
1242                 /* In DMG, bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1 */
1243                 if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_ESS)
1244                         == NativeScanResult.BSS_CAPABILITY_DMG_ESS) {
1245                     isESS = true;
1246                 } else if ((beaconCap & NativeScanResult.BSS_CAPABILITY_DMG_IBSS) != 0) {
1247                     isIBSS = true;
1248                 }
1249             } else {
1250                 isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0;
1251                 isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0;
1252             }
1253             for (InformationElement ie : ies) {
1254                 WifiNl80211Manager.OemSecurityType oemSecurityType =
1255                         WifiNl80211Manager.parseOemSecurityTypeElement(
1256                         ie.id, ie.idExt, ie.bytes);
1257                 if (oemSecurityType != null
1258                         && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) {
1259                     protocol.add(oemSecurityType.protocol);
1260                     keyManagement.add(oemSecurityType.keyManagement);
1261                     pairwiseCipher.add(oemSecurityType.pairwiseCipher);
1262                     groupCipher.add(oemSecurityType.groupCipher);
1263                 }
1264 
1265                 if (ie.id == InformationElement.EID_RSN) {
1266                     parseRsnElement(ie);
1267                 }
1268 
1269                 if (ie.id == InformationElement.EID_VSA) {
1270                     if (isWpaOneElement(ie)) {
1271                         parseWpaOneElement(ie);
1272                     }
1273                     if (isWpsElement(ie)) {
1274                         // TODO(b/62134557): parse WPS IE to provide finer granularity information.
1275                         isWPS = true;
1276                     }
1277                     if (isOweSupported && isOweElement(ie)) {
1278                         /* From RFC 8110: Once the client and AP have finished 802.11 association,
1279                            they then complete the Diffie-Hellman key exchange and create a Pairwise
1280                            Master Key (PMK) and its associated identifier, PMKID [IEEE802.11].
1281                            Upon completion of 802.11 association, the AP initiates the 4-way
1282                            handshake to the client using the PMK generated above.  The 4-way
1283                            handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation
1284                            Key (KCK), and a Message Integrity Code (MIC) to use for protection
1285                            of the frames that define the 4-way handshake.
1286 
1287                            We check if OWE is supported here because we are adding the OWE
1288                            capabilities to the Open BSS. Non-supporting devices need to see this
1289                            open network and ignore this element. Supporting devices need to hide
1290                            the Open BSS of OWE in transition mode and connect to the Hidden one.
1291                         */
1292                         protocol.add(ScanResult.PROTOCOL_RSN);
1293                         groupCipher.add(ScanResult.CIPHER_CCMP);
1294                         ArrayList<Integer> owePairwiseCipher = new ArrayList<>();
1295                         owePairwiseCipher.add(ScanResult.CIPHER_CCMP);
1296                         pairwiseCipher.add(owePairwiseCipher);
1297                         ArrayList<Integer> oweKeyManagement = new ArrayList<>();
1298                         oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION);
1299                         keyManagement.add(oweKeyManagement);
1300                     }
1301                 }
1302             }
1303         }
1304 
isOweElement(InformationElement ie)1305         private static boolean isOweElement(InformationElement ie) {
1306             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1307             try {
1308                 // OWE OUI and type
1309                 return (buf.getInt() == OWE_VENDOR_OUI_TYPE);
1310             } catch (BufferUnderflowException e) {
1311                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1312                 return false;
1313             }
1314         }
1315 
protocolToString(@rotocol int protocol)1316         private String protocolToString(@Protocol int protocol) {
1317             switch (protocol) {
1318                 case ScanResult.PROTOCOL_NONE:
1319                     return "None";
1320                 case ScanResult.PROTOCOL_WPA:
1321                     return "WPA";
1322                 case ScanResult.PROTOCOL_RSN:
1323                     return "RSN";
1324                 case ScanResult.PROTOCOL_OSEN:
1325                     return "OSEN";
1326                 case ScanResult.PROTOCOL_WAPI:
1327                     return "WAPI";
1328                 default:
1329                     return "?";
1330             }
1331         }
1332 
keyManagementToString(@eyMgmt int akm)1333         private String keyManagementToString(@KeyMgmt int akm) {
1334             switch (akm) {
1335                 case ScanResult.KEY_MGMT_NONE:
1336                     return "None";
1337                 case ScanResult.KEY_MGMT_PSK:
1338                     return "PSK";
1339                 case ScanResult.KEY_MGMT_EAP:
1340                     return "EAP/SHA1";
1341                 case ScanResult.KEY_MGMT_FT_EAP:
1342                     return "FT/EAP";
1343                 case ScanResult.KEY_MGMT_FT_PSK:
1344                     return "FT/PSK";
1345                 case ScanResult.KEY_MGMT_EAP_SHA256:
1346                     return "EAP/SHA256";
1347                 case ScanResult.KEY_MGMT_PSK_SHA256:
1348                     return "PSK-SHA256";
1349                 case ScanResult.KEY_MGMT_OWE:
1350                     return "OWE";
1351                 case ScanResult.KEY_MGMT_OWE_TRANSITION:
1352                     return "OWE_TRANSITION";
1353                 case ScanResult.KEY_MGMT_SAE:
1354                     return "SAE";
1355                 case ScanResult.KEY_MGMT_FT_SAE:
1356                     return "FT/SAE";
1357                 case ScanResult.KEY_MGMT_EAP_SUITE_B_192:
1358                     return "EAP_SUITE_B_192";
1359                 case ScanResult.KEY_MGMT_OSEN:
1360                     return "OSEN";
1361                 case ScanResult.KEY_MGMT_WAPI_PSK:
1362                     return "WAPI-PSK";
1363                 case ScanResult.KEY_MGMT_WAPI_CERT:
1364                     return "WAPI-CERT";
1365                 case ScanResult.KEY_MGMT_FILS_SHA256:
1366                     return "EAP-FILS-SHA256";
1367                 case ScanResult.KEY_MGMT_FILS_SHA384:
1368                     return "EAP-FILS-SHA384";
1369                 default:
1370                     return "?";
1371             }
1372         }
1373 
cipherToString(@ipher int cipher)1374         private String cipherToString(@Cipher int cipher) {
1375             switch (cipher) {
1376                 case ScanResult.CIPHER_NONE:
1377                     return "None";
1378                 case ScanResult.CIPHER_CCMP:
1379                     return "CCMP";
1380                 case ScanResult.CIPHER_GCMP_256:
1381                     return "GCMP-256";
1382                 case ScanResult.CIPHER_TKIP:
1383                     return "TKIP";
1384                 case ScanResult.CIPHER_SMS4:
1385                     return "SMS4";
1386                 default:
1387                     return "?";
1388             }
1389         }
1390 
1391         /**
1392          * Build the ScanResult.capabilities String.
1393          *
1394          * @return security string that mirrors what wpa_supplicant generates
1395          */
generateCapabilitiesString()1396         public String generateCapabilitiesString() {
1397             StringBuilder capabilities = new StringBuilder();
1398             // private Beacon without an RSNE or WPA IE, hence WEP0
1399             boolean isWEP = (protocol.isEmpty()) && isPrivacy;
1400 
1401             if (isWEP) {
1402                 capabilities.append("[WEP]");
1403             }
1404             for (int i = 0; i < protocol.size(); i++) {
1405                 String capability = generateCapabilitiesStringPerProtocol(i);
1406                 // add duplicate capabilities for WPA2 for backward compatibility:
1407                 // duplicate "RSN" entries as "WPA2"
1408                 String capWpa2 = generateWPA2CapabilitiesString(capability, i);
1409                 capabilities.append(capWpa2);
1410                 capabilities.append(capability);
1411             }
1412             if (isESS) {
1413                 capabilities.append("[ESS]");
1414             }
1415             if (isIBSS) {
1416                 capabilities.append("[IBSS]");
1417             }
1418             if (isWPS) {
1419                 capabilities.append("[WPS]");
1420             }
1421             if (!groupManagementCipher.isEmpty()) {
1422                 if (isManagementFrameProtectionRequired) {
1423                     capabilities.append("[MFPR]");
1424                 }
1425                 if (isManagementFrameProtectionCapable) {
1426                     capabilities.append("[MFPC]");
1427                 }
1428             }
1429 
1430             return capabilities.toString();
1431         }
1432 
1433         /**
1434          * Build the Capability String for one protocol
1435          * @param index: index number of the protocol
1436          * @return security string for one protocol
1437          */
generateCapabilitiesStringPerProtocol(int index)1438         private String generateCapabilitiesStringPerProtocol(int index) {
1439             StringBuilder capability = new StringBuilder();
1440             capability.append("[").append(protocolToString(protocol.get(index)));
1441 
1442             if (index < keyManagement.size()) {
1443                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1444                     capability.append((j == 0) ? "-" : "+").append(
1445                             keyManagementToString(keyManagement.get(index).get(j)));
1446                 }
1447             }
1448             if (index < pairwiseCipher.size()) {
1449                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1450                     capability.append((j == 0) ? "-" : "+").append(
1451                             cipherToString(pairwiseCipher.get(index).get(j)));
1452                 }
1453             }
1454             capability.append("]");
1455             return capability.toString();
1456         }
1457 
1458         /**
1459          * Build the duplicate Capability String for WPA2
1460          * @param cap: original capability String
1461          * @param index: index number of the protocol
1462          * @return security string for WPA2, empty String if protocol is not WPA2
1463          */
generateWPA2CapabilitiesString(String cap, int index)1464         private String generateWPA2CapabilitiesString(String cap, int index) {
1465             StringBuilder capWpa2 = new StringBuilder();
1466             // if not WPA2, return empty String
1467             if (cap.contains("EAP_SUITE_B_192")
1468                     || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP")
1469                     && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) {
1470                 return "";
1471             }
1472             capWpa2.append("[").append("WPA2");
1473             if (index < keyManagement.size()) {
1474                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1475                     capWpa2.append((j == 0) ? "-" : "+").append(
1476                             keyManagementToString(keyManagement.get(index).get(j)));
1477                     // WPA3/WPA2 transition mode
1478                     if (cap.contains("SAE")) {
1479                         break;
1480                     }
1481                 }
1482             }
1483             if (index < pairwiseCipher.size()) {
1484                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1485                     capWpa2.append((j == 0) ? "-" : "+").append(
1486                             cipherToString(pairwiseCipher.get(index).get(j)));
1487                 }
1488             }
1489             capWpa2.append("]");
1490             return capWpa2.toString();
1491         }
1492     }
1493 
1494 
1495     /**
1496      * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
1497      * only be present in scan results that are derived from a Beacon Frame, not from the more
1498      * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
1499      */
1500     public static class TrafficIndicationMap {
1501         private static final int MAX_TIM_LENGTH = 254;
1502         private boolean mValid = false;
1503         public int mLength = 0;
1504         public int mDtimCount = -1;
1505         //Negative DTIM Period means no TIM element was given this frame.
1506         public int mDtimPeriod = -1;
1507         public int mBitmapControl = 0;
1508 
1509         /**
1510          * Is this a valid TIM information element.
1511          */
isValid()1512         public boolean isValid() {
1513             return mValid;
1514         }
1515 
1516         // Traffic Indication Map format (size unit: byte)
1517         //
1518         //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
1519         //      1          1          1            1               1                1 - 251
1520         //
1521         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1522         //       stripped off already
1523         //
from(InformationElement ie)1524         public void from(InformationElement ie) {
1525             mValid = false;
1526             if (ie == null || ie.bytes == null) return;
1527             mLength = ie.bytes.length;
1528             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1529             try {
1530                 mDtimCount = data.get() & Constants.BYTE_MASK;
1531                 mDtimPeriod = data.get() & Constants.BYTE_MASK;
1532                 mBitmapControl = data.get() & Constants.BYTE_MASK;
1533                 //A valid TIM element must have atleast one more byte
1534                 data.get();
1535             } catch (BufferUnderflowException e) {
1536                 return;
1537             }
1538             if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
1539                 mValid = true;
1540             }
1541         }
1542     }
1543 
1544     /**
1545      * This util class determines the 802.11 standard (a/b/g/n/ac/ax) being used
1546      */
1547     public static class WifiMode {
1548         public static final int MODE_UNDEFINED = 0; // Unknown/undefined
1549         public static final int MODE_11A = 1;       // 802.11a
1550         public static final int MODE_11B = 2;       // 802.11b
1551         public static final int MODE_11G = 3;       // 802.11g
1552         public static final int MODE_11N = 4;       // 802.11n
1553         public static final int MODE_11AC = 5;      // 802.11ac
1554         public static final int MODE_11AX = 6;      // 802.11ax
1555         //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
1556 
1557         /**
1558          * Use frequency, max supported rate, and the existence of HE, VHT, HT & ERP fields in scan
1559          * scan result to determine the 802.11 Wifi standard being used.
1560          */
determineMode(int frequency, int maxRate, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)1561         public static int determineMode(int frequency, int maxRate, boolean foundHe,
1562                 boolean foundVht, boolean foundHt, boolean foundErp) {
1563             if (foundHe) {
1564                 return MODE_11AX;
1565             } else if (!ScanResult.is24GHz(frequency) && foundVht) {
1566                 // Do not include subset of VHT on 2.4 GHz vendor extension
1567                 // in consideration for reporting VHT.
1568                 return MODE_11AC;
1569             } else if (foundHt) {
1570                 return MODE_11N;
1571             } else if (foundErp) {
1572                 return MODE_11G;
1573             } else if (ScanResult.is24GHz(frequency)) {
1574                 if (maxRate < 24000000) {
1575                     return MODE_11B;
1576                 } else {
1577                     return MODE_11G;
1578                 }
1579             } else {
1580                 return MODE_11A;
1581             }
1582         }
1583 
1584         /**
1585          * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
1586          */
toString(int mode)1587         public static String toString(int mode) {
1588             switch(mode) {
1589                 case MODE_11A:
1590                     return "MODE_11A";
1591                 case MODE_11B:
1592                     return "MODE_11B";
1593                 case MODE_11G:
1594                     return "MODE_11G";
1595                 case MODE_11N:
1596                     return "MODE_11N";
1597                 case MODE_11AC:
1598                     return "MODE_11AC";
1599                 case MODE_11AX:
1600                     return "MODE_11AX";
1601                 default:
1602                     return "MODE_UNDEFINED";
1603             }
1604         }
1605     }
1606 
1607     /**
1608      * Parser for both the Supported Rates & Extended Supported Rates Information Elements
1609      */
1610     public static class SupportedRates {
1611         public static final int MASK = 0x7F; // 0111 1111
1612         public boolean mValid = false;
1613         public ArrayList<Integer> mRates;
1614 
SupportedRates()1615         public SupportedRates() {
1616             mRates = new ArrayList<Integer>();
1617         }
1618 
1619         /**
1620          * Is this a valid Supported Rates information element.
1621          */
isValid()1622         public boolean isValid() {
1623             return mValid;
1624         }
1625 
1626         /**
1627          * get the Rate in bits/s from associated byteval
1628          */
getRateFromByte(int byteVal)1629         public static int getRateFromByte(int byteVal) {
1630             byteVal &= MASK;
1631             switch(byteVal) {
1632                 case 2:
1633                     return 1000000;
1634                 case 4:
1635                     return 2000000;
1636                 case 11:
1637                     return 5500000;
1638                 case 12:
1639                     return 6000000;
1640                 case 18:
1641                     return 9000000;
1642                 case 22:
1643                     return 11000000;
1644                 case 24:
1645                     return 12000000;
1646                 case 36:
1647                     return 18000000;
1648                 case 44:
1649                     return 22000000;
1650                 case 48:
1651                     return 24000000;
1652                 case 66:
1653                     return 33000000;
1654                 case 72:
1655                     return 36000000;
1656                 case 96:
1657                     return 48000000;
1658                 case 108:
1659                     return 54000000;
1660                 default:
1661                     //ERROR UNKNOWN RATE
1662                     return -1;
1663             }
1664         }
1665 
1666         // Supported Rates format (size unit: byte)
1667         //
1668         //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
1669         //      1          1          1 - 8
1670         //
1671         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1672         //       stripped off already
1673         //
from(InformationElement ie)1674         public void from(InformationElement ie) {
1675             mValid = false;
1676             if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
1677                 return;
1678             }
1679             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1680             try {
1681                 for (int i = 0; i < ie.bytes.length; i++) {
1682                     int rate = getRateFromByte(data.get());
1683                     if (rate > 0) {
1684                         mRates.add(rate);
1685                     } else {
1686                         return;
1687                     }
1688                 }
1689             } catch (BufferUnderflowException e) {
1690                 return;
1691             }
1692             mValid = true;
1693             return;
1694         }
1695 
1696         /**
1697          * Lists the rates in a human readable string
1698          */
toString()1699         public String toString() {
1700             StringBuilder sbuf = new StringBuilder();
1701             for (Integer rate : mRates) {
1702                 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
1703             }
1704             return sbuf.toString();
1705         }
1706     }
1707 }
1708