summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/java/com/android/server/wifi/MboOceConstants.java67
-rw-r--r--service/java/com/android/server/wifi/WifiNetworkSelector.java19
-rw-r--r--service/java/com/android/server/wifi/hotspot2/NetworkDetail.java21
-rw-r--r--service/java/com/android/server/wifi/util/InformationElementUtil.java90
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java38
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java70
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java116
7 files changed, 410 insertions, 11 deletions
diff --git a/service/java/com/android/server/wifi/MboOceConstants.java b/service/java/com/android/server/wifi/MboOceConstants.java
new file mode 100644
index 000000000..a7f5fefab
--- /dev/null
+++ b/service/java/com/android/server/wifi/MboOceConstants.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * MBO-OCE related constants
+ */
+public class MboOceConstants {
+
+ public static final int MBO_OCE_ATTRIBUTE_NOT_PRESENT = -1;
+
+ /** MBO-OCE attribute Ids */
+ public static final int MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION = 0x01;
+ public static final int MBO_OCE_AID_NON_PREFERRED_CHANNEL_REPORT = 0x02;
+ public static final int MBO_OCE_AID_CELLULAR_DATA_CAPABILITIES = 0x03;
+ public static final int MBO_OCE_AID_ASSOCIATION_DISALLOWED = 0x04;
+ public static final int MBO_OCE_AID_CELLULAR_DATA_CONNECTION_PREFERENCE = 0x05;
+ public static final int MBO_OCE_AID_TRANSITION_REASON_CODE = 0x06;
+ public static final int MBO_OCE_AID_TRANSITION_REJECTION_REASON_CODE = 0x07;
+ public static final int MBO_OCE_AID_ASSOCIATION_RETRY_DELAY = 0x08;
+ public static final int MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION = 0x65;
+ public static final int MBO_OCE_AID_RSSI_BASED_ASSOCIATION_REJECTION = 0x66;
+ public static final int MBO_OCE_AID_REDUCED_WAN_METRICS = 0x67;
+ public static final int MBO_OCE_AID_RNR_COMPLETENESS = 0x68;
+ public static final int MBO_OCE_AID_PROBE_SUPPRESSION_BSSIDS = 0x69;
+ public static final int MBO_OCE_AID_PROBE_SUPPRESSION_SSIDS = 0x6A;
+
+ @IntDef(prefix = { "MBO_OCE_AID_" }, value = {
+ MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION,
+ MBO_OCE_AID_NON_PREFERRED_CHANNEL_REPORT,
+ MBO_OCE_AID_CELLULAR_DATA_CAPABILITIES,
+ MBO_OCE_AID_ASSOCIATION_DISALLOWED,
+ MBO_OCE_AID_CELLULAR_DATA_CONNECTION_PREFERENCE,
+ MBO_OCE_AID_TRANSITION_REASON_CODE,
+ MBO_OCE_AID_TRANSITION_REJECTION_REASON_CODE,
+ MBO_OCE_AID_ASSOCIATION_RETRY_DELAY,
+ MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION,
+ MBO_OCE_AID_RSSI_BASED_ASSOCIATION_REJECTION,
+ MBO_OCE_AID_REDUCED_WAN_METRICS,
+ MBO_OCE_AID_RNR_COMPLETENESS,
+ MBO_OCE_AID_PROBE_SUPPRESSION_BSSIDS,
+ MBO_OCE_AID_PROBE_SUPPRESSION_SSIDS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MboOceAid{}
+
+ public static final int MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE = 0x40;
+}
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index c8384614a..433fd0b01 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -36,6 +36,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.proto.nano.WifiMetricsProto;
import com.android.server.wifi.util.InformationElementUtil.BssLoad;
import com.android.server.wifi.util.ScanResultUtil;
@@ -384,6 +385,7 @@ public class WifiNetworkSelector {
StringBuffer noValidSsid = new StringBuffer();
StringBuffer blacklistedBssid = new StringBuffer();
StringBuffer lowRssi = new StringBuffer();
+ StringBuffer mboAssociationDisallowedBssid = new StringBuffer();
boolean scanResultsHaveCurrentBssid = false;
for (ScanDetail scanDetail : scanDetails) {
@@ -414,6 +416,18 @@ public class WifiNetworkSelector {
continue;
}
+ // Skip BSS which is not accepting new connections.
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ if (networkDetail != null) {
+ if (networkDetail.getMboAssociationDisallowedReasonCode()
+ != MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT) {
+ mboAssociationDisallowedBssid.append(scanId).append("(")
+ .append(networkDetail.getMboAssociationDisallowedReasonCode())
+ .append(")").append(" / ");
+ continue;
+ }
+ }
+
validScanDetails.add(scanDetail);
}
@@ -440,6 +454,11 @@ public class WifiNetworkSelector {
localLog("Networks filtered out due to low signal strength: " + lowRssi);
}
+ if (mboAssociationDisallowedBssid.length() != 0) {
+ localLog("Networks filtered out due to mbo association disallowed indication: "
+ + mboAssociationDisallowedBssid);
+ }
+
return validScanDetails;
}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index b456ef486..743bc76ac 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -119,6 +119,13 @@ public class NetworkDetail {
private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
+ /*
+ * From Wi-Fi Alliance MBO-OCE Information element.
+ * mMboAssociationDisallowedReasonCode is the reason code for AP not accepting new connections
+ * and is set to -1 if association disallowed attribute is not present in the element.
+ */
+ private final int mMboAssociationDisallowedReasonCode;
+
public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements,
List<String> anqpLines, int freq) {
if (infoElements == null) {
@@ -279,6 +286,7 @@ public class NetworkDetail {
mInternet = interworking.internet;
mHSRelease = vsa.hsRelease;
mAnqpDomainID = vsa.anqpDomainID;
+ mMboAssociationDisallowedReasonCode = vsa.mboAssociationDisallowedReasonCode;
mAnqpOICount = roamingConsortium.anqpOICount;
mRoamingConsortiums = roamingConsortium.getRoamingConsortiums();
mExtendedCapabilities = extendedCapabilities;
@@ -356,14 +364,16 @@ public class NetworkDetail {
}
if (DBG) {
Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: "
- + mPrimaryFreq + " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: "
+ + mPrimaryFreq + " Centerfreq0: " + mCenterfreq0 + " Centerfreq1: "
+ mCenterfreq1 + (extendedCapabilities.is80211McRTTResponder()
? " Support RTT responder" : " Do not support RTT responder")
- + " mMaxNumberSpatialStreams: " + mMaxNumberSpatialStreams);
+ + " MaxNumberSpatialStreams: " + mMaxNumberSpatialStreams
+ + " MboAssociationDisallowedReasonCode: "
+ + mMboAssociationDisallowedReasonCode);
Log.v("WifiMode", mSSID
+ ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
+ ", Freq: " + mPrimaryFreq
- + ", mMaxRate: " + mMaxRate
+ + ", MaxRate: " + mMaxRate
+ ", HE: " + String.valueOf(heOperation.isPresent())
+ ", VHT: " + String.valueOf(vhtOperation.isPresent())
+ ", HT: " + String.valueOf(htOperation.isPresent())
@@ -406,6 +416,7 @@ public class NetworkDetail {
mWifiMode = base.mWifiMode;
mMaxRate = base.mMaxRate;
mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams;
+ mMboAssociationDisallowedReasonCode = base.mMboAssociationDisallowedReasonCode;
}
public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
@@ -609,4 +620,8 @@ public class NetworkDetail {
}
return sb.toString();
}
+
+ public int getMboAssociationDisallowedReasonCode() {
+ return mMboAssociationDisallowedReasonCode;
+ }
}
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index 14f6a9f3e..0aff20432 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -20,6 +20,7 @@ import android.net.wifi.ScanResult.InformationElement;
import android.util.Log;
import com.android.server.wifi.ByteBufferReader;
+import com.android.server.wifi.MboOceConstants;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.hotspot2.anqp.Constants;
@@ -693,13 +694,65 @@ public class InformationElementUtil {
public static class Vsa {
private static final int ANQP_DOMID_BIT = 0x04;
+ private static final int OUI_WFA_ALLIANCE = 0x506F9a;
+ private static final int OUI_TYPE_HS20 = 0x10;
+ private static final int OUI_TYPE_MBO_OCE = 0x16;
public NetworkDetail.HSRelease hsRelease = null;
public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP.
- public void from(InformationElement ie) {
+ public boolean IsMboCapable = false;
+ public boolean IsMboApCellularDataAware = false;
+ public boolean IsOceCapable = false;
+ public int mboAssociationDisallowedReasonCode =
+ MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT;
+
+ private void parseVsaMboOce(InformationElement ie) {
+ ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
+
+ // skip WFA OUI and type parsing as parseVsaMboOce() is called after identifying
+ // MBO-OCE OUI type.
+ data.getInt();
+
+ while (data.remaining() > 1) {
+ int attrId = data.get() & Constants.BYTE_MASK;
+ int attrLen = data.get() & Constants.BYTE_MASK;
+
+ if ((attrLen == 0) || (attrLen > data.remaining())) {
+ return;
+ }
+ byte[] attrBytes = new byte[attrLen];
+ data.get(attrBytes);
+ switch (attrId) {
+ case MboOceConstants.MBO_OCE_AID_MBO_AP_CAPABILITY_INDICATION:
+ IsMboCapable = true;
+ IsMboApCellularDataAware = (attrBytes[0]
+ & MboOceConstants.MBO_AP_CAP_IND_ATTR_CELL_DATA_AWARE) != 0;
+ break;
+ case MboOceConstants.MBO_OCE_AID_ASSOCIATION_DISALLOWED:
+ mboAssociationDisallowedReasonCode = attrBytes[0] & Constants.BYTE_MASK;
+ break;
+ case MboOceConstants.MBO_OCE_AID_OCE_AP_CAPABILITY_INDICATION:
+ IsOceCapable = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (DBG) {
+ Log.e(TAG, ":parseMboOce MBO: " + IsMboCapable + " cellDataAware: "
+ + IsMboApCellularDataAware + " AssocDisAllowRC: "
+ + mboAssociationDisallowedReasonCode + " :OCE: " + IsOceCapable);
+ }
+ }
+
+ private void parseVsaHs20(InformationElement ie) {
ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
- if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) {
+ if (ie.bytes.length >= 5) {
+ // skip WFA OUI and type parsing as parseVsaHs20() is called after identifying
+ // HS20 OUI type.
+ data.getInt();
+
int hsConf = data.get() & Constants.BYTE_MASK;
switch ((hsConf >> 4) & Constants.NIBBLE_MASK) {
case 0:
@@ -721,6 +774,39 @@ public class InformationElementUtil {
}
}
}
+
+ /**
+ * Parse the vendor specific information element to build
+ * InformationElemmentUtil.vsa object.
+ *
+ * @param ie -- Information Element
+ */
+ public void from(InformationElement ie) {
+ if (ie.bytes.length < 3) {
+ if (DBG) {
+ Log.w(TAG, "Invalid vendor specific element len: " + ie.bytes.length);
+ }
+ return;
+ }
+
+ int oui = (((ie.bytes[0] & Constants.BYTE_MASK) << 16)
+ | ((ie.bytes[1] & Constants.BYTE_MASK) << 8)
+ | ((ie.bytes[2] & Constants.BYTE_MASK)));
+
+ if (oui == OUI_WFA_ALLIANCE && ie.bytes.length >= 4) {
+ int ouiType = ie.bytes[3];
+ switch (ouiType) {
+ case OUI_TYPE_HS20:
+ parseVsaHs20(ie);
+ break;
+ case OUI_TYPE_MBO_OCE:
+ parseVsaMboOce(ie);
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 09bab3d50..bddf3f946 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -40,6 +40,7 @@ import android.util.LocalLog;
import androidx.test.filters.SmallTest;
import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.proto.nano.WifiMetricsProto;
import com.android.wifi.resources.R;
@@ -204,6 +205,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest {
@Spy private MockResources mResource = new MockResources();
@Mock private WifiInfo mWifiInfo;
@Mock private Clock mClock;
+ @Mock private NetworkDetail mNetworkDetail;
private ScoringParams mScoringParams;
private LocalLog mLocalLog;
private int mThresholdMinimumRssi2G;
@@ -1613,4 +1615,40 @@ public class WifiNetworkSelectorTest extends WifiBaseTest {
verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(throughputExpId,
WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, true, 2);
}
+
+ /**
+ * Test that network which are not accepting new connections(MBO
+ * association disallowed attribute in beacons/probe responses)
+ * are filtered out from network selection.
+ *
+ * NetworkDetail contain the parsed association disallowed
+ * reason code.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void filterMboApAdvertisingAssociationDisallowedAttr() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5180};
+ String[] caps = {"[WPA2-PSK][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G + 8};
+ int[] securities = {SECURITY_PSK};
+ // MBO-OCE IE with association disallowed attribute.
+ byte[][] iesByteStream = {{(byte) 0xdd, (byte) 0x0a,
+ (byte) 0x50, (byte) 0x6F, (byte) 0x9A, (byte) 0x16,
+ (byte) 0x01, (byte) 0x01, (byte) 0x40,
+ (byte) 0x04, (byte) 0x01, (byte) 0x03}};
+ HashSet<String> blacklist = new HashSet<String>();
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock, iesByteStream);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+ assertEquals("Expect null configuration", null, candidate);
+ assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
index 4b35edf6a..233d1339b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
@@ -34,6 +34,8 @@ import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.text.TextUtils;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.util.InformationElementUtil;
import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.ScanResultUtil;
@@ -88,10 +90,29 @@ public class WifiNetworkSelectorTestUtil {
String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities,
WifiConfigManager wifiConfigManager, Clock clock) {
List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock);
+
WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities);
- checkConsistencyOfScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
- prepareConfigStore(wifiConfigManager, savedConfigs);
- scanResultLinkConfiguration(wifiConfigManager, savedConfigs, scanDetails);
+
+ addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
+
+ return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
+ }
+
+ public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids,
+ String[] bssids, int[] freqs, String[] caps, int[] levels,
+ int[] securities, WifiConfigManager wifiConfigManager, Clock clock,
+ byte[][] iesByteStream) {
+
+ if (iesByteStream == null) {
+ throw new IllegalArgumentException("Null ies");
+ }
+
+ List<ScanDetail> scanDetails = buildScanDetailsWithNetworkDetails(ssids, bssids, freqs,
+ caps, levels, iesByteStream, clock);
+
+ WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities);
+
+ addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
}
@@ -130,13 +151,18 @@ public class WifiNetworkSelectorTestUtil {
savedConfigs[i].networkId = i;
}
- checkConsistencyOfScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
- prepareConfigStore(wifiConfigManager, savedConfigs);
- scanResultLinkConfiguration(wifiConfigManager, savedConfigs, scanDetails);
+ addWifiConfigAndLinkScanResult(wifiConfigManager, savedConfigs, scanDetails);
return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
}
+ private static void addWifiConfigAndLinkScanResult(WifiConfigManager wifiConfigManager,
+ WifiConfiguration[] configs, List<ScanDetail> scanDetails) {
+ checkConsistencyOfScanDetailsAndWifiConfigs(scanDetails, configs);
+ prepareConfigStore(wifiConfigManager, configs);
+ scanResultLinkConfiguration(wifiConfigManager, configs, scanDetails);
+ }
+
private static void checkConsistencyOfScanDetailsAndWifiConfigs(
List<ScanDetail> scanDetails,
WifiConfiguration[] savedConfigs) {
@@ -189,6 +215,38 @@ public class WifiNetworkSelectorTestUtil {
return scanDetailList;
}
+ /**
+ * Build a list of scanDetails along with network details based
+ * on the caller supplied network SSID, BSSID, frequency,
+ * capability, byte stream of IEs and RSSI level information.
+ *
+ * @param ssids an array of SSIDs
+ * @param bssids an array of BSSIDs
+ * @param freqs an array of the network's frequency
+ * @param caps an array of the network's capability
+ * @param levels an array of the network's RSSI levels
+ * @return the constructed list of ScanDetail
+ */
+ public static List<ScanDetail> buildScanDetailsWithNetworkDetails(String[] ssids,
+ String[] bssids, int[] freqs,
+ String[] caps, int[] levels, byte[][] iesByteStream, Clock clock) {
+ List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
+
+ long timeStamp = clock.getElapsedSinceBootMillis();
+ for (int index = 0; index < ssids.length; index++) {
+ byte[] ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssids[index]));
+ ScanResult.InformationElement[] ies =
+ InformationElementUtil.parseInformationElements(iesByteStream[index]);
+ NetworkDetail nd = new NetworkDetail(bssids[index], ies, new ArrayList<String>(),
+ freqs[index]);
+ ScanDetail scanDetail = new ScanDetail(nd, WifiSsid.createFromByteArray(ssid),
+ bssids[index], caps[index], levels[index], freqs[index], timeStamp,
+ ies, new ArrayList<String>(),
+ ScanResults.generateIERawDatafromScanResultIE(ies));
+ scanDetailList.add(scanDetail);
+ }
+ return scanDetailList;
+ }
/**
* Generate an array of {@link android.net.wifi.WifiConfiguration} based on the caller
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 9bd1290a8..729c09c14 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
@@ -26,6 +26,7 @@ import android.net.wifi.ScanResult.InformationElement;
import androidx.test.filters.SmallTest;
+import com.android.server.wifi.MboOceConstants;
import com.android.server.wifi.WifiBaseTest;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.util.InformationElementUtil.HeOperation;
@@ -1453,5 +1454,120 @@ public class InformationElementUtilTest extends WifiBaseTest {
assertEquals(true, heCapabilities.isPresent());
}
+ /**
+ * Verify that the expected MBO-OCE Vendor Specific information
+ * element is parsed and MBO AP Capability Indication is
+ * retrieved.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseMboOceIeWithApCapabilityIndicationAttr() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VSA;
+ /**
+ * Vendor Specific MBO-OCE IE Format:
+ * | OUI | OUI Type | MBO-OCE attributes |
+ * 3 1 Variable
+ *
+ * The Format of MBO Attribute:
+ * | Attribute ID | Attribute length | Attribute Body Field
+ * 1 1 Variable
+ *
+ * MBO AP capability indication attribute Body field:
+ * | MBO AP Capability Indication field values |
+ * 1
+ * | Reserved | Cellular Data aware | Reserved
+ * Bits: 0x80(MSB) 0x40 0x20-0x01(LSB)
+ */
+ ie.bytes = new byte[] { (byte) 0x50, (byte) 0x6F, (byte) 0x9A, (byte) 0x16,
+ (byte) 0x01, (byte) 0x01, (byte) 0x40};
+ InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa();
+ vsa.from(ie);
+ assertEquals(true, vsa.IsMboCapable);
+ assertEquals(true, vsa.IsMboApCellularDataAware);
+ assertEquals(MboOceConstants.MBO_OCE_ATTRIBUTE_NOT_PRESENT,
+ vsa.mboAssociationDisallowedReasonCode);
+ assertEquals(false, vsa.IsOceCapable);
+ }
+
+ /**
+ * Verify that the expected MBO-OCE Vendor Specific information
+ * element is parsed and MBO association disallowed reason code is
+ * retrieved.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseMboOceIeWithAssociationDisallowedAttr() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VSA;
+ /**
+ * Vendor Specific MBO-OCE IE Format:
+ * | OUI | OUI Type | MBO-OCE attributes |
+ * 3 1 Variable
+ *
+ * The Format of MBO Attribute:
+ * | Attribute ID | Attribute length | Attribute Body Field
+ * 1 1 Variable
+ *
+ * MBO AP capability indication attribute Body field:
+ * | MBO AP Capability Indication field values |
+ * 1
+ * | Reserved | Cellular Data aware | Reserved
+ * Bits: 0x80(MSB) 0x40 0x20-0x01(LSB)
+ *
+ * MBO association disallowed attribute Body field:
+ * | Reason code |
+ * 1
+ */
+ ie.bytes = new byte[] { (byte) 0x50, (byte) 0x6F, (byte) 0x9A, (byte) 0x16,
+ (byte) 0x01, (byte) 0x01, (byte) 0x40,
+ (byte) 0x04, (byte) 0x01, (byte) 0x03};
+ InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa();
+ vsa.from(ie);
+ assertEquals(0x03, vsa.mboAssociationDisallowedReasonCode);
+ }
+
+ /**
+ * Verify that the expected MBO-OCE Vendor Specific information
+ * element is parsed and OCE capability indication attribute is
+ * retrieved.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseMboOceIeWithOceCapabilityIndicationAttr() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VSA;
+ /**
+ * Vendor Specific MBO-OCE IE Format:
+ * | OUI | OUI Type | MBO-OCE attributes |
+ * 3 1 Variable
+ *
+ * The Format of MBO Attribute:
+ * | Attribute ID | Attribute length | Attribute Body Field
+ * 1 1 Variable
+ *
+ * MBO AP capability indication attribute Body field:
+ * | MBO AP Capability Indication field values |
+ * 1
+ * | Reserved | Cellular Data aware | Reserved
+ * Bits: 0x80(MSB) 0x40 0x20-0x01(LSB)
+ *
+ * OCE capability indication attribute Body field:
+ * | OCE Control field |
+ * 1
+ * | OCE ver | STA-CFON | 11b-only AP present | HLP Enabled | Non-OCE AP present | Rsvd |
+ * Bits: B0 B2 B3 B4 B5 B6 B7
+ */
+ ie.bytes = new byte[] { (byte) 0x50, (byte) 0x6F, (byte) 0x9A, (byte) 0x16,
+ (byte) 0x01, (byte) 0x01, (byte) 0x40,
+ (byte) 0x65, (byte) 0x01, (byte) 0x01};
+ InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa();
+ vsa.from(ie);
+ assertEquals(true, vsa.IsOceCapable);
+ }
+
// TODO: SAE, OWN, SUITE_B
}