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