diff options
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 } |