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