• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.hotspot2;
2 
3 import com.android.anqp.Constants;
4 
5 import java.nio.ByteBuffer;
6 import java.nio.charset.CharacterCodingException;
7 import java.nio.charset.CharsetDecoder;
8 import java.nio.charset.StandardCharsets;
9 import java.util.ArrayList;
10 import java.util.Calendar;
11 import java.util.Collection;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.TimeZone;
15 
16 import static com.android.anqp.Constants.BYTE_MASK;
17 import static com.android.anqp.Constants.NIBBLE_MASK;
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 
splitDomain(String domain)28     public static List<String> splitDomain(String domain) {
29 
30         if (domain.endsWith("."))
31             domain = domain.substring(0, domain.length() - 1);
32         int at = domain.indexOf('@');
33         if (at >= 0)
34             domain = domain.substring(at + 1);
35 
36         String[] labels = domain.toLowerCase().split("\\.");
37         LinkedList<String> labelList = new LinkedList<String>();
38         for (String label : labels) {
39             labelList.addFirst(label);
40         }
41 
42         return labelList;
43     }
44 
parseMac(String s)45     public static long parseMac(String s) {
46 
47         long mac = 0;
48         int count = 0;
49         for (int n = 0; n < s.length(); n++) {
50             int nibble = Utils.fromHex(s.charAt(n), true);  // Set lenient to not blow up on ':'
51             if (nibble >= 0) {                              // ... and use only legit hex.
52                 mac = (mac << 4) | nibble;
53                 count++;
54             }
55         }
56         if (count < 12 || (count & 1) == 1) {
57             throw new IllegalArgumentException("Bad MAC address: '" + s + "'");
58         }
59         return mac;
60     }
61 
macToString(long mac)62     public static String macToString(long mac) {
63         int len = (mac & ~EUI48Mask) != 0 ? EUI64Length : EUI48Length;
64         StringBuilder sb = new StringBuilder();
65         boolean first = true;
66         for (int n = (len - 1) * Byte.SIZE; n >= 0; n -= Byte.SIZE) {
67             if (first) {
68                 first = false;
69             } else {
70                 sb.append(':');
71             }
72             sb.append(String.format("%02x", (mac >>> n) & Constants.BYTE_MASK));
73         }
74         return sb.toString();
75     }
76 
getMccMnc(List<String> domain)77     public static String getMccMnc(List<String> domain) {
78         if (domain.size() != PLMNText.length) {
79             return null;
80         }
81 
82         for (int n = 0; n < PLMNText.length; n++) {
83             String expect = PLMNText[n];
84             int len = expect.endsWith("*") ? expect.length() - 1 : expect.length();
85             if (!domain.get(n).regionMatches(0, expect, 0, len)) {
86                 return null;
87             }
88         }
89 
90         String prefix = domain.get(2).substring(3) + domain.get(3).substring(3);
91         for (int n = 0; n < prefix.length(); n++) {
92             char ch = prefix.charAt(n);
93             if (ch < '0' || ch > '9') {
94                 return null;
95             }
96         }
97         return prefix;
98     }
99 
toIpString(int leIp)100     public static String toIpString(int leIp) {
101         return String.format("%d.%d.%d.%d",
102                 leIp & BYTE_MASK,
103                 (leIp >> 8) & BYTE_MASK,
104                 (leIp >> 16) & BYTE_MASK,
105                 (leIp >> 24) & BYTE_MASK);
106     }
107 
bssidsToString(Collection<Long> bssids)108     public static String bssidsToString(Collection<Long> bssids) {
109         StringBuilder sb = new StringBuilder();
110         for (Long bssid : bssids) {
111             sb.append(String.format(" %012x", bssid));
112         }
113         return sb.toString();
114     }
115 
roamingConsortiumsToString(long[] ois)116     public static String roamingConsortiumsToString(long[] ois) {
117         if (ois == null) {
118             return "null";
119         }
120         List<Long> list = new ArrayList<Long>(ois.length);
121         for (long oi : ois) {
122             list.add(oi);
123         }
124         return roamingConsortiumsToString(list);
125     }
126 
roamingConsortiumsToString(Collection<Long> ois)127     public static String roamingConsortiumsToString(Collection<Long> ois) {
128         StringBuilder sb = new StringBuilder();
129         boolean first = true;
130         for (long oi : ois) {
131             if (first) {
132                 first = false;
133             } else {
134                 sb.append(", ");
135             }
136             if (Long.numberOfLeadingZeros(oi) > 40) {
137                 sb.append(String.format("%06x", oi));
138             } else {
139                 sb.append(String.format("%010x", oi));
140             }
141         }
142         return sb.toString();
143     }
144 
toUnicodeEscapedString(String s)145     public static String toUnicodeEscapedString(String s) {
146         StringBuilder sb = new StringBuilder(s.length());
147         for (int n = 0; n < s.length(); n++) {
148             char ch = s.charAt(n);
149             if (ch >= ' ' && ch < 127) {
150                 sb.append(ch);
151             } else {
152                 sb.append("\\u").append(String.format("%04x", (int) ch));
153             }
154         }
155         return sb.toString();
156     }
157 
toHexString(byte[] data)158     public static String toHexString(byte[] data) {
159         if (data == null) {
160             return "null";
161         }
162         StringBuilder sb = new StringBuilder(data.length * 3);
163 
164         boolean first = true;
165         for (byte b : data) {
166             if (first) {
167                 first = false;
168             } else {
169                 sb.append(' ');
170             }
171             sb.append(String.format("%02x", b & BYTE_MASK));
172         }
173         return sb.toString();
174     }
175 
toHex(byte[] octets)176     public static String toHex(byte[] octets) {
177         StringBuilder sb = new StringBuilder(octets.length * 2);
178         for (byte o : octets) {
179             sb.append(String.format("%02x", o & BYTE_MASK));
180         }
181         return sb.toString();
182     }
183 
hexToBytes(String text)184     public static byte[] hexToBytes(String text) {
185         if ((text.length() & 1) == 1) {
186             throw new NumberFormatException("Odd length hex string: " + text.length());
187         }
188         byte[] data = new byte[text.length() >> 1];
189         int position = 0;
190         for (int n = 0; n < text.length(); n += 2) {
191             data[position] =
192                     (byte) (((fromHex(text.charAt(n), false) & NIBBLE_MASK) << 4) |
193                             (fromHex(text.charAt(n + 1), false) & NIBBLE_MASK));
194             position++;
195         }
196         return data;
197     }
198 
fromHex(char ch, boolean lenient)199     public static int fromHex(char ch, boolean lenient) throws NumberFormatException {
200         if (ch <= '9' && ch >= '0') {
201             return ch - '0';
202         } else if (ch >= 'a' && ch <= 'f') {
203             return ch + 10 - 'a';
204         } else if (ch <= 'F' && ch >= 'A') {
205             return ch + 10 - 'A';
206         } else if (lenient) {
207             return -1;
208         } else {
209             throw new NumberFormatException("Bad hex-character: " + ch);
210         }
211     }
212 
toAscii(int b)213     private static char toAscii(int b) {
214         return b >= ' ' && b < 0x7f ? (char) b : '.';
215     }
216 
isDecimal(String s)217     static boolean isDecimal(String s) {
218         for (int n = 0; n < s.length(); n++) {
219             char ch = s.charAt(n);
220             if (ch < '0' || ch > '9') {
221                 return false;
222             }
223         }
224         return true;
225     }
226 
compare(Comparable<T> c1, T c2)227     public static <T extends Comparable> int compare(Comparable<T> c1, T c2) {
228         if (c1 == null) {
229             return c2 == null ? 0 : -1;
230         } else if (c2 == null) {
231             return 1;
232         } else {
233             return c1.compareTo(c2);
234         }
235     }
236 
bytesToBingoCard(ByteBuffer data, int len)237     public static String bytesToBingoCard(ByteBuffer data, int len) {
238         ByteBuffer dup = data.duplicate();
239         dup.limit(dup.position() + len);
240         return bytesToBingoCard(dup);
241     }
242 
bytesToBingoCard(ByteBuffer data)243     public static String bytesToBingoCard(ByteBuffer data) {
244         ByteBuffer dup = data.duplicate();
245         StringBuilder sbx = new StringBuilder();
246         while (dup.hasRemaining()) {
247             sbx.append(String.format("%02x ", dup.get() & BYTE_MASK));
248         }
249         dup = data.duplicate();
250         sbx.append(' ');
251         while (dup.hasRemaining()) {
252             sbx.append(String.format("%c", toAscii(dup.get() & BYTE_MASK)));
253         }
254         return sbx.toString();
255     }
256 
toHMS(long millis)257     public static String toHMS(long millis) {
258         long time = millis >= 0 ? millis : -millis;
259         long tmp = time / 1000L;
260         long ms = time - tmp * 1000L;
261 
262         time = tmp;
263         tmp /= 60L;
264         long s = time - tmp * 60L;
265 
266         time = tmp;
267         tmp /= 60L;
268         long m = time - tmp * 60L;
269 
270         return String.format("%s%d:%02d:%02d.%03d", millis < 0 ? "-" : "", tmp, m, s, ms);
271     }
272 
toUTCString(long ms)273     public static String toUTCString(long ms) {
274         if (ms < 0) {
275             return "unset";
276         }
277         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
278         c.setTimeInMillis(ms);
279         return String.format("%4d/%02d/%02d %2d:%02d:%02dZ",
280                 c.get(Calendar.YEAR),
281                 c.get(Calendar.MONTH) + 1,
282                 c.get(Calendar.DAY_OF_MONTH),
283                 c.get(Calendar.HOUR_OF_DAY),
284                 c.get(Calendar.MINUTE),
285                 c.get(Calendar.SECOND));
286     }
287 
288     /**
289      * Decode a wpa_supplicant SSID. wpa_supplicant uses double quotes around plain strings, or
290      * expects a hex-string if no quotes appear.
291      * For Ascii encoded string, any octet < 32 or > 127 is encoded as
292      * a "\x" followed by the hex representation of the octet.
293      * Exception chars are ", \, \e, \n, \r, \t which are escaped by a \
294      * See src/utils/common.c for the implementation in the supplicant.
295      *
296      * @param ssid The SSID from the config.
297      * @return The actual string content of the SSID
298      */
decodeSsid(String ssid)299     public static String decodeSsid(String ssid) {
300         if (ssid.length() <= 1) {
301             return ssid;
302         } else if (ssid.startsWith("\"") && ssid.endsWith("\"")) {
303             return unescapeSsid(ssid.substring(1, ssid.length() - 1));
304         } else if ((ssid.length() & 1) == 1) {
305             return ssid;
306         }
307 
308         byte[] codepoints;
309         try {
310             codepoints = new byte[ssid.length() / 2];
311             for (int n = 0; n < ssid.length(); n += 2) {
312                 codepoints[n / 2] = (byte) decodeHexPair(ssid, n);
313             }
314         } catch (NumberFormatException nfe) {
315             return ssid;
316         }
317 
318         try {
319             CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
320             return decoder.decode(ByteBuffer.wrap(codepoints)).toString();
321         } catch (CharacterCodingException cce) {
322             /* Do nothing, try LATIN-1 */
323         }
324         try {
325             CharsetDecoder decoder = StandardCharsets.ISO_8859_1.newDecoder();
326             return decoder.decode(ByteBuffer.wrap(codepoints)).toString();
327         } catch (CharacterCodingException cce) {    // Should not be possible.
328             return ssid;
329         }
330     }
331 
unescapeSsid(String s)332     private static String unescapeSsid(String s) {
333         StringBuilder sb = new StringBuilder();
334         for (int n = 0; n < s.length(); n++) {
335             char ch = s.charAt(n);
336             if (ch != '\\' || n >= s.length() - 1) {
337                 sb.append(ch);
338             } else {
339                 n++;
340                 ch = s.charAt(n);
341                 switch (ch) {
342                     case '"':
343                     case '\\':
344                     default:
345                         sb.append(ch);
346                         break;
347                     case 'e':
348                         sb.append((char) 27);    // Escape char
349                         break;
350                     case 'n':
351                         sb.append('\n');
352                         break;
353                     case 'r':
354                         sb.append('\r');
355                         break;
356                     case 't':
357                         sb.append('\t');
358                         break;
359                     case 'x':
360                         if (s.length() - n < 3) {
361                             sb.append('\\').append(ch);
362                         } else {
363                             n++;
364                             sb.append((char) decodeHexPair(s, n));
365                             n++;
366                         }
367                         break;
368                 }
369             }
370         }
371         return sb.toString();
372     }
373 
decodeHexPair(String s, int position)374     private static int decodeHexPair(String s, int position) {
375         return fromHex(s.charAt(position)) << 4 | fromHex(s.charAt(position + 1));
376     }
377 
fromHex(char ch)378     private static int fromHex(char ch) {
379         if (ch >= '0' && ch <= '9') {
380             return ch - '0';
381         } else if (ch >= 'A' && ch <= 'F') {
382             return ch - 'A' + 10;
383         } else if (ch >= 'a' && ch <= 'f') {
384             return ch - 'a' + 10;
385         } else {
386             throw new NumberFormatException(String.format("Not hex: '%c'", ch));
387         }
388     }
389 
delay(long ms)390     public static void delay(long ms) {
391         long until = System.currentTimeMillis() + ms;
392         for (; ; ) {
393             long remainder = until - System.currentTimeMillis();
394             if (remainder <= 0) {
395                 break;
396             }
397             try {
398                 Thread.sleep(remainder);
399             } catch (InterruptedException ie) { /**/ }
400         }
401     }
402 
mapEnum(int ordinal, Class<T> enumClass)403     public static <T extends Enum<T>> T mapEnum(int ordinal, Class<T> enumClass) {
404         T[] constants = enumClass.getEnumConstants();
405         return ordinal >= 0 && ordinal < constants.length ? constants[ordinal]: null;
406     }
407 }
408