diff options
3 files changed, 285 insertions, 40 deletions
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java index edee2da1a..36ed8592e 100644 --- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java +++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java @@ -258,17 +258,28 @@ public class NetworkDetail { mANQPElements = null; //set up channel info mPrimaryFreq = freq; + int channelWidth = ScanResult.UNSPECIFIED; + int centerFreq0 = 0; + int centerFreq1 = 0; + + if (vhtOperation.isPresent()) { + channelWidth = vhtOperation.getChannelWidth(); + if (channelWidth != ScanResult.UNSPECIFIED) { + centerFreq0 = vhtOperation.getCenterFreq0(); + centerFreq1 = vhtOperation.getCenterFreq1(); + } + } - if (vhtOperation.isValid()) { - // 80 or 160 MHz - mChannelWidth = vhtOperation.getChannelWidth(); - mCenterfreq0 = vhtOperation.getCenterFreq0(); - mCenterfreq1 = vhtOperation.getCenterFreq1(); - } else { - mChannelWidth = htOperation.getChannelWidth(); - mCenterfreq0 = htOperation.getCenterFreq0(mPrimaryFreq); - mCenterfreq1 = 0; + if (channelWidth == ScanResult.UNSPECIFIED) { + //Either no vht, or vht shows BW is 40/20 MHz + if (htOperation.isPresent()) { + channelWidth = htOperation.getChannelWidth(); + centerFreq0 = htOperation.getCenterFreq0(mPrimaryFreq); + } } + mChannelWidth = channelWidth; + mCenterfreq0 = centerFreq0; + mCenterfreq1 = centerFreq1; // If trafficIndicationMap is not valid, mDtimPeriod will be negative if (trafficIndicationMap.isValid()) { @@ -287,8 +298,7 @@ public class NetworkDetail { maxRateA = supportedRates.mRates.get(supportedRates.mRates.size() - 1); mMaxRate = maxRateA > maxRateB ? maxRateA : maxRateB; mWifiMode = InformationElementUtil.WifiMode.determineMode(mPrimaryFreq, mMaxRate, - vhtOperation.isValid(), - iesFound.contains(ScanResult.InformationElement.EID_HT_OPERATION), + vhtOperation.isPresent(), htOperation.isPresent(), iesFound.contains(ScanResult.InformationElement.EID_ERP)); } else { mWifiMode = 0; @@ -303,9 +313,8 @@ public class NetworkDetail { + ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode) + ", Freq: " + mPrimaryFreq + ", mMaxRate: " + mMaxRate - + ", VHT: " + String.valueOf(vhtOperation.isValid()) - + ", HT: " + String.valueOf( - iesFound.contains(ScanResult.InformationElement.EID_HT_OPERATION)) + + ", VHT: " + String.valueOf(vhtOperation.isPresent()) + + ", HT: " + String.valueOf(htOperation.isPresent()) + ", ERP: " + String.valueOf( iesFound.contains(ScanResult.InformationElement.EID_ERP)) + ", SupportedRates: " + supportedRates.toString() diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index 3ed829d8c..61b600b97 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -151,73 +151,148 @@ public class InformationElementUtil { } public static class HtOperation { - public int secondChannelOffset = 0; + private static final int HT_OPERATION_IE_LEN = 22; + private boolean mPresent = false; + private int mSecondChannelOffset = 0; + /** + * returns if HT Operation IE present in the message. + */ + public boolean isPresent() { + return mPresent; + } + + /** + * Returns channel width if it is 20 or 40MHz + * Results will be invalid if channel width greater than 40MHz + * So caller should only call this method if VHT Operation IE is not present, + * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. + */ public int getChannelWidth() { - if (secondChannelOffset != 0) { - return 1; + if (mSecondChannelOffset != 0) { + return ScanResult.CHANNEL_WIDTH_40MHZ; } else { - return 0; + return ScanResult.CHANNEL_WIDTH_20MHZ; } } + /** + * Returns channel Center frequency (for 20/40 MHz channels only) + * Results will be invalid for larger channel width, + * So, caller should only call this method if VHT Operation IE is not present, + * or if VhtOperation.getChannelWidth() returns ScanResult.UNSPECIFIED. + */ public int getCenterFreq0(int primaryFrequency) { - //40 MHz - if (secondChannelOffset != 0) { - if (secondChannelOffset == 1) { + if (mSecondChannelOffset != 0) { + //40 MHz + if (mSecondChannelOffset == 1) { return primaryFrequency + 10; - } else if (secondChannelOffset == 3) { + } else if (mSecondChannelOffset == 3) { return primaryFrequency - 10; } else { - Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset); + Log.e("HtOperation", "Error on secondChannelOffset: " + mSecondChannelOffset); return 0; } } else { - return 0; + //20 MHz + return primaryFrequency; } } + /** + * Parse the HT Operation IE to read the fields of interest. + */ public void from(InformationElement ie) { if (ie.id != InformationElement.EID_HT_OPERATION) { throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); } - secondChannelOffset = ie.bytes[1] & 0x3; + if (ie.bytes.length < HT_OPERATION_IE_LEN) { + throw new IllegalArgumentException("Invalid HT_OPERATION len: " + ie.bytes.length); + } + mPresent = true; + mSecondChannelOffset = ie.bytes[1] & 0x3; } } public static class VhtOperation { - public int channelMode = 0; - public int centerFreqIndex1 = 0; - public int centerFreqIndex2 = 0; + private static final int VHT_OPERATION_IE_LEN = 5; + private boolean mPresent = false; + private int mChannelMode = 0; + private int mCenterFreqIndex1 = 0; + private int mCenterFreqIndex2 = 0; - public boolean isValid() { - return channelMode != 0; + /** + * returns if VHT Operation IE present in the message. + */ + public boolean isPresent() { + return mPresent; } + /** + * Returns channel width if it is above 40MHz, + * otherwise, returns {@link ScanResult.UNSPECIFIED} to indicate that + * channel width should be obtained from the HT Operation IE via + * HtOperation.getChannelWidth(). + */ public int getChannelWidth() { - return channelMode + 1; + if (mChannelMode == 0) { + // 20 or 40MHz + return ScanResult.UNSPECIFIED; + } else if (mCenterFreqIndex2 == 0) { + // No secondary channel + return ScanResult.CHANNEL_WIDTH_80MHZ; + } else if (Math.abs(mCenterFreqIndex2 - mCenterFreqIndex1) == 8) { + // Primary and secondary channels adjacent + return ScanResult.CHANNEL_WIDTH_160MHZ; + } else { + // Primary and secondary channels not adjacent + return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ; + } } + /** + * Returns center frequency of primary channel (if channel width greater than 40MHz), + * otherwise, it returns zero to indicate that center frequency should be obtained from + * the HT Operation IE via HtOperation.getCenterFreq0(). + */ public int getCenterFreq0() { - //convert channel index to frequency in MHz, channel 36 is 5180MHz - return (centerFreqIndex1 - 36) * 5 + 5180; + if (mCenterFreqIndex1 == 0 || mChannelMode == 0) { + return 0; + } else { + //convert channel index to frequency in MHz, channel 36 is 5180MHz + return (mCenterFreqIndex1 - 36) * 5 + 5180; + } } + /** + * Returns center frequency of secondary channel if exists (channel width greater than + * 40MHz), otherwise, it returns zero. + * Note that the secondary channel center frequency only applies to 80+80 or 160 MHz + * channels. + */ public int getCenterFreq1() { - if (channelMode > 1) { //160MHz - return (centerFreqIndex2 - 36) * 5 + 5180; - } else { + if (mCenterFreqIndex2 == 0 || mChannelMode == 0) { return 0; + } else { + //convert channel index to frequency in MHz, channel 36 is 5180MHz + return (mCenterFreqIndex2 - 36) * 5 + 5180; } } + /** + * Parse the VHT Operation IE to read the fields of interest. + */ public void from(InformationElement ie) { if (ie.id != InformationElement.EID_VHT_OPERATION) { throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); } - channelMode = ie.bytes[0] & Constants.BYTE_MASK; - centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; - centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; + if (ie.bytes.length < VHT_OPERATION_IE_LEN) { + throw new IllegalArgumentException("Invalid VHT_OPERATION len: " + ie.bytes.length); + } + mPresent = true; + mChannelMode = ie.bytes[0] & Constants.BYTE_MASK; + mCenterFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; + mCenterFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; } } diff --git a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java index f46dd915f..c11b300e5 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.ScanResult; import android.net.wifi.ScanResult.InformationElement; import androidx.test.filters.SmallTest; import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.hotspot2.NetworkDetail; +import com.android.server.wifi.util.InformationElementUtil.HtOperation; +import com.android.server.wifi.util.InformationElementUtil.VhtOperation; import org.junit.Test; @@ -1009,5 +1012,163 @@ public class InformationElementUtilTest extends WifiBaseTest { assertEquals(0x112233445566L, interworking.hessid); } + /** + * Verify that the expected HT Operation information element is parsed and retrieved from the + * list of IEs. + * + * @throws Exception + */ + @Test + public void getHtOperationElement() throws Exception { + final int primaryFreq = 2467; + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_HT_OPERATION; + /** + * HT Operation Format: + * | Primary Channel | HT Operation Info | Basic HT-MCS Set | + * 1 5 16 + * + * HT Operation Info Format (relevant parts only): + * + * B0 B1 B2 ----- + * | Secondary Channel Offset | STA Channel Width | Other | + */ + ie.bytes = new byte[22]; + ie.bytes[0] = (byte) 11; + ie.bytes[1] = (byte) 0x83; //Setting Secondary channel offset = 3 + // Remaining bytes are not relevant + + HtOperation htOperation = new HtOperation(); + htOperation.from(ie); + + assertTrue(htOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_40MHZ, htOperation.getChannelWidth()); + assertEquals(primaryFreq - 10, htOperation.getCenterFreq0(primaryFreq)); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 20/40 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement20_40Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x00, (byte) 0xF0, (byte) 0xF1, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.UNSPECIFIED, vhtOperation.getChannelWidth()); + assertEquals(0, vhtOperation.getCenterFreq0()); + assertEquals(0, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 80 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement80Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 36, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_80MHZ, vhtOperation.getChannelWidth()); + assertEquals(5180, vhtOperation.getCenterFreq0()); + assertEquals(0, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 160 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement160Mhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 44, (byte) 36, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_160MHZ, vhtOperation.getChannelWidth()); + assertEquals(5220, vhtOperation.getCenterFreq0()); + assertEquals(5180, vhtOperation.getCenterFreq1()); + } + + /** + * Verify that the expected VHT Operation information element is parsed and retrieved from the + * list of IEs. + * In this test case Channel BW is set to be 80+80 MHz + * + * @throws Exception + */ + @Test + public void getVhtOperationElement80PlusMhz() throws Exception { + InformationElement ie = new InformationElement(); + ie.id = InformationElement.EID_VHT_OPERATION; + /** + * VHT Operation Format: + * | VHT Operation Info | Basic HT-MCS Set | + * 3 2 + * + * VHT Operation Info Format: + * | Channel Width | Channel Center Freq Seg 0 | Channel Center Freq Seg 1 | + * 1 1 1 + */ + ie.bytes = new byte[]{(byte) 0x01, (byte) 54, (byte) 36, (byte) 0x00, (byte) 0x00}; + + VhtOperation vhtOperation = new VhtOperation(); + vhtOperation.from(ie); + + assertTrue(vhtOperation.isPresent()); + assertEquals(ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ, vhtOperation.getChannelWidth()); + assertEquals(5270, vhtOperation.getCenterFreq0()); + assertEquals(5180, vhtOperation.getCenterFreq1()); + } + // TODO: SAE, OWN, SUITE_B } |