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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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.convertChannelToFrequencyMhz(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_FILS_SHA256 = 0x0eac0f00;
922         private static final int RSN_AKM_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 
934         public List<Integer> protocol;
935         public List<List<Integer>> keyManagement;
936         public List<List<Integer>> pairwiseCipher;
937         public List<Integer> groupCipher;
938         public boolean isESS;
939         public boolean isIBSS;
940         public boolean isPrivacy;
941         public boolean isWPS;
942 
Capabilities()943         public Capabilities() {
944         }
945 
946         // RSNE format (size unit: byte)
947         //
948         // | Element ID | Length | Version | Group Data Cipher Suite |
949         //      1           1         2                 4
950         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
951         //              2                            4 * m
952         // | AKM Suite Count | AKM Suite List | RSN Capabilities |
953         //          2               4 * n               2
954         // | PMKID Count | PMKID List | Group Management Cipher Suite |
955         //        2          16 * s                 4
956         //
957         // Note: InformationElement.bytes has 'Element ID' and 'Length'
958         //       stripped off already
parseRsnElement(InformationElement ie)959         private void parseRsnElement(InformationElement ie) {
960             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
961 
962             try {
963                 // version
964                 if (buf.getShort() != RSNE_VERSION) {
965                     // incorrect version
966                     return;
967                 }
968 
969                 // found the RSNE IE, hence start building the capability string
970                 protocol.add(ScanResult.PROTOCOL_RSN);
971 
972                 // group data cipher suite
973                 groupCipher.add(parseRsnCipher(buf.getInt()));
974 
975                 // pairwise cipher suite count
976                 short cipherCount = buf.getShort();
977                 ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
978                 // pairwise cipher suite list
979                 for (int i = 0; i < cipherCount; i++) {
980                     rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
981                 }
982                 pairwiseCipher.add(rsnPairwiseCipher);
983 
984                 // AKM
985                 // AKM suite count
986                 short akmCount = buf.getShort();
987                 ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
988 
989                 for (int i = 0; i < akmCount; i++) {
990                     int akm = buf.getInt();
991                     switch (akm) {
992                         case RSN_AKM_EAP:
993                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
994                             break;
995                         case RSN_AKM_PSK:
996                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
997                             break;
998                         case RSN_AKM_FT_EAP:
999                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
1000                             break;
1001                         case RSN_AKM_FT_PSK:
1002                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
1003                             break;
1004                         case RSN_AKM_EAP_SHA256:
1005                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
1006                             break;
1007                         case RSN_AKM_PSK_SHA256:
1008                             rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
1009                             break;
1010                         case RSN_AKM_SAE:
1011                             rsnKeyManagement.add(ScanResult.KEY_MGMT_SAE);
1012                             break;
1013                         case RSN_AKM_FT_SAE:
1014                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_SAE);
1015                             break;
1016                         case RSN_AKM_OWE:
1017                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OWE);
1018                             break;
1019                         case RSN_AKM_EAP_SUITE_B_192:
1020                             rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SUITE_B_192);
1021                             break;
1022                         case RSN_OSEN:
1023                             rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN);
1024                             break;
1025                         case RSN_AKM_FILS_SHA256:
1026                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256);
1027                             break;
1028                         case RSN_AKM_FILS_SHA384:
1029                             rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384);
1030                             break;
1031                         default:
1032                             // do nothing
1033                             break;
1034                     }
1035                 }
1036                 // Default AKM
1037                 if (rsnKeyManagement.isEmpty()) {
1038                     rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1039                 }
1040                 keyManagement.add(rsnKeyManagement);
1041             } catch (BufferUnderflowException e) {
1042                 Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
1043             }
1044         }
1045 
parseWpaCipher(int cipher)1046         private static @Cipher int parseWpaCipher(int cipher) {
1047             switch (cipher) {
1048                 case WPA_CIPHER_NONE:
1049                     return ScanResult.CIPHER_NONE;
1050                 case WPA_CIPHER_TKIP:
1051                     return ScanResult.CIPHER_TKIP;
1052                 case WPA_CIPHER_CCMP:
1053                     return ScanResult.CIPHER_CCMP;
1054                 default:
1055                     Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
1056                             + Integer.toHexString(cipher));
1057                     return ScanResult.CIPHER_NONE;
1058             }
1059         }
1060 
parseRsnCipher(int cipher)1061         private static @Cipher int parseRsnCipher(int cipher) {
1062             switch (cipher) {
1063                 case RSN_CIPHER_NONE:
1064                     return ScanResult.CIPHER_NONE;
1065                 case RSN_CIPHER_TKIP:
1066                     return ScanResult.CIPHER_TKIP;
1067                 case RSN_CIPHER_CCMP:
1068                     return ScanResult.CIPHER_CCMP;
1069                 case RSN_CIPHER_GCMP_256:
1070                     return ScanResult.CIPHER_GCMP_256;
1071                 case RSN_CIPHER_NO_GROUP_ADDRESSED:
1072                     return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
1073                 default:
1074                     Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
1075                             + Integer.toHexString(cipher));
1076                     return ScanResult.CIPHER_NONE;
1077             }
1078         }
1079 
isWpsElement(InformationElement ie)1080         private static boolean isWpsElement(InformationElement ie) {
1081             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1082             try {
1083                 // WPS OUI and type
1084                 return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
1085             } catch (BufferUnderflowException e) {
1086                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1087                 return false;
1088             }
1089         }
1090 
isWpaOneElement(InformationElement ie)1091         private static boolean isWpaOneElement(InformationElement ie) {
1092             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1093 
1094             try {
1095                 // WPA OUI and type
1096                 return (buf.getInt() == WPA_VENDOR_OUI_TYPE_ONE);
1097             } catch (BufferUnderflowException e) {
1098                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1099                 return false;
1100             }
1101         }
1102 
1103         // WPA type 1 format (size unit: byte)
1104         //
1105         // | Element ID | Length | OUI | Type | Version |
1106         //      1           1       3     1        2
1107         // | Group Data Cipher Suite |
1108         //             4
1109         // | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
1110         //              2                            4 * m
1111         // | AKM Suite Count | AKM Suite List |
1112         //          2               4 * n
1113         //
1114         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1115         //       stripped off already
1116         //
parseWpaOneElement(InformationElement ie)1117         private void parseWpaOneElement(InformationElement ie) {
1118             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1119 
1120             try {
1121                 // skip WPA OUI and type parsing. isWpaOneElement() should have
1122                 // been called for verification before we reach here.
1123                 buf.getInt();
1124 
1125                 // version
1126                 if (buf.getShort() != WPA_VENDOR_OUI_VERSION) {
1127                     // incorrect version
1128                     return;
1129                 }
1130 
1131                 // start building the string
1132                 protocol.add(ScanResult.PROTOCOL_WPA);
1133 
1134                 // group data cipher suite
1135                 groupCipher.add(parseWpaCipher(buf.getInt()));
1136 
1137                 // pairwise cipher suite count
1138                 short cipherCount = buf.getShort();
1139                 ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
1140                 // pairwise chipher suite list
1141                 for (int i = 0; i < cipherCount; i++) {
1142                     wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
1143                 }
1144                 pairwiseCipher.add(wpaPairwiseCipher);
1145 
1146                 // AKM
1147                 // AKM suite count
1148                 short akmCount = buf.getShort();
1149                 ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
1150 
1151                 // AKM suite list
1152                 for (int i = 0; i < akmCount; i++) {
1153                     int akm = buf.getInt();
1154                     switch (akm) {
1155                         case WPA_AKM_EAP:
1156                             wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1157                             break;
1158                         case WPA_AKM_PSK:
1159                             wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
1160                             break;
1161                         default:
1162                             // do nothing
1163                             break;
1164                     }
1165                 }
1166                 // Default AKM
1167                 if (wpaKeyManagement.isEmpty()) {
1168                     wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
1169                 }
1170                 keyManagement.add(wpaKeyManagement);
1171             } catch (BufferUnderflowException e) {
1172                 Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
1173             }
1174         }
1175 
1176         /**
1177          * Parse the Information Element and the 16-bit Capability Information field
1178          * to build the InformationElemmentUtil.capabilities object.
1179          *
1180          * @param ies            -- Information Element array
1181          * @param beaconCap      -- 16-bit Beacon Capability Information field
1182          * @param isOweSupported -- Boolean flag to indicate if OWE is supported by the device
1183          */
1184 
from(InformationElement[] ies, int beaconCap, boolean isOweSupported)1185         public void from(InformationElement[] ies, int beaconCap, boolean isOweSupported) {
1186             protocol = new ArrayList<>();
1187             keyManagement = new ArrayList<>();
1188             groupCipher = new ArrayList<>();
1189             pairwiseCipher = new ArrayList<>();
1190 
1191             if (ies == null) {
1192                 return;
1193             }
1194             isESS = (beaconCap & NativeScanResult.BSS_CAPABILITY_ESS) != 0;
1195             isIBSS = (beaconCap & NativeScanResult.BSS_CAPABILITY_IBSS) != 0;
1196             isPrivacy = (beaconCap & NativeScanResult.BSS_CAPABILITY_PRIVACY) != 0;
1197             for (InformationElement ie : ies) {
1198                 WifiNl80211Manager.OemSecurityType oemSecurityType =
1199                         WifiNl80211Manager.parseOemSecurityTypeElement(
1200                         ie.id, ie.idExt, ie.bytes);
1201                 if (oemSecurityType != null
1202                         && oemSecurityType.protocol != ScanResult.PROTOCOL_NONE) {
1203                     protocol.add(oemSecurityType.protocol);
1204                     keyManagement.add(oemSecurityType.keyManagement);
1205                     pairwiseCipher.add(oemSecurityType.pairwiseCipher);
1206                     groupCipher.add(oemSecurityType.groupCipher);
1207                 }
1208 
1209                 if (ie.id == InformationElement.EID_RSN) {
1210                     parseRsnElement(ie);
1211                 }
1212 
1213                 if (ie.id == InformationElement.EID_VSA) {
1214                     if (isWpaOneElement(ie)) {
1215                         parseWpaOneElement(ie);
1216                     }
1217                     if (isWpsElement(ie)) {
1218                         // TODO(b/62134557): parse WPS IE to provide finer granularity information.
1219                         isWPS = true;
1220                     }
1221                     if (isOweSupported && isOweElement(ie)) {
1222                         /* From RFC 8110: Once the client and AP have finished 802.11 association,
1223                            they then complete the Diffie-Hellman key exchange and create a Pairwise
1224                            Master Key (PMK) and its associated identifier, PMKID [IEEE802.11].
1225                            Upon completion of 802.11 association, the AP initiates the 4-way
1226                            handshake to the client using the PMK generated above.  The 4-way
1227                            handshake generates a Key-Encrypting Key (KEK), a Key-Confirmation
1228                            Key (KCK), and a Message Integrity Code (MIC) to use for protection
1229                            of the frames that define the 4-way handshake.
1230 
1231                            We check if OWE is supported here because we are adding the OWE
1232                            capabilities to the Open BSS. Non-supporting devices need to see this
1233                            open network and ignore this element. Supporting devices need to hide
1234                            the Open BSS of OWE in transition mode and connect to the Hidden one.
1235                         */
1236                         protocol.add(ScanResult.PROTOCOL_RSN);
1237                         groupCipher.add(ScanResult.CIPHER_CCMP);
1238                         ArrayList<Integer> owePairwiseCipher = new ArrayList<>();
1239                         owePairwiseCipher.add(ScanResult.CIPHER_CCMP);
1240                         pairwiseCipher.add(owePairwiseCipher);
1241                         ArrayList<Integer> oweKeyManagement = new ArrayList<>();
1242                         oweKeyManagement.add(ScanResult.KEY_MGMT_OWE_TRANSITION);
1243                         keyManagement.add(oweKeyManagement);
1244                     }
1245                 }
1246             }
1247         }
1248 
isOweElement(InformationElement ie)1249         private static boolean isOweElement(InformationElement ie) {
1250             ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1251             try {
1252                 // OWE OUI and type
1253                 return (buf.getInt() == OWE_VENDOR_OUI_TYPE);
1254             } catch (BufferUnderflowException e) {
1255                 Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
1256                 return false;
1257             }
1258         }
1259 
protocolToString(@rotocol int protocol)1260         private String protocolToString(@Protocol int protocol) {
1261             switch (protocol) {
1262                 case ScanResult.PROTOCOL_NONE:
1263                     return "None";
1264                 case ScanResult.PROTOCOL_WPA:
1265                     return "WPA";
1266                 case ScanResult.PROTOCOL_RSN:
1267                     return "RSN";
1268                 case ScanResult.PROTOCOL_OSEN:
1269                     return "OSEN";
1270                 case ScanResult.PROTOCOL_WAPI:
1271                     return "WAPI";
1272                 default:
1273                     return "?";
1274             }
1275         }
1276 
keyManagementToString(@eyMgmt int akm)1277         private String keyManagementToString(@KeyMgmt int akm) {
1278             switch (akm) {
1279                 case ScanResult.KEY_MGMT_NONE:
1280                     return "None";
1281                 case ScanResult.KEY_MGMT_PSK:
1282                     return "PSK";
1283                 case ScanResult.KEY_MGMT_EAP:
1284                     return "EAP";
1285                 case ScanResult.KEY_MGMT_FT_EAP:
1286                     return "FT/EAP";
1287                 case ScanResult.KEY_MGMT_FT_PSK:
1288                     return "FT/PSK";
1289                 case ScanResult.KEY_MGMT_EAP_SHA256:
1290                     return "EAP-SHA256";
1291                 case ScanResult.KEY_MGMT_PSK_SHA256:
1292                     return "PSK-SHA256";
1293                 case ScanResult.KEY_MGMT_OWE:
1294                     return "OWE";
1295                 case ScanResult.KEY_MGMT_OWE_TRANSITION:
1296                     return "OWE_TRANSITION";
1297                 case ScanResult.KEY_MGMT_SAE:
1298                     return "SAE";
1299                 case ScanResult.KEY_MGMT_FT_SAE:
1300                     return "FT/SAE";
1301                 case ScanResult.KEY_MGMT_EAP_SUITE_B_192:
1302                     return "EAP_SUITE_B_192";
1303                 case ScanResult.KEY_MGMT_OSEN:
1304                     return "OSEN";
1305                 case ScanResult.KEY_MGMT_WAPI_PSK:
1306                     return "WAPI-PSK";
1307                 case ScanResult.KEY_MGMT_WAPI_CERT:
1308                     return "WAPI-CERT";
1309                 case ScanResult.KEY_MGMT_FILS_SHA256:
1310                     return "FILS-SHA256";
1311                 case ScanResult.KEY_MGMT_FILS_SHA384:
1312                     return "FILS-SHA384";
1313                 default:
1314                     return "?";
1315             }
1316         }
1317 
cipherToString(@ipher int cipher)1318         private String cipherToString(@Cipher int cipher) {
1319             switch (cipher) {
1320                 case ScanResult.CIPHER_NONE:
1321                     return "None";
1322                 case ScanResult.CIPHER_CCMP:
1323                     return "CCMP";
1324                 case ScanResult.CIPHER_GCMP_256:
1325                     return "GCMP-256";
1326                 case ScanResult.CIPHER_TKIP:
1327                     return "TKIP";
1328                 case ScanResult.CIPHER_SMS4:
1329                     return "SMS4";
1330                 default:
1331                     return "?";
1332             }
1333         }
1334 
1335         /**
1336          * Build the ScanResult.capabilities String.
1337          *
1338          * @return security string that mirrors what wpa_supplicant generates
1339          */
generateCapabilitiesString()1340         public String generateCapabilitiesString() {
1341             StringBuilder capabilities = new StringBuilder();
1342             // private Beacon without an RSNE or WPA IE, hence WEP0
1343             boolean isWEP = (protocol.isEmpty()) && isPrivacy;
1344 
1345             if (isWEP) {
1346                 capabilities.append("[WEP]");
1347             }
1348             for (int i = 0; i < protocol.size(); i++) {
1349                 String capability = generateCapabilitiesStringPerProtocol(i);
1350                 // add duplicate capabilities for WPA2 for backward compatibility:
1351                 // duplicate "RSN" entries as "WPA2"
1352                 String capWpa2 = generateWPA2CapabilitiesString(capability, i);
1353                 capabilities.append(capWpa2);
1354                 capabilities.append(capability);
1355             }
1356             if (isESS) {
1357                 capabilities.append("[ESS]");
1358             }
1359             if (isIBSS) {
1360                 capabilities.append("[IBSS]");
1361             }
1362             if (isWPS) {
1363                 capabilities.append("[WPS]");
1364             }
1365 
1366             return capabilities.toString();
1367         }
1368 
1369         /**
1370          * Build the Capability String for one protocol
1371          * @param index: index number of the protocol
1372          * @return security string for one protocol
1373          */
generateCapabilitiesStringPerProtocol(int index)1374         private String generateCapabilitiesStringPerProtocol(int index) {
1375             StringBuilder capability = new StringBuilder();
1376             capability.append("[").append(protocolToString(protocol.get(index)));
1377 
1378             if (index < keyManagement.size()) {
1379                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1380                     capability.append((j == 0) ? "-" : "+").append(
1381                             keyManagementToString(keyManagement.get(index).get(j)));
1382                 }
1383             }
1384             if (index < pairwiseCipher.size()) {
1385                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1386                     capability.append((j == 0) ? "-" : "+").append(
1387                             cipherToString(pairwiseCipher.get(index).get(j)));
1388                 }
1389             }
1390             capability.append("]");
1391             return capability.toString();
1392         }
1393 
1394         /**
1395          * Build the duplicate Capability String for WPA2
1396          * @param cap: original capability String
1397          * @param index: index number of the protocol
1398          * @return security string for WPA2, empty String if protocol is not WPA2
1399          */
generateWPA2CapabilitiesString(String cap, int index)1400         private String generateWPA2CapabilitiesString(String cap, int index) {
1401             StringBuilder capWpa2 = new StringBuilder();
1402             // if not WPA2, return empty String
1403             if (cap.contains("EAP_SUITE_B_192")
1404                     || (!cap.contains("RSN-EAP") && !cap.contains("RSN-FT/EAP")
1405                     && !cap.contains("RSN-PSK") && !cap.contains("RSN-FT/PSK"))) {
1406                 return "";
1407             }
1408             capWpa2.append("[").append("WPA2");
1409             if (index < keyManagement.size()) {
1410                 for (int j = 0; j < keyManagement.get(index).size(); j++) {
1411                     capWpa2.append((j == 0) ? "-" : "+").append(
1412                             keyManagementToString(keyManagement.get(index).get(j)));
1413                     // WPA3/WPA2 transition mode
1414                     if (cap.contains("SAE")) {
1415                         break;
1416                     }
1417                 }
1418             }
1419             if (index < pairwiseCipher.size()) {
1420                 for (int j = 0; j < pairwiseCipher.get(index).size(); j++) {
1421                     capWpa2.append((j == 0) ? "-" : "+").append(
1422                             cipherToString(pairwiseCipher.get(index).get(j)));
1423                 }
1424             }
1425             capWpa2.append("]");
1426             return capWpa2.toString();
1427         }
1428     }
1429 
1430 
1431     /**
1432      * Parser for the Traffic Indication Map (TIM) Information Element (EID 5). This element will
1433      * only be present in scan results that are derived from a Beacon Frame, not from the more
1434      * plentiful probe responses. Call 'isValid()' after parsing, to ensure the results are correct.
1435      */
1436     public static class TrafficIndicationMap {
1437         private static final int MAX_TIM_LENGTH = 254;
1438         private boolean mValid = false;
1439         public int mLength = 0;
1440         public int mDtimCount = -1;
1441         //Negative DTIM Period means no TIM element was given this frame.
1442         public int mDtimPeriod = -1;
1443         public int mBitmapControl = 0;
1444 
1445         /**
1446          * Is this a valid TIM information element.
1447          */
isValid()1448         public boolean isValid() {
1449             return mValid;
1450         }
1451 
1452         // Traffic Indication Map format (size unit: byte)
1453         //
1454         //| ElementID | Length | DTIM Count | DTIM Period | BitmapControl | Partial Virtual Bitmap |
1455         //      1          1          1            1               1                1 - 251
1456         //
1457         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1458         //       stripped off already
1459         //
from(InformationElement ie)1460         public void from(InformationElement ie) {
1461             mValid = false;
1462             if (ie == null || ie.bytes == null) return;
1463             mLength = ie.bytes.length;
1464             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1465             try {
1466                 mDtimCount = data.get() & Constants.BYTE_MASK;
1467                 mDtimPeriod = data.get() & Constants.BYTE_MASK;
1468                 mBitmapControl = data.get() & Constants.BYTE_MASK;
1469                 //A valid TIM element must have atleast one more byte
1470                 data.get();
1471             } catch (BufferUnderflowException e) {
1472                 return;
1473             }
1474             if (mLength <= MAX_TIM_LENGTH && mDtimPeriod > 0) {
1475                 mValid = true;
1476             }
1477         }
1478     }
1479 
1480     /**
1481      * This util class determines the 802.11 standard (a/b/g/n/ac/ax) being used
1482      */
1483     public static class WifiMode {
1484         public static final int MODE_UNDEFINED = 0; // Unknown/undefined
1485         public static final int MODE_11A = 1;       // 802.11a
1486         public static final int MODE_11B = 2;       // 802.11b
1487         public static final int MODE_11G = 3;       // 802.11g
1488         public static final int MODE_11N = 4;       // 802.11n
1489         public static final int MODE_11AC = 5;      // 802.11ac
1490         public static final int MODE_11AX = 6;      // 802.11ax
1491         //<TODO> add support for 802.11ad and be more selective instead of defaulting to 11A
1492 
1493         /**
1494          * Use frequency, max supported rate, and the existence of HE, VHT, HT & ERP fields in scan
1495          * scan result to determine the 802.11 Wifi standard being used.
1496          */
determineMode(int frequency, int maxRate, boolean foundHe, boolean foundVht, boolean foundHt, boolean foundErp)1497         public static int determineMode(int frequency, int maxRate, boolean foundHe,
1498                 boolean foundVht, boolean foundHt, boolean foundErp) {
1499             if (foundHe) {
1500                 return MODE_11AX;
1501             } else if (!ScanResult.is24GHz(frequency) && foundVht) {
1502                 // Do not include subset of VHT on 2.4 GHz vendor extension
1503                 // in consideration for reporting VHT.
1504                 return MODE_11AC;
1505             } else if (foundHt) {
1506                 return MODE_11N;
1507             } else if (foundErp) {
1508                 return MODE_11G;
1509             } else if (ScanResult.is24GHz(frequency)) {
1510                 if (maxRate < 24000000) {
1511                     return MODE_11B;
1512                 } else {
1513                     return MODE_11G;
1514                 }
1515             } else {
1516                 return MODE_11A;
1517             }
1518         }
1519 
1520         /**
1521          * Map the wifiMode integer to its type, and output as String MODE_11<A/B/G/N/AC>
1522          */
toString(int mode)1523         public static String toString(int mode) {
1524             switch(mode) {
1525                 case MODE_11A:
1526                     return "MODE_11A";
1527                 case MODE_11B:
1528                     return "MODE_11B";
1529                 case MODE_11G:
1530                     return "MODE_11G";
1531                 case MODE_11N:
1532                     return "MODE_11N";
1533                 case MODE_11AC:
1534                     return "MODE_11AC";
1535                 case MODE_11AX:
1536                     return "MODE_11AX";
1537                 default:
1538                     return "MODE_UNDEFINED";
1539             }
1540         }
1541     }
1542 
1543     /**
1544      * Parser for both the Supported Rates & Extended Supported Rates Information Elements
1545      */
1546     public static class SupportedRates {
1547         public static final int MASK = 0x7F; // 0111 1111
1548         public boolean mValid = false;
1549         public ArrayList<Integer> mRates;
1550 
SupportedRates()1551         public SupportedRates() {
1552             mRates = new ArrayList<Integer>();
1553         }
1554 
1555         /**
1556          * Is this a valid Supported Rates information element.
1557          */
isValid()1558         public boolean isValid() {
1559             return mValid;
1560         }
1561 
1562         /**
1563          * get the Rate in bits/s from associated byteval
1564          */
getRateFromByte(int byteVal)1565         public static int getRateFromByte(int byteVal) {
1566             byteVal &= MASK;
1567             switch(byteVal) {
1568                 case 2:
1569                     return 1000000;
1570                 case 4:
1571                     return 2000000;
1572                 case 11:
1573                     return 5500000;
1574                 case 12:
1575                     return 6000000;
1576                 case 18:
1577                     return 9000000;
1578                 case 22:
1579                     return 11000000;
1580                 case 24:
1581                     return 12000000;
1582                 case 36:
1583                     return 18000000;
1584                 case 44:
1585                     return 22000000;
1586                 case 48:
1587                     return 24000000;
1588                 case 66:
1589                     return 33000000;
1590                 case 72:
1591                     return 36000000;
1592                 case 96:
1593                     return 48000000;
1594                 case 108:
1595                     return 54000000;
1596                 default:
1597                     //ERROR UNKNOWN RATE
1598                     return -1;
1599             }
1600         }
1601 
1602         // Supported Rates format (size unit: byte)
1603         //
1604         //| ElementID | Length | Supported Rates  [7 Little Endian Info bits - 1 Flag bit]
1605         //      1          1          1 - 8
1606         //
1607         // Note: InformationElement.bytes has 'Element ID' and 'Length'
1608         //       stripped off already
1609         //
from(InformationElement ie)1610         public void from(InformationElement ie) {
1611             mValid = false;
1612             if (ie == null || ie.bytes == null || ie.bytes.length > 8 || ie.bytes.length < 1)  {
1613                 return;
1614             }
1615             ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
1616             try {
1617                 for (int i = 0; i < ie.bytes.length; i++) {
1618                     int rate = getRateFromByte(data.get());
1619                     if (rate > 0) {
1620                         mRates.add(rate);
1621                     } else {
1622                         return;
1623                     }
1624                 }
1625             } catch (BufferUnderflowException e) {
1626                 return;
1627             }
1628             mValid = true;
1629             return;
1630         }
1631 
1632         /**
1633          * Lists the rates in a human readable string
1634          */
toString()1635         public String toString() {
1636             StringBuilder sbuf = new StringBuilder();
1637             for (Integer rate : mRates) {
1638                 sbuf.append(String.format("%.1f", (double) rate / 1000000) + ", ");
1639             }
1640             return sbuf.toString();
1641         }
1642     }
1643 }
1644