1 package com.android.server.wifi.hotspot2; 2 3 import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; 4 import static com.android.server.wifi.hotspot2.anqp.Constants.NIBBLE_MASK; 5 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE; 6 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT; 7 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI; 8 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE; 9 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED; 10 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS; 11 12 import android.net.wifi.WifiConfiguration; 13 14 import com.android.server.wifi.hotspot2.anqp.Constants; 15 16 import java.nio.ByteBuffer; 17 import java.util.ArrayList; 18 import java.util.Calendar; 19 import java.util.Collection; 20 import java.util.LinkedList; 21 import java.util.List; 22 import java.util.TimeZone; 23 24 public abstract class Utils { 25 26 public static final long UNSET_TIME = -1; 27 28 private static final int EUI48Length = 6; 29 private static final int EUI64Length = 8; 30 private static final long EUI48Mask = 0xffffffffffffL; 31 private static final String[] PLMNText = {"org", "3gppnetwork", "mcc*", "mnc*", "wlan" }; 32 33 /* 34 * OpenRoaming defines the use of multiple RCOIs to facilitate the implementation of policies 35 * across the federation. The currently defined RCOIs are: 36 * OpenRoaming-Settled: BA-A2-D0-xx-xx 37 * OpenRoaming-Settlement-Free: 5A-03-BA -xx-xx 38 * Refer to "OpenRoaming Framework & Standard Arch v3.0.0". 39 */ 40 private static final long RCOI_OPEN_ROAMING_SETTLED_PREFIX = 0xBAA2D0; 41 private static final long RCOI_OPEN_ROAMING_FREE_PREFIX = 0x5A03BA; 42 splitDomain(String domain)43 public static List<String> splitDomain(String domain) { 44 45 if (domain.endsWith(".")) 46 domain = domain.substring(0, domain.length() - 1); 47 int at = domain.indexOf('@'); 48 if (at >= 0) 49 domain = domain.substring(at + 1); 50 51 String[] labels = domain.toLowerCase().split("\\."); 52 LinkedList<String> labelList = new LinkedList<String>(); 53 for (String label : labels) { 54 labelList.addFirst(label); 55 } 56 57 return labelList; 58 } 59 parseMac(String s)60 public static long parseMac(String s) { 61 if (s == null) { 62 throw new IllegalArgumentException("Null MAC adddress"); 63 } 64 long mac = 0; 65 int count = 0; 66 for (int n = 0; n < s.length(); n++) { 67 int nibble = Utils.fromHex(s.charAt(n), true); // Set lenient to not blow up on ':' 68 if (nibble >= 0) { // ... and use only legit hex. 69 mac = (mac << 4) | nibble; 70 count++; 71 } 72 } 73 if (count < 12 || (count&1) == 1) { 74 throw new IllegalArgumentException("Bad MAC address: '" + s + "'"); 75 } 76 return mac; 77 } 78 79 /** 80 * Convert from mac address as long to string in hex code, separated with colon. 81 * @param mac The Mac address as long value. 82 * @return String value of mac address. 83 */ macToString(long mac)84 public static String macToString(long mac) { 85 int len = (mac & ~EUI48Mask) != 0 ? EUI64Length : EUI48Length; 86 StringBuilder sb = new StringBuilder(); 87 boolean first = true; 88 for (int n = (len - 1) * Byte.SIZE; n >= 0; n -= Byte.SIZE) { 89 if (first) { 90 first = false; 91 } else { 92 sb.append(':'); 93 } 94 long b = (mac >>> n) & Constants.BYTE_MASK; 95 sb.append(b > 0xf ? Long.toHexString(b) : "0" + Long.toHexString(b)); 96 } 97 return sb.toString(); 98 } 99 getMccMnc(List<String> domain)100 public static String getMccMnc(List<String> domain) { 101 if (domain.size() != PLMNText.length) { 102 return null; 103 } 104 105 for (int n = 0; n < PLMNText.length; n++ ) { 106 String expect = PLMNText[n]; 107 int len = expect.endsWith("*") ? expect.length() - 1 : expect.length(); 108 if (!domain.get(n).regionMatches(0, expect, 0, len)) { 109 return null; 110 } 111 } 112 113 String prefix = domain.get(2).substring(3) + domain.get(3).substring(3); 114 for (int n = 0; n < prefix.length(); n++) { 115 char ch = prefix.charAt(n); 116 if (ch < '0' || ch > '9') { 117 return null; 118 } 119 } 120 return prefix; 121 } 122 roamingConsortiumsToString(long[] ois)123 public static String roamingConsortiumsToString(long[] ois) { 124 if (ois == null) { 125 return "null"; 126 } 127 List<Long> list = new ArrayList<Long>(ois.length); 128 for (long oi : ois) { 129 list.add(oi); 130 } 131 return roamingConsortiumsToString(list); 132 } 133 roamingConsortiumsToString(Collection<Long> ois)134 public static String roamingConsortiumsToString(Collection<Long> ois) { 135 StringBuilder sb = new StringBuilder(); 136 boolean first = true; 137 for (long oi : ois) { 138 if (first) { 139 first = false; 140 } else { 141 sb.append(", "); 142 } 143 if (Long.numberOfLeadingZeros(oi) > 40) { 144 sb.append(String.format("%06x", oi)); 145 } else { 146 sb.append(String.format("%010x", oi)); 147 } 148 } 149 return sb.toString(); 150 } 151 toUnicodeEscapedString(String s)152 public static String toUnicodeEscapedString(String s) { 153 StringBuilder sb = new StringBuilder(s.length()); 154 for (int n = 0; n < s.length(); n++) { 155 char ch = s.charAt(n); 156 if (ch>= ' ' && ch < 127) { 157 sb.append(ch); 158 } 159 else { 160 sb.append("\\u").append(String.format("%04x", (int)ch)); 161 } 162 } 163 return sb.toString(); 164 } 165 toHexString(byte[] data)166 public static String toHexString(byte[] data) { 167 if (data == null) { 168 return "null"; 169 } 170 StringBuilder sb = new StringBuilder(data.length * 3); 171 172 boolean first = true; 173 for (byte b : data) { 174 if (first) { 175 first = false; 176 } else { 177 sb.append(' '); 178 } 179 sb.append(String.format("%02x", b & BYTE_MASK)); 180 } 181 return sb.toString(); 182 } 183 toHex(byte[] octets)184 public static String toHex(byte[] octets) { 185 StringBuilder sb = new StringBuilder(octets.length * 2); 186 for (byte o : octets) { 187 sb.append(String.format("%02x", o & BYTE_MASK)); 188 } 189 return sb.toString(); 190 } 191 hexToBytes(String text)192 public static byte[] hexToBytes(String text) { 193 if ((text.length() & 1) == 1) { 194 throw new NumberFormatException("Odd length hex string: " + text.length()); 195 } 196 byte[] data = new byte[text.length() >> 1]; 197 int position = 0; 198 for (int n = 0; n < text.length(); n += 2) { 199 data[position] = 200 (byte) (((fromHex(text.charAt(n), false) & NIBBLE_MASK) << 4) | 201 (fromHex(text.charAt(n + 1), false) & NIBBLE_MASK)); 202 position++; 203 } 204 return data; 205 } 206 fromHex(char ch, boolean lenient)207 public static int fromHex(char ch, boolean lenient) throws NumberFormatException { 208 if (ch <= '9' && ch >= '0') { 209 return ch - '0'; 210 } else if (ch >= 'a' && ch <= 'f') { 211 return ch + 10 - 'a'; 212 } else if (ch <= 'F' && ch >= 'A') { 213 return ch + 10 - 'A'; 214 } else if (lenient) { 215 return -1; 216 } else { 217 throw new NumberFormatException("Bad hex-character: " + ch); 218 } 219 } 220 toAscii(int b)221 private static char toAscii(int b) { 222 return b >= ' ' && b < 0x7f ? (char) b : '.'; 223 } 224 isDecimal(String s)225 static boolean isDecimal(String s) { 226 for (int n = 0; n < s.length(); n++) { 227 char ch = s.charAt(n); 228 if (ch < '0' || ch > '9') { 229 return false; 230 } 231 } 232 return true; 233 } 234 compare(Comparable<T> c1, T c2)235 public static <T extends Comparable> int compare(Comparable<T> c1, T c2) { 236 if (c1 == null) { 237 return c2 == null ? 0 : -1; 238 } 239 else if (c2 == null) { 240 return 1; 241 } 242 else { 243 return c1.compareTo(c2); 244 } 245 } 246 bytesToBingoCard(ByteBuffer data, int len)247 public static String bytesToBingoCard(ByteBuffer data, int len) { 248 ByteBuffer dup = data.duplicate(); 249 dup.limit(dup.position() + len); 250 return bytesToBingoCard(dup); 251 } 252 bytesToBingoCard(ByteBuffer data)253 public static String bytesToBingoCard(ByteBuffer data) { 254 ByteBuffer dup = data.duplicate(); 255 StringBuilder sbx = new StringBuilder(); 256 while (dup.hasRemaining()) { 257 sbx.append(String.format("%02x ", dup.get() & BYTE_MASK)); 258 } 259 dup = data.duplicate(); 260 sbx.append(' '); 261 while (dup.hasRemaining()) { 262 sbx.append(String.format("%c", toAscii(dup.get() & BYTE_MASK))); 263 } 264 return sbx.toString(); 265 } 266 toHMS(long millis)267 public static String toHMS(long millis) { 268 long time = millis >= 0 ? millis : -millis; 269 long tmp = time / 1000L; 270 long ms = time - tmp * 1000L; 271 272 time = tmp; 273 tmp /= 60L; 274 long s = time - tmp * 60L; 275 276 time = tmp; 277 tmp /= 60L; 278 long m = time - tmp * 60L; 279 280 return String.format("%s%d:%02d:%02d.%03d", millis < 0 ? "-" : "", tmp, m, s, ms); 281 } 282 toUTCString(long ms)283 public static String toUTCString(long ms) { 284 if (ms < 0) { 285 return "unset"; 286 } 287 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 288 c.setTimeInMillis(ms); 289 return String.format("%4d/%02d/%02d %2d:%02d:%02dZ", 290 c.get(Calendar.YEAR), 291 c.get(Calendar.MONTH) + 1, 292 c.get(Calendar.DAY_OF_MONTH), 293 c.get(Calendar.HOUR_OF_DAY), 294 c.get(Calendar.MINUTE), 295 c.get(Calendar.SECOND)); 296 } 297 unquote(String s)298 public static String unquote(String s) { 299 if (s == null) { 300 return null; 301 } 302 else if (s.length() > 1 && s.startsWith("\"") && s.endsWith("\"")) { 303 return s.substring(1, s.length()-1); 304 } 305 else { 306 return s; 307 } 308 } 309 310 /* 311 * Gets the first three octets from the RCOI which has 3 or 5 octets. 312 */ getRcoiPrefix(long rcoi)313 private static long getRcoiPrefix(long rcoi) { 314 rcoi &= 0xff_ffff_ffffL; 315 if ((rcoi & 0xff_ff00_0000L) != 0) { 316 // This is a 5-octet RCOI, pick the first 3 octets. 317 rcoi >>= 16; 318 } 319 return rcoi; 320 } 321 322 /** 323 * Checks whether the given RCOI is a free Openroaming RCOI. 324 */ isFreeOpenRoaming(long rcoi)325 public static boolean isFreeOpenRoaming(long rcoi) { 326 // There are more than 5 octets in the rcoi 327 if (Long.numberOfLeadingZeros(rcoi) < 24) return false; 328 return getRcoiPrefix(rcoi) == RCOI_OPEN_ROAMING_FREE_PREFIX; 329 } 330 331 /** 332 * Checks whether there is a free OpenRoaming RCOI. 333 */ containsFreeOpenRoaming(long[] rcois)334 public static boolean containsFreeOpenRoaming(long[] rcois) { 335 for (int i = 0; i < rcois.length; i++) { 336 if (isFreeOpenRoaming(rcois[i])) return true; 337 } 338 return false; 339 } 340 341 /** 342 * Checks whether the given RCOI is a settled OpenRoaming RCOI. 343 */ isSettledOpenRoaming(long rcoi)344 public static boolean isSettledOpenRoaming(long rcoi) { 345 // There are more than 5 octets in the rcoi 346 if (Long.numberOfLeadingZeros(rcoi) < 24) return false; 347 return getRcoiPrefix(rcoi) == RCOI_OPEN_ROAMING_SETTLED_PREFIX; 348 } 349 350 /** 351 * Checks whether there is a settled OpenRoaming RCOI. 352 */ containsSettledOpenRoaming(long[] rcois)353 public static boolean containsSettledOpenRoaming(long[] rcois) { 354 for (int i = 0; i < rcois.length; i++) { 355 if (isSettledOpenRoaming(rcois[i])) return true; 356 } 357 return false; 358 } 359 360 /** 361 * Gets the roaming type of the given WifiConfiguration. 362 * 363 * @param connectConfig the passpoint WifiConfuration which has been connected or is being 364 * connecting. 365 * @return ROAMING_NOT_PASSPOINT if the input WifiConfiguration is not for passpoint. 366 * ROAMING_NONE if it is a home network. 367 * ROAMING_UNKNOWN if there is no selected RCOI which is set during connection. 368 * ROAMING_OPENROAMING_FREE if it is a free OpenRoaming network. 369 * ROAMING_OPENROAMING_SETTLED if it is a settled OpenRoaming network. 370 */ getRoamingType(WifiConfiguration connectConfig)371 public static int getRoamingType(WifiConfiguration connectConfig) { 372 if (!connectConfig.isPasspoint()) { 373 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT; 374 } 375 if (connectConfig.isHomeProviderNetwork) { 376 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE; 377 } 378 379 long selectedRcoi = connectConfig.enterpriseConfig.getSelectedRcoi(); 380 if (isFreeOpenRoaming(selectedRcoi)) { 381 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE; 382 } 383 if (isSettledOpenRoaming(selectedRcoi)) { 384 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED; 385 } 386 if (selectedRcoi != 0L) { 387 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS; 388 } 389 390 return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI; 391 } 392 } 393