diff options
author | Sunil Ravi <sunilravi@google.com> | 2019-11-26 18:25:05 -0800 |
---|---|---|
committer | Sunil Ravi <sunilravi@google.com> | 2019-12-04 12:53:36 -0800 |
commit | 6eaf2cc4ac4454dc664a3b27386ebea73eb77c65 (patch) | |
tree | 7ffe30601a571e3a91ca6440bb0b07606249abf4 /service | |
parent | c3aa2712ce17a826335ba38577b37206e699f0cf (diff) |
wifi: MBO-OCE feature support (phase 2)
1. MBO-OCE IE parsing
2. Skip MBO APs which are not accepting new connections from
network selection.
Bug: 139474288
Test: atest com.android.server.wifi
Test: Manual
Change-Id: I7b8dd7540465adac07c1e55e43f0848c1b1af02b
Diffstat (limited to 'service')
4 files changed, 192 insertions, 5 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; + } + } + } } /** |