Wifi: Parse HT/VHT/HE capabilities IE to derive maxNumSpatialStream
Bug: 143893522
Test: atest com.android.wifi.server and manual test with 11ac/ax AP
Change-Id: I28a60e6a9219fcf541a212d927c35ce31107cca0
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index ea1cfe6..b456ef4 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -25,7 +25,7 @@
private static final boolean DBG = false;
- private static final String TAG = "NetworkDetail:";
+ private static final String TAG = "NetworkDetail";
public enum Ant {
Private,
@@ -88,6 +88,7 @@
*/
private final int mWifiMode;
private final int mMaxRate;
+ private final int mMaxNumberSpatialStreams;
/*
* From Interworking element:
@@ -145,6 +146,13 @@
new InformationElementUtil.VhtOperation();
InformationElementUtil.HeOperation heOperation = new InformationElementUtil.HeOperation();
+ InformationElementUtil.HtCapabilities htCapabilities =
+ new InformationElementUtil.HtCapabilities();
+ InformationElementUtil.VhtCapabilities vhtCapabilities =
+ new InformationElementUtil.VhtCapabilities();
+ InformationElementUtil.HeCapabilities heCapabilities =
+ new InformationElementUtil.HeCapabilities();
+
InformationElementUtil.ExtendedCapabilities extendedCapabilities =
new InformationElementUtil.ExtendedCapabilities();
@@ -175,6 +183,12 @@
case ScanResult.InformationElement.EID_VHT_OPERATION:
vhtOperation.from(ie);
break;
+ case ScanResult.InformationElement.EID_HT_CAPABILITIES:
+ htCapabilities.from(ie);
+ break;
+ case ScanResult.InformationElement.EID_VHT_CAPABILITIES:
+ vhtCapabilities.from(ie);
+ break;
case ScanResult.InformationElement.EID_INTERWORKING:
interworking.from(ie);
break;
@@ -201,6 +215,9 @@
case ScanResult.InformationElement.EID_EXT_HE_OPERATION:
heOperation.from(ie);
break;
+ case ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES:
+ heCapabilities.from(ie);
+ break;
default:
break;
}
@@ -315,6 +332,10 @@
mDtimInterval = trafficIndicationMap.mDtimPeriod;
}
+ mMaxNumberSpatialStreams = Math.max(heCapabilities.getMaxNumberSpatialStreams(),
+ Math.max(vhtCapabilities.getMaxNumberSpatialStreams(),
+ htCapabilities.getMaxNumberSpatialStreams()));
+
int maxRateA = 0;
int maxRateB = 0;
// If we got some Extended supported rates, consider them, if not default to 0
@@ -334,10 +355,11 @@
mMaxRate = 0;
}
if (DBG) {
- Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq
- + " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: " + mCenterfreq1
- + (extendedCapabilities.is80211McRTTResponder() ? "Support RTT responder"
- : "Do not support RTT responder"));
+ Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: "
+ + mPrimaryFreq + " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: "
+ + mCenterfreq1 + (extendedCapabilities.is80211McRTTResponder()
+ ? " Support RTT responder" : " Do not support RTT responder")
+ + " mMaxNumberSpatialStreams: " + mMaxNumberSpatialStreams);
Log.v("WifiMode", mSSID
+ ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
+ ", Freq: " + mPrimaryFreq
@@ -383,6 +405,7 @@
mDtimInterval = base.mDtimInterval;
mWifiMode = base.mWifiMode;
mMaxRate = base.mMaxRate;
+ mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams;
}
public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
@@ -494,6 +517,10 @@
return mWifiMode;
}
+ public int getMaxNumberSpatialStreams() {
+ return mMaxNumberSpatialStreams;
+ }
+
public int getDtimInterval() {
return mDtimInterval;
}
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index 9fcf069..673fce5 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -31,7 +31,7 @@
public class InformationElementUtil {
private static final String TAG = "InformationElementUtil";
-
+ private static final boolean DBG = false;
public static InformationElement[] parseInformationElements(byte[] bytes) {
if (bytes == null) {
return new InformationElement[0];
@@ -421,7 +421,9 @@
// Make sure the byte array length is at least the fixed size
if (ie.bytes.length < HE_OPERATION_BASIC_LENGTH) {
- Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
+ if (DBG) {
+ Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
+ }
// Skipping parsing of the IE
return;
}
@@ -434,7 +436,9 @@
// Make sure the byte array length is at least fitting the known parameters
if (ie.bytes.length < expectedLen) {
- Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
+ if (DBG) {
+ Log.w(TAG, "Invalid HE_OPERATION len: " + ie.bytes.length);
+ }
// Skipping parsing of the IE
return;
}
@@ -461,6 +465,146 @@
}
}
+ /**
+ * HtCapabilities: represents the HT Capabilities IE
+ */
+ public static class HtCapabilities {
+ private int mMaxNumberSpatialStreams = 1;
+ private boolean mPresent = false;
+ /** Returns whether HT Capabilities IE is present */
+ public boolean isPresent() {
+ return mPresent;
+ }
+ /**
+ * Returns max number of spatial streams if HT Capabilities IE is found and parsed,
+ * or 1 otherwise
+ */
+ public int getMaxNumberSpatialStreams() {
+ return mMaxNumberSpatialStreams;
+ }
+
+ /** Parse HT Capabilities IE */
+ public void from(InformationElement ie) {
+ if (ie.id != InformationElement.EID_HT_CAPABILITIES) {
+ throw new IllegalArgumentException("Element id is not HT_CAPABILITIES: " + ie.id);
+ }
+ if (ie.bytes.length < 26) {
+ if (DBG) {
+ Log.w(TAG, "Invalid HtCapabilities len: " + ie.bytes.length);
+ }
+ return;
+ }
+ int stream1 = ie.bytes[3] & Constants.BYTE_MASK;
+ int stream2 = ie.bytes[4] & Constants.BYTE_MASK;
+ int stream3 = ie.bytes[5] & Constants.BYTE_MASK;
+ int stream4 = ie.bytes[6] & Constants.BYTE_MASK;
+ if (DBG) {
+ Log.d(TAG, "HT Rx MCS set4: " + Integer.toHexString(stream4));
+ Log.d(TAG, "HT Rx MCS set3: " + Integer.toHexString(stream3));
+ Log.d(TAG, "HT Rx MCS set2: " + Integer.toHexString(stream2));
+ Log.d(TAG, "HT Rx MCS set1: " + Integer.toHexString(stream1));
+ }
+ mMaxNumberSpatialStreams = (stream4 > 0) ? 4 :
+ ((stream3 > 0) ? 3 :
+ ((stream2 > 0) ? 2 : 1));
+ mPresent = true;
+ }
+ }
+
+ /**
+ * VhtCapabilities: represents the VHT Capabilities IE
+ */
+ public static class VhtCapabilities {
+ private int mMaxNumberSpatialStreams = 1;
+ private boolean mPresent = false;
+ /** Returns whether VHT Capabilities IE is present */
+ public boolean isPresent() {
+ return mPresent;
+ }
+ /**
+ * Returns max number of spatial streams if VHT Capabilities IE is found and parsed,
+ * or 1 otherwise
+ */
+ public int getMaxNumberSpatialStreams() {
+ return mMaxNumberSpatialStreams;
+ }
+ /** Parse VHT Capabilities IE */
+ public void from(InformationElement ie) {
+ if (ie.id != InformationElement.EID_VHT_CAPABILITIES) {
+ throw new IllegalArgumentException("Element id is not VHT_CAPABILITIES: " + ie.id);
+ }
+ if (ie.bytes.length < 12) {
+ if (DBG) {
+ Log.w(TAG, "Invalid VHT_CAPABILITIES len: " + ie.bytes.length);
+ }
+ return;
+ }
+ int mcsMap = ((ie.bytes[5] & Constants.BYTE_MASK) << 8)
+ + (ie.bytes[4] & Constants.BYTE_MASK);
+ mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
+ mPresent = true;
+ }
+ }
+
+ /**
+ * HeCapabilities: represents the HE Capabilities IE
+ */
+ public static class HeCapabilities {
+ private int mMaxNumberSpatialStreams = 1;
+ private boolean mPresent = false;
+ /** Returns whether HE Capabilities IE is present */
+ public boolean isPresent() {
+ return mPresent;
+ }
+ /**
+ * Returns max number of spatial streams if HE Capabilities IE is found and parsed,
+ * or 1 otherwise
+ */
+ public int getMaxNumberSpatialStreams() {
+ return mMaxNumberSpatialStreams;
+ }
+ /** Parse HE Capabilities IE */
+ public void from(InformationElement ie) {
+ if (ie.id != InformationElement.EID_EXTENSION_PRESENT
+ || ie.idExt != InformationElement.EID_EXT_HE_CAPABILITIES) {
+ throw new IllegalArgumentException("Element id is not HE_CAPABILITIES: " + ie.id);
+ }
+ if (ie.bytes.length < 21) {
+ if (DBG) {
+ Log.w(TAG, "Invalid HE_CAPABILITIES len: " + ie.bytes.length);
+ }
+ return;
+ }
+ int mcsMap = ((ie.bytes[18] & Constants.BYTE_MASK) << 8)
+ + (ie.bytes[17] & Constants.BYTE_MASK);
+ mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
+ mPresent = true;
+ }
+ }
+
+ private static int parseMaxNumberSpatialStreamsFromMcsMap(int mcsMap) {
+ int maxNumberSpatialStreams = 1;
+ for (int i = 8; i >= 1; --i) {
+ int streamMap = mcsMapToStreamMap(mcsMap, i);
+ // 3 means unsupported
+ if (streamMap != 3) {
+ maxNumberSpatialStreams = i;
+ break;
+ }
+ }
+ if (DBG) {
+ for (int i = 8; i >= 1; --i) {
+ int streamMap = mcsMapToStreamMap(mcsMap, i);
+ Log.d(TAG, "Rx MCS set " + i + " : " + streamMap);
+ }
+ }
+ return maxNumberSpatialStreams;
+ }
+
+ private static int mcsMapToStreamMap(int mcsMap, int i) {
+ return (mcsMap >> ((i - 1) * 2)) & 0x3;
+ }
+
public static class Interworking {
public NetworkDetail.Ant ant = null;
public boolean internet = false;
diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
index 41b9aab..9bd1290 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
@@ -1362,6 +1362,96 @@
assertEquals(5200, vhtOperation.getCenterFreq0());
assertEquals(0, vhtOperation.getCenterFreq1());
}
+ /**
+ * Verify that the expected max number of spatial stream is parsed correctly from
+ * HT capabilities IE
+ *
+ * HT capabilities IE Format:
+ * | HT Capability Information | A-MPDU Parameters | Supported MCS Set
+ * 2 1 16
+ * | HT Extended Capabilities | Transmit Beamforming Capabilities | ASEL Capabilities |
+ * 2 4 1
+ *
+ * Supported MCS Set Format:
+ * B0 B8 B16 B23
+ * | Rx MCS Bitmask 1SS | Rx MCS Bitmask 2SS | Rx MCS Bitmask 3SS | Rx MCS Bitmask 4SS
+ */
+ @Test
+ public void getMaxNumberSpatialStreamsWithHtCapabilitiesIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_HT_CAPABILITIES;
+ ie.bytes = new byte[]{(byte) 0xee, (byte) 0x01, (byte) 0x17, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00};
+ InformationElementUtil.HtCapabilities htCapabilities =
+ new InformationElementUtil.HtCapabilities();
+ htCapabilities.from(ie);
+ assertEquals(3, htCapabilities.getMaxNumberSpatialStreams());
+ assertEquals(true, htCapabilities.isPresent());
+ }
+
+ /**
+ * Verify that the expected max number of spatial stream is parsed correctly from
+ * VHT capabilities IE
+ */
+ @Test
+ public void getMaxNumberSpatialStreamsWithVhtCapabilitiesIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VHT_CAPABILITIES;
+ /**
+ * VHT Capabilities IE Format:
+ * | VHT capabilities Info | Supported VHT-MCS and NSS Set |
+ * 4 8
+ *
+ * Supported VHT-MCS set Format:
+ * B0 B2 B4 B6
+ * | Max MCS For 1SS | Max MCS For 2SS | Max MCS For 3SS | Max MCS For 4SS
+ * B8 B10 B12 B14
+ * | Max MCS For 5SS | Max MCS For 6SS | Max MCS For 7SS | Max MCS For 8SS
+ */
+ ie.bytes = new byte[]{(byte) 0x92, (byte) 0x01, (byte) 0x80, (byte) 0x33, (byte) 0xaa,
+ (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0xaa, (byte) 0xff, (byte) 0x00,
+ (byte) 0x00};
+ InformationElementUtil.VhtCapabilities vhtCapabilities =
+ new InformationElementUtil.VhtCapabilities();
+ vhtCapabilities.from(ie);
+ assertEquals(4, vhtCapabilities.getMaxNumberSpatialStreams());
+ assertEquals(true, vhtCapabilities.isPresent());
+ }
+
+ /**
+ * Verify that the expected max number of spatial stream is parsed correctly from
+ * HE capabilities IE
+ */
+ @Test
+ public void getMaxNumberSpatialStreamsWithHeCapabilitiesIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENSION_PRESENT;
+ ie.idExt = InformationElement.EID_EXT_HE_CAPABILITIES;
+ /**
+ * HE Capabilities IE Format:
+ * | HE MAC Capabilities Info | HE PHY Capabilities Info | Supported HE-MCS and NSS Set |
+ * 6 11 4
+ *
+ * Supported HE-MCS set Format:
+ * B0 B2 B4 B6
+ * | Max MCS For 1SS | Max MCS For 2SS | Max MCS For 3SS | Max MCS For 4SS
+ * B8 B10 B12 B14
+ * | Max MCS For 5SS | Max MCS For 6SS | Max MCS For 7SS | Max MCS For 8SS
+ */
+ ie.bytes = new byte[]{(byte) 0x09, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x40,
+ (byte) 0x04, (byte) 0x70, (byte) 0x0c, (byte) 0x80, (byte) 0x00, (byte) 0x07,
+ (byte) 0x80, (byte) 0x04, (byte) 0x00, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
+ (byte) 0xaa, (byte) 0x7f, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0x1c,
+ (byte) 0xc7, (byte) 0x71};
+ InformationElementUtil.HeCapabilities heCapabilities =
+ new InformationElementUtil.HeCapabilities();
+ heCapabilities.from(ie);
+ assertEquals(8, heCapabilities.getMaxNumberSpatialStreams());
+ assertEquals(true, heCapabilities.isPresent());
+ }
// TODO: SAE, OWN, SUITE_B
}