InformationElementUtil.java revision 9f743918a412fec9ad5a0386fbf6cf0361313f58
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi.util; 18 19import android.net.wifi.ScanResult.InformationElement; 20import android.util.Log; 21 22import com.android.server.wifi.anqp.Constants; 23import com.android.server.wifi.anqp.VenueNameElement; 24import com.android.server.wifi.hotspot2.NetworkDetail; 25 26import java.net.ProtocolException; 27import java.nio.ByteBuffer; 28import java.nio.ByteOrder; 29import java.util.ArrayList; 30 31import static com.android.server.wifi.anqp.Constants.getInteger; 32 33public class InformationElementUtil { 34 35 public static InformationElement[] parseInformationElements(byte[] bytes) { 36 ByteBuffer data = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 37 38 ArrayList<InformationElement> infoElements = new ArrayList<>(); 39 boolean found_ssid = false; 40 while (data.remaining() > 1) { 41 int eid = data.get() & Constants.BYTE_MASK; 42 int elementLength = data.get() & Constants.BYTE_MASK; 43 44 if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID && 45 found_ssid)) { 46 break; 47 } 48 if(eid == InformationElement.EID_SSID) { 49 found_ssid = true; 50 } 51 52 InformationElement ie = new InformationElement(); 53 ie.id = eid; 54 ie.bytes = new byte[elementLength]; 55 data.get(ie.bytes); 56 infoElements.add(ie); 57 } 58 return infoElements.toArray(new InformationElement[infoElements.size()]); 59 } 60 61 62 public static class BssLoad { 63 public int stationCount = 0; 64 public int channelUtilization = 0; 65 public int capacity = 0; 66 67 public void from(InformationElement ie) { 68 if(ie.id != InformationElement.EID_BSS_LOAD) { 69 throw new IllegalArgumentException("Element id is not BSS_LOAD, : " + ie.id); 70 } 71 if (ie.bytes.length != 5) { 72 throw new IllegalArgumentException("BSS Load element length is not 5: " + 73 ie.bytes.length); 74 } 75 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 76 stationCount = data.getShort() & Constants.SHORT_MASK; 77 channelUtilization = data.get() & Constants.BYTE_MASK; 78 capacity = data.getShort() & Constants.SHORT_MASK; 79 } 80 } 81 82 public static class HtOperation { 83 public int secondChannelOffset = 0; 84 85 public int getChannelWidth() { 86 if(secondChannelOffset != 0) { 87 return 1; 88 } 89 else { 90 return 0; 91 } 92 } 93 94 public int getCenterFreq0(int primaryFrequency) { 95 //40 MHz 96 if (secondChannelOffset != 0) { 97 if (secondChannelOffset == 1) { 98 return primaryFrequency + 10; 99 } else if (secondChannelOffset == 3) { 100 return primaryFrequency - 10; 101 } else { 102 Log.e("HtOperation", "Error on secondChannelOffset: " + secondChannelOffset); 103 return 0; 104 } 105 } else { 106 return 0; 107 } 108 } 109 110 public void from(InformationElement ie) { 111 if(ie.id != InformationElement.EID_HT_OPERATION) { 112 throw new IllegalArgumentException("Element id is not HT_OPERATION, : " + ie.id); 113 } 114 secondChannelOffset = ie.bytes[1] & 0x3; 115 } 116 } 117 118 public static class VhtOperation { 119 public int channelMode = 0; 120 public int centerFreqIndex1 = 0; 121 public int centerFreqIndex2 = 0; 122 123 public boolean isValid() { 124 return channelMode != 0; 125 } 126 127 public int getChannelWidth() { 128 return channelMode + 1; 129 } 130 131 public int getCenterFreq0() { 132 //convert channel index to frequency in MHz, channel 36 is 5180MHz 133 return (centerFreqIndex1 - 36) * 5 + 5180; 134 } 135 136 public int getCenterFreq1() { 137 if(channelMode > 1) { //160MHz 138 return (centerFreqIndex2 - 36) * 5 + 5180; 139 } else { 140 return 0; 141 } 142 } 143 144 public void from(InformationElement ie) { 145 if(ie.id != InformationElement.EID_VHT_OPERATION) { 146 throw new IllegalArgumentException("Element id is not VHT_OPERATION, : " + ie.id); 147 } 148 channelMode = ie.bytes[0] & Constants.BYTE_MASK; 149 centerFreqIndex1 = ie.bytes[1] & Constants.BYTE_MASK; 150 centerFreqIndex2 = ie.bytes[2] & Constants.BYTE_MASK; 151 } 152 } 153 154 public static class Interworking { 155 public NetworkDetail.Ant ant = null; 156 public boolean internet = false; 157 public VenueNameElement.VenueGroup venueGroup = null; 158 public VenueNameElement.VenueType venueType = null; 159 public long hessid = 0L; 160 161 public void from(InformationElement ie) { 162 if(ie.id != InformationElement.EID_INTERWORKING) { 163 throw new IllegalArgumentException("Element id is not INTERWORKING, : " + ie.id); 164 } 165 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 166 int anOptions = data.get() & Constants.BYTE_MASK; 167 ant = NetworkDetail.Ant.values()[anOptions & 0x0f]; 168 internet = (anOptions & 0x10) != 0; 169 // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID 170 if (ie.bytes.length == 3 || ie.bytes.length == 9) { 171 try { 172 ByteBuffer vinfo = data.duplicate(); 173 vinfo.limit(vinfo.position() + 2); 174 VenueNameElement vne = new VenueNameElement( 175 Constants.ANQPElementType.ANQPVenueName, vinfo); 176 venueGroup = vne.getGroup(); 177 venueType = vne.getType(); 178 } catch (ProtocolException pe) { 179 /*Cannot happen*/ 180 } 181 } else if (ie.bytes.length != 1 && ie.bytes.length != 7) { 182 throw new IllegalArgumentException("Bad Interworking element length: " + 183 ie.bytes.length); 184 } 185 if (ie.bytes.length == 7 || ie.bytes.length == 9) { 186 hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6); 187 } 188 } 189 } 190 191 public static class RoamingConsortium { 192 public int anqpOICount = 0; 193 public long[] roamingConsortiums = null; 194 195 public void from(InformationElement ie) { 196 if(ie.id != InformationElement.EID_ROAMING_CONSORTIUM) { 197 throw new IllegalArgumentException("Element id is not ROAMING_CONSORTIUM, : " + 198 ie.id); 199 } 200 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 201 anqpOICount = data.get() & Constants.BYTE_MASK; 202 203 int oi12Length = data.get() & Constants.BYTE_MASK; 204 int oi1Length = oi12Length & Constants.NIBBLE_MASK; 205 int oi2Length = (oi12Length >>> 4) & Constants.NIBBLE_MASK; 206 int oi3Length = ie.bytes.length - 2 - oi1Length - oi2Length; 207 int oiCount = 0; 208 if (oi1Length > 0) { 209 oiCount++; 210 if (oi2Length > 0) { 211 oiCount++; 212 if (oi3Length > 0) { 213 oiCount++; 214 } 215 } 216 } 217 roamingConsortiums = new long[oiCount]; 218 if (oi1Length > 0 && roamingConsortiums.length > 0) { 219 roamingConsortiums[0] = 220 getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length); 221 } 222 if (oi2Length > 0 && roamingConsortiums.length > 1) { 223 roamingConsortiums[1] = 224 getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length); 225 } 226 if (oi3Length > 0 && roamingConsortiums.length > 2) { 227 roamingConsortiums[2] = 228 getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length); 229 } 230 } 231 } 232 233 public static class Vsa { 234 private static final int ANQP_DOMID_BIT = 0x04; 235 236 public NetworkDetail.HSRelease hsRelease = null; 237 public int anqpDomainID = 0; // No domain ID treated the same as a 0; unique info per AP. 238 239 public void from(InformationElement ie) { 240 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 241 if (ie.bytes.length >= 5 && data.getInt() == Constants.HS20_FRAME_PREFIX) { 242 int hsConf = data.get() & Constants.BYTE_MASK; 243 switch ((hsConf >> 4) & Constants.NIBBLE_MASK) { 244 case 0: 245 hsRelease = NetworkDetail.HSRelease.R1; 246 break; 247 case 1: 248 hsRelease = NetworkDetail.HSRelease.R2; 249 break; 250 default: 251 hsRelease = NetworkDetail.HSRelease.Unknown; 252 break; 253 } 254 if ((hsConf & ANQP_DOMID_BIT) != 0) { 255 if (ie.bytes.length < 7) { 256 throw new IllegalArgumentException( 257 "HS20 indication element too short: " + ie.bytes.length); 258 } 259 anqpDomainID = data.getShort() & Constants.SHORT_MASK; 260 } 261 } 262 } 263 } 264 265 public static class ExtendedCapabilities { 266 private static final int RTT_RESP_ENABLE_BIT = 70; 267 private static final long SSID_UTF8_BIT = 0x0001000000000000L; 268 269 public Long extendedCapabilities = null; 270 public boolean is80211McRTTResponder = false; 271 272 public ExtendedCapabilities() { 273 } 274 275 public ExtendedCapabilities(ExtendedCapabilities other) { 276 extendedCapabilities = other.extendedCapabilities; 277 is80211McRTTResponder = other.is80211McRTTResponder; 278 } 279 280 public boolean isStrictUtf8() { 281 return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0; 282 } 283 284 public void from(InformationElement ie) { 285 ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN); 286 extendedCapabilities = 287 Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length); 288 289 int index = RTT_RESP_ENABLE_BIT / 8; 290 byte offset = RTT_RESP_ENABLE_BIT % 8; 291 if(ie.bytes.length < index + 1) { 292 is80211McRTTResponder = false; 293 } else { 294 is80211McRTTResponder = (ie.bytes[index] & ((byte)0x1 << offset)) != 0; 295 } 296 } 297 } 298} 299