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 6 import android.net.wifi.EAPConstants; 7 8 import com.android.server.wifi.IMSIParameter; 9 import com.android.server.wifi.hotspot2.anqp.Constants; 10 11 import java.nio.ByteBuffer; 12 import java.util.ArrayList; 13 import java.util.Calendar; 14 import java.util.Collection; 15 import java.util.LinkedList; 16 import java.util.List; 17 import java.util.TimeZone; 18 19 public abstract class Utils { 20 21 public static final long UNSET_TIME = -1; 22 23 private static final int EUI48Length = 6; 24 private static final int EUI64Length = 8; 25 private static final long EUI48Mask = 0xffffffffffffL; 26 private static final String[] PLMNText = {"org", "3gppnetwork", "mcc*", "mnc*", "wlan" }; 27 hs2LogTag(Class c)28 public static String hs2LogTag(Class c) { 29 return "HS20"; 30 } 31 splitDomain(String domain)32 public static List<String> splitDomain(String domain) { 33 34 if (domain.endsWith(".")) 35 domain = domain.substring(0, domain.length() - 1); 36 int at = domain.indexOf('@'); 37 if (at >= 0) 38 domain = domain.substring(at + 1); 39 40 String[] labels = domain.toLowerCase().split("\\."); 41 LinkedList<String> labelList = new LinkedList<String>(); 42 for (String label : labels) { 43 labelList.addFirst(label); 44 } 45 46 return labelList; 47 } 48 parseMac(String s)49 public static long parseMac(String s) { 50 if (s == null) { 51 throw new IllegalArgumentException("Null MAC adddress"); 52 } 53 long mac = 0; 54 int count = 0; 55 for (int n = 0; n < s.length(); n++) { 56 int nibble = Utils.fromHex(s.charAt(n), true); // Set lenient to not blow up on ':' 57 if (nibble >= 0) { // ... and use only legit hex. 58 mac = (mac << 4) | nibble; 59 count++; 60 } 61 } 62 if (count < 12 || (count&1) == 1) { 63 throw new IllegalArgumentException("Bad MAC address: '" + s + "'"); 64 } 65 return mac; 66 } 67 macToString(long mac)68 public static String macToString(long mac) { 69 int len = (mac & ~EUI48Mask) != 0 ? EUI64Length : EUI48Length; 70 StringBuilder sb = new StringBuilder(); 71 boolean first = true; 72 for (int n = (len - 1)*Byte.SIZE; n >= 0; n -= Byte.SIZE) { 73 if (first) { 74 first = false; 75 } 76 else { 77 sb.append(':'); 78 } 79 sb.append(String.format("%02x", (mac >>> n) & Constants.BYTE_MASK)); 80 } 81 return sb.toString(); 82 } 83 getMccMnc(List<String> domain)84 public static String getMccMnc(List<String> domain) { 85 if (domain.size() != PLMNText.length) { 86 return null; 87 } 88 89 for (int n = 0; n < PLMNText.length; n++ ) { 90 String expect = PLMNText[n]; 91 int len = expect.endsWith("*") ? expect.length() - 1 : expect.length(); 92 if (!domain.get(n).regionMatches(0, expect, 0, len)) { 93 return null; 94 } 95 } 96 97 String prefix = domain.get(2).substring(3) + domain.get(3).substring(3); 98 for (int n = 0; n < prefix.length(); n++) { 99 char ch = prefix.charAt(n); 100 if (ch < '0' || ch > '9') { 101 return null; 102 } 103 } 104 return prefix; 105 } 106 107 /** 108 * Creates a realm that consists of mcc and mnc. 109 * 110 * @param mccmnc MCC(Mobile Country Code) and MNC (Mobile Network Code) 111 * @return a realm that has the MCC and MNC as format (wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org). 112 */ getRealmForMccMnc(String mccmnc)113 public static String getRealmForMccMnc(String mccmnc) { 114 // The length of mccmnc is 5 or 6. 115 if (mccmnc == null || (mccmnc.length() != (IMSIParameter.MCC_MNC_LENGTH - 1) 116 && mccmnc.length() != IMSIParameter.MCC_MNC_LENGTH)) { 117 return null; 118 } 119 120 // Please refer to 3GPP TS 23.003 for the definition of Home network realm when making 121 // the home network realm using mcc/mnc. 122 // 1. Take first 5 or 6 digits, depending on whether a 2 or 3 digit MNC used and separate 123 // them into MCC and MNC; if the MNC is 2 digits then a zero shall be added at the 124 // beginning. 125 // 2. Create the wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org domain name. 126 String mcc = mccmnc.substring(0, 3); 127 String mnc = mccmnc.substring(3); 128 if (mnc.length() == 2) { 129 mnc = "0" + mnc; 130 } 131 return String.format("wlan.mnc%s.mcc%s.3gppnetwork.org", mnc, mcc); 132 } 133 134 /** 135 * Check if the eapMethod is for EAP-Methods that carrier supports. 136 * 137 * @param eapMethod eap method to check 138 * @return {@code true} if the provided {@code eapMethod} belongs to the 139 * EAP-Methods(EAP-SIM/AKA/AKA'), {@code false} otherwise. 140 */ isCarrierEapMethod(int eapMethod)141 public static boolean isCarrierEapMethod(int eapMethod) { 142 return eapMethod == EAPConstants.EAP_SIM 143 || eapMethod == EAPConstants.EAP_AKA 144 || eapMethod == EAPConstants.EAP_AKA_PRIME; 145 } 146 roamingConsortiumsToString(long[] ois)147 public static String roamingConsortiumsToString(long[] ois) { 148 if (ois == null) { 149 return "null"; 150 } 151 List<Long> list = new ArrayList<Long>(ois.length); 152 for (long oi : ois) { 153 list.add(oi); 154 } 155 return roamingConsortiumsToString(list); 156 } 157 roamingConsortiumsToString(Collection<Long> ois)158 public static String roamingConsortiumsToString(Collection<Long> ois) { 159 StringBuilder sb = new StringBuilder(); 160 boolean first = true; 161 for (long oi : ois) { 162 if (first) { 163 first = false; 164 } else { 165 sb.append(", "); 166 } 167 if (Long.numberOfLeadingZeros(oi) > 40) { 168 sb.append(String.format("%06x", oi)); 169 } else { 170 sb.append(String.format("%010x", oi)); 171 } 172 } 173 return sb.toString(); 174 } 175 toUnicodeEscapedString(String s)176 public static String toUnicodeEscapedString(String s) { 177 StringBuilder sb = new StringBuilder(s.length()); 178 for (int n = 0; n < s.length(); n++) { 179 char ch = s.charAt(n); 180 if (ch>= ' ' && ch < 127) { 181 sb.append(ch); 182 } 183 else { 184 sb.append("\\u").append(String.format("%04x", (int)ch)); 185 } 186 } 187 return sb.toString(); 188 } 189 toHexString(byte[] data)190 public static String toHexString(byte[] data) { 191 if (data == null) { 192 return "null"; 193 } 194 StringBuilder sb = new StringBuilder(data.length * 3); 195 196 boolean first = true; 197 for (byte b : data) { 198 if (first) { 199 first = false; 200 } else { 201 sb.append(' '); 202 } 203 sb.append(String.format("%02x", b & BYTE_MASK)); 204 } 205 return sb.toString(); 206 } 207 toHex(byte[] octets)208 public static String toHex(byte[] octets) { 209 StringBuilder sb = new StringBuilder(octets.length * 2); 210 for (byte o : octets) { 211 sb.append(String.format("%02x", o & BYTE_MASK)); 212 } 213 return sb.toString(); 214 } 215 hexToBytes(String text)216 public static byte[] hexToBytes(String text) { 217 if ((text.length() & 1) == 1) { 218 throw new NumberFormatException("Odd length hex string: " + text.length()); 219 } 220 byte[] data = new byte[text.length() >> 1]; 221 int position = 0; 222 for (int n = 0; n < text.length(); n += 2) { 223 data[position] = 224 (byte) (((fromHex(text.charAt(n), false) & NIBBLE_MASK) << 4) | 225 (fromHex(text.charAt(n + 1), false) & NIBBLE_MASK)); 226 position++; 227 } 228 return data; 229 } 230 fromHex(char ch, boolean lenient)231 public static int fromHex(char ch, boolean lenient) throws NumberFormatException { 232 if (ch <= '9' && ch >= '0') { 233 return ch - '0'; 234 } else if (ch >= 'a' && ch <= 'f') { 235 return ch + 10 - 'a'; 236 } else if (ch <= 'F' && ch >= 'A') { 237 return ch + 10 - 'A'; 238 } else if (lenient) { 239 return -1; 240 } else { 241 throw new NumberFormatException("Bad hex-character: " + ch); 242 } 243 } 244 toAscii(int b)245 private static char toAscii(int b) { 246 return b >= ' ' && b < 0x7f ? (char) b : '.'; 247 } 248 isDecimal(String s)249 static boolean isDecimal(String s) { 250 for (int n = 0; n < s.length(); n++) { 251 char ch = s.charAt(n); 252 if (ch < '0' || ch > '9') { 253 return false; 254 } 255 } 256 return true; 257 } 258 compare(Comparable<T> c1, T c2)259 public static <T extends Comparable> int compare(Comparable<T> c1, T c2) { 260 if (c1 == null) { 261 return c2 == null ? 0 : -1; 262 } 263 else if (c2 == null) { 264 return 1; 265 } 266 else { 267 return c1.compareTo(c2); 268 } 269 } 270 bytesToBingoCard(ByteBuffer data, int len)271 public static String bytesToBingoCard(ByteBuffer data, int len) { 272 ByteBuffer dup = data.duplicate(); 273 dup.limit(dup.position() + len); 274 return bytesToBingoCard(dup); 275 } 276 bytesToBingoCard(ByteBuffer data)277 public static String bytesToBingoCard(ByteBuffer data) { 278 ByteBuffer dup = data.duplicate(); 279 StringBuilder sbx = new StringBuilder(); 280 while (dup.hasRemaining()) { 281 sbx.append(String.format("%02x ", dup.get() & BYTE_MASK)); 282 } 283 dup = data.duplicate(); 284 sbx.append(' '); 285 while (dup.hasRemaining()) { 286 sbx.append(String.format("%c", toAscii(dup.get() & BYTE_MASK))); 287 } 288 return sbx.toString(); 289 } 290 toHMS(long millis)291 public static String toHMS(long millis) { 292 long time = millis >= 0 ? millis : -millis; 293 long tmp = time / 1000L; 294 long ms = time - tmp * 1000L; 295 296 time = tmp; 297 tmp /= 60L; 298 long s = time - tmp * 60L; 299 300 time = tmp; 301 tmp /= 60L; 302 long m = time - tmp * 60L; 303 304 return String.format("%s%d:%02d:%02d.%03d", millis < 0 ? "-" : "", tmp, m, s, ms); 305 } 306 toUTCString(long ms)307 public static String toUTCString(long ms) { 308 if (ms < 0) { 309 return "unset"; 310 } 311 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 312 c.setTimeInMillis(ms); 313 return String.format("%4d/%02d/%02d %2d:%02d:%02dZ", 314 c.get(Calendar.YEAR), 315 c.get(Calendar.MONTH) + 1, 316 c.get(Calendar.DAY_OF_MONTH), 317 c.get(Calendar.HOUR_OF_DAY), 318 c.get(Calendar.MINUTE), 319 c.get(Calendar.SECOND)); 320 } 321 unquote(String s)322 public static String unquote(String s) { 323 if (s == null) { 324 return null; 325 } 326 else if (s.length() > 1 && s.startsWith("\"") && s.endsWith("\"")) { 327 return s.substring(1, s.length()-1); 328 } 329 else { 330 return s; 331 } 332 } 333 } 334