1package com.android.hotspot2;
2
3import com.android.anqp.Constants;
4
5import java.nio.ByteBuffer;
6import java.util.ArrayList;
7import java.util.Calendar;
8import java.util.Collection;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.TimeZone;
12
13import static com.android.anqp.Constants.BYTE_MASK;
14import static com.android.anqp.Constants.NIBBLE_MASK;
15
16public abstract class Utils {
17
18    public static final long UNSET_TIME = -1;
19
20    private static final int EUI48Length = 6;
21    private static final int EUI64Length = 8;
22    private static final long EUI48Mask = 0xffffffffffffL;
23    private static final String[] PLMNText = {"org", "3gppnetwork", "mcc*", "mnc*", "wlan"};
24
25    public static List<String> splitDomain(String domain) {
26
27        if (domain.endsWith("."))
28            domain = domain.substring(0, domain.length() - 1);
29        int at = domain.indexOf('@');
30        if (at >= 0)
31            domain = domain.substring(at + 1);
32
33        String[] labels = domain.toLowerCase().split("\\.");
34        LinkedList<String> labelList = new LinkedList<String>();
35        for (String label : labels) {
36            labelList.addFirst(label);
37        }
38
39        return labelList;
40    }
41
42    public static long parseMac(String s) {
43
44        long mac = 0;
45        int count = 0;
46        for (int n = 0; n < s.length(); n++) {
47            int nibble = Utils.fromHex(s.charAt(n), true);  // Set lenient to not blow up on ':'
48            if (nibble >= 0) {                              // ... and use only legit hex.
49                mac = (mac << 4) | nibble;
50                count++;
51            }
52        }
53        if (count < 12 || (count & 1) == 1) {
54            throw new IllegalArgumentException("Bad MAC address: '" + s + "'");
55        }
56        return mac;
57    }
58
59    public static String macToString(long mac) {
60        int len = (mac & ~EUI48Mask) != 0 ? EUI64Length : EUI48Length;
61        StringBuilder sb = new StringBuilder();
62        boolean first = true;
63        for (int n = (len - 1) * Byte.SIZE; n >= 0; n -= Byte.SIZE) {
64            if (first) {
65                first = false;
66            } else {
67                sb.append(':');
68            }
69            sb.append(String.format("%02x", (mac >>> n) & Constants.BYTE_MASK));
70        }
71        return sb.toString();
72    }
73
74    public static String getMccMnc(List<String> domain) {
75        if (domain.size() != PLMNText.length) {
76            return null;
77        }
78
79        for (int n = 0; n < PLMNText.length; n++) {
80            String expect = PLMNText[n];
81            int len = expect.endsWith("*") ? expect.length() - 1 : expect.length();
82            if (!domain.get(n).regionMatches(0, expect, 0, len)) {
83                return null;
84            }
85        }
86
87        String prefix = domain.get(2).substring(3) + domain.get(3).substring(3);
88        for (int n = 0; n < prefix.length(); n++) {
89            char ch = prefix.charAt(n);
90            if (ch < '0' || ch > '9') {
91                return null;
92            }
93        }
94        return prefix;
95    }
96
97    public static String bssidsToString(Collection<Long> bssids) {
98        StringBuilder sb = new StringBuilder();
99        for (Long bssid : bssids) {
100            sb.append(String.format(" %012x", bssid));
101        }
102        return sb.toString();
103    }
104
105    public static String roamingConsortiumsToString(long[] ois) {
106        if (ois == null) {
107            return "null";
108        }
109        List<Long> list = new ArrayList<Long>(ois.length);
110        for (long oi : ois) {
111            list.add(oi);
112        }
113        return roamingConsortiumsToString(list);
114    }
115
116    public static String roamingConsortiumsToString(Collection<Long> ois) {
117        StringBuilder sb = new StringBuilder();
118        boolean first = true;
119        for (long oi : ois) {
120            if (first) {
121                first = false;
122            } else {
123                sb.append(", ");
124            }
125            if (Long.numberOfLeadingZeros(oi) > 40) {
126                sb.append(String.format("%06x", oi));
127            } else {
128                sb.append(String.format("%010x", oi));
129            }
130        }
131        return sb.toString();
132    }
133
134    public static String toUnicodeEscapedString(String s) {
135        StringBuilder sb = new StringBuilder(s.length());
136        for (int n = 0; n < s.length(); n++) {
137            char ch = s.charAt(n);
138            if (ch >= ' ' && ch < 127) {
139                sb.append(ch);
140            } else {
141                sb.append("\\u").append(String.format("%04x", (int) ch));
142            }
143        }
144        return sb.toString();
145    }
146
147    public static String toHexString(byte[] data) {
148        if (data == null) {
149            return "null";
150        }
151        StringBuilder sb = new StringBuilder(data.length * 3);
152
153        boolean first = true;
154        for (byte b : data) {
155            if (first) {
156                first = false;
157            } else {
158                sb.append(' ');
159            }
160            sb.append(String.format("%02x", b & BYTE_MASK));
161        }
162        return sb.toString();
163    }
164
165    public static String toHex(byte[] octets) {
166        StringBuilder sb = new StringBuilder(octets.length * 2);
167        for (byte o : octets) {
168            sb.append(String.format("%02x", o & BYTE_MASK));
169        }
170        return sb.toString();
171    }
172
173    public static byte[] hexToBytes(String text) {
174        if ((text.length() & 1) == 1) {
175            throw new NumberFormatException("Odd length hex string: " + text.length());
176        }
177        byte[] data = new byte[text.length() >> 1];
178        int position = 0;
179        for (int n = 0; n < text.length(); n += 2) {
180            data[position] =
181                    (byte) (((fromHex(text.charAt(n), false) & NIBBLE_MASK) << 4) |
182                            (fromHex(text.charAt(n + 1), false) & NIBBLE_MASK));
183            position++;
184        }
185        return data;
186    }
187
188    public static int fromHex(char ch, boolean lenient) throws NumberFormatException {
189        if (ch <= '9' && ch >= '0') {
190            return ch - '0';
191        } else if (ch >= 'a' && ch <= 'f') {
192            return ch + 10 - 'a';
193        } else if (ch <= 'F' && ch >= 'A') {
194            return ch + 10 - 'A';
195        } else if (lenient) {
196            return -1;
197        } else {
198            throw new NumberFormatException("Bad hex-character: " + ch);
199        }
200    }
201
202    private static char toAscii(int b) {
203        return b >= ' ' && b < 0x7f ? (char) b : '.';
204    }
205
206    static boolean isDecimal(String s) {
207        for (int n = 0; n < s.length(); n++) {
208            char ch = s.charAt(n);
209            if (ch < '0' || ch > '9') {
210                return false;
211            }
212        }
213        return true;
214    }
215
216    public static <T extends Comparable> int compare(Comparable<T> c1, T c2) {
217        if (c1 == null) {
218            return c2 == null ? 0 : -1;
219        } else if (c2 == null) {
220            return 1;
221        } else {
222            return c1.compareTo(c2);
223        }
224    }
225
226    public static String bytesToBingoCard(ByteBuffer data, int len) {
227        ByteBuffer dup = data.duplicate();
228        dup.limit(dup.position() + len);
229        return bytesToBingoCard(dup);
230    }
231
232    public static String bytesToBingoCard(ByteBuffer data) {
233        ByteBuffer dup = data.duplicate();
234        StringBuilder sbx = new StringBuilder();
235        while (dup.hasRemaining()) {
236            sbx.append(String.format("%02x ", dup.get() & BYTE_MASK));
237        }
238        dup = data.duplicate();
239        sbx.append(' ');
240        while (dup.hasRemaining()) {
241            sbx.append(String.format("%c", toAscii(dup.get() & BYTE_MASK)));
242        }
243        return sbx.toString();
244    }
245
246    public static String toHMS(long millis) {
247        long time = millis >= 0 ? millis : -millis;
248        long tmp = time / 1000L;
249        long ms = time - tmp * 1000L;
250
251        time = tmp;
252        tmp /= 60L;
253        long s = time - tmp * 60L;
254
255        time = tmp;
256        tmp /= 60L;
257        long m = time - tmp * 60L;
258
259        return String.format("%s%d:%02d:%02d.%03d", millis < 0 ? "-" : "", tmp, m, s, ms);
260    }
261
262    public static String toUTCString(long ms) {
263        if (ms < 0) {
264            return "unset";
265        }
266        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
267        c.setTimeInMillis(ms);
268        return String.format("%4d/%02d/%02d %2d:%02d:%02dZ",
269                c.get(Calendar.YEAR),
270                c.get(Calendar.MONTH) + 1,
271                c.get(Calendar.DAY_OF_MONTH),
272                c.get(Calendar.HOUR_OF_DAY),
273                c.get(Calendar.MINUTE),
274                c.get(Calendar.SECOND));
275    }
276
277    public static String unquote(String s) {
278        if (s == null) {
279            return null;
280        } else if (s.length() > 1 && s.startsWith("\"") && s.endsWith("\"")) {
281            return s.substring(1, s.length() - 1);
282        } else {
283            return s;
284        }
285    }
286
287
288    public static void delay(long ms) {
289        long until = System.currentTimeMillis() + ms;
290        for (; ; ) {
291            long remainder = until - System.currentTimeMillis();
292            if (remainder <= 0) {
293                break;
294            }
295            try {
296                Thread.sleep(remainder);
297            } catch (InterruptedException ie) { /**/ }
298        }
299    }
300}
301