1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.util; 18 19 import android.text.TextUtils; 20 21 import com.android.server.wifi.ByteBufferReader; 22 23 import libcore.util.HexEncoding; 24 25 import java.nio.BufferUnderflowException; 26 import java.nio.ByteBuffer; 27 import java.nio.ByteOrder; 28 import java.nio.CharBuffer; 29 import java.nio.charset.CharacterCodingException; 30 import java.nio.charset.CharsetDecoder; 31 import java.nio.charset.CharsetEncoder; 32 import java.nio.charset.StandardCharsets; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 36 /** 37 * Provide utility functions for native interfacing modules. 38 */ 39 public class NativeUtil { 40 private static final String ANY_MAC_STR = "any"; 41 public static final byte[] ANY_MAC_BYTES = {0, 0, 0, 0, 0, 0}; 42 private static final int MAC_LENGTH = 6; 43 private static final int MAC_OUI_LENGTH = 3; 44 private static final int MAC_STR_LENGTH = MAC_LENGTH * 2 + 5; 45 private static final int SSID_BYTES_MAX_LEN = 32; 46 47 /** 48 * Convert the string to byte array list. 49 * 50 * @return the UTF_8 char byte values of str, as an ArrayList. 51 * @throws IllegalArgumentException if a null or unencodable string is sent. 52 */ stringToByteArrayList(String str)53 public static ArrayList<Byte> stringToByteArrayList(String str) { 54 if (str == null) { 55 throw new IllegalArgumentException("null string"); 56 } 57 // Ensure that the provided string is UTF_8 encoded. 58 CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder(); 59 try { 60 ByteBuffer encoded = encoder.encode(CharBuffer.wrap(str)); 61 byte[] byteArray = new byte[encoded.remaining()]; 62 encoded.get(byteArray); 63 return byteArrayToArrayList(byteArray); 64 } catch (CharacterCodingException cce) { 65 throw new IllegalArgumentException("cannot be utf-8 encoded", cce); 66 } 67 } 68 69 /** 70 * Convert the byte array list to string. 71 * 72 * @return the string decoded from UTF_8 byte values in byteArrayList. 73 * @throws IllegalArgumentException if a null byte array list is sent. 74 */ stringFromByteArrayList(ArrayList<Byte> byteArrayList)75 public static String stringFromByteArrayList(ArrayList<Byte> byteArrayList) { 76 if (byteArrayList == null) { 77 throw new IllegalArgumentException("null byte array list"); 78 } 79 byte[] byteArray = new byte[byteArrayList.size()]; 80 int i = 0; 81 for (Byte b : byteArrayList) { 82 byteArray[i] = b; 83 i++; 84 } 85 return new String(byteArray, StandardCharsets.UTF_8); 86 } 87 88 /** 89 * Convert the string to byte array. 90 * 91 * @return the UTF_8 char byte values of str, as an Array. 92 * @throws IllegalArgumentException if a null string is sent. 93 */ stringToByteArray(String str)94 public static byte[] stringToByteArray(String str) { 95 if (str == null) { 96 throw new IllegalArgumentException("null string"); 97 } 98 return str.getBytes(StandardCharsets.UTF_8); 99 } 100 101 /** 102 * Convert the byte array list to string. 103 * 104 * @return the string decoded from UTF_8 byte values in byteArray. 105 * @throws IllegalArgumentException if a null byte array is sent. 106 */ stringFromByteArray(byte[] byteArray)107 public static String stringFromByteArray(byte[] byteArray) { 108 if (byteArray == null) { 109 throw new IllegalArgumentException("null byte array"); 110 } 111 return new String(byteArray); 112 } 113 114 /** 115 * Converts a mac address string to an array of Bytes. 116 * 117 * @param macStr string of format: "XX:XX:XX:XX:XX:XX" or "XXXXXXXXXXXX", where X is any 118 * hexadecimal digit. 119 * Passing null, empty string or "any" is the same as 00:00:00:00:00:00 120 * @throws IllegalArgumentException for various malformed inputs. 121 */ macAddressToByteArray(String macStr)122 public static byte[] macAddressToByteArray(String macStr) { 123 if (TextUtils.isEmpty(macStr) || ANY_MAC_STR.equals(macStr)) return ANY_MAC_BYTES; 124 String cleanMac = macStr.replace(":", ""); 125 if (cleanMac.length() != MAC_LENGTH * 2) { 126 throw new IllegalArgumentException("invalid mac string length: " + cleanMac); 127 } 128 return HexEncoding.decode(cleanMac.toCharArray(), false); 129 } 130 131 /** 132 * Converts an array of 6 bytes to a HexEncoded String with format: "XX:XX:XX:XX:XX:XX", where X 133 * is any hexadecimal digit. 134 * 135 * @param macArray byte array of mac values, must have length 6 136 * @throws IllegalArgumentException for malformed inputs. 137 */ macAddressFromByteArray(byte[] macArray)138 public static String macAddressFromByteArray(byte[] macArray) { 139 if (macArray == null) { 140 throw new IllegalArgumentException("null mac bytes"); 141 } 142 if (macArray.length != MAC_LENGTH) { 143 throw new IllegalArgumentException("invalid macArray length: " + macArray.length); 144 } 145 StringBuilder sb = new StringBuilder(MAC_STR_LENGTH); 146 for (int i = 0; i < macArray.length; i++) { 147 if (i != 0) sb.append(":"); 148 sb.append(new String(HexEncoding.encode(macArray, i, 1))); 149 } 150 return sb.toString().toLowerCase(); 151 } 152 153 /** 154 * Converts a mac address OUI string to an array of Bytes. 155 * 156 * @param macStr string of format: "XX:XX:XX" or "XXXXXX", where X is any hexadecimal digit. 157 * @throws IllegalArgumentException for various malformed inputs. 158 */ macAddressOuiToByteArray(String macStr)159 public static byte[] macAddressOuiToByteArray(String macStr) { 160 if (macStr == null) { 161 throw new IllegalArgumentException("null mac string"); 162 } 163 String cleanMac = macStr.replace(":", ""); 164 if (cleanMac.length() != MAC_OUI_LENGTH * 2) { 165 throw new IllegalArgumentException("invalid mac oui string length: " + cleanMac); 166 } 167 return HexEncoding.decode(cleanMac.toCharArray(), false); 168 } 169 170 /** 171 * Converts an array of 6 bytes to a long representing the MAC address. 172 * 173 * @param macArray byte array of mac values, must have length 6 174 * @return Long value of the mac address. 175 * @throws IllegalArgumentException for malformed inputs. 176 */ macAddressToLong(byte[] macArray)177 public static Long macAddressToLong(byte[] macArray) { 178 if (macArray == null) { 179 throw new IllegalArgumentException("null mac bytes"); 180 } 181 if (macArray.length != MAC_LENGTH) { 182 throw new IllegalArgumentException("invalid macArray length: " + macArray.length); 183 } 184 try { 185 return ByteBufferReader.readInteger( 186 ByteBuffer.wrap(macArray), ByteOrder.BIG_ENDIAN, macArray.length); 187 } catch (BufferUnderflowException | IllegalArgumentException e) { 188 throw new IllegalArgumentException("invalid macArray"); 189 } 190 } 191 192 /** 193 * Remove enclosing quotes from the provided string. 194 * 195 * @param quotedStr String to be unquoted. 196 * @return String without the enclosing quotes. 197 */ removeEnclosingQuotes(String quotedStr)198 public static String removeEnclosingQuotes(String quotedStr) { 199 int length = quotedStr.length(); 200 if ((length >= 2) 201 && (quotedStr.charAt(0) == '"') && (quotedStr.charAt(length - 1) == '"')) { 202 return quotedStr.substring(1, length - 1); 203 } 204 return quotedStr; 205 } 206 207 /** 208 * Add enclosing quotes to the provided string. 209 * 210 * @param str String to be quoted. 211 * @return String with the enclosing quotes. 212 */ addEnclosingQuotes(String str)213 public static String addEnclosingQuotes(String str) { 214 return "\"" + str + "\""; 215 } 216 217 /** 218 * Converts an string to an arraylist of UTF_8 byte values. 219 * These forms are acceptable: 220 * a) UTF-8 String encapsulated in quotes, or 221 * b) Hex string with no delimiters. 222 * 223 * @param str String to be converted. 224 * @throws IllegalArgumentException for null string. 225 */ hexOrQuotedStringToBytes(String str)226 public static ArrayList<Byte> hexOrQuotedStringToBytes(String str) { 227 if (str == null) { 228 throw new IllegalArgumentException("null string"); 229 } 230 int length = str.length(); 231 if ((length > 1) && (str.charAt(0) == '"') && (str.charAt(length - 1) == '"')) { 232 str = str.substring(1, str.length() - 1); 233 return stringToByteArrayList(str); 234 } else { 235 return byteArrayToArrayList(hexStringToByteArray(str)); 236 } 237 } 238 239 /** 240 * Converts an ArrayList<Byte> of UTF_8 byte values to string. 241 * The string will either be: 242 * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non null), 243 * or 244 * b) Hex string with no delimiters. 245 * 246 * @param bytes List of bytes for ssid. 247 * @throws IllegalArgumentException for null bytes. 248 */ bytesToHexOrQuotedString(ArrayList<Byte> bytes)249 public static String bytesToHexOrQuotedString(ArrayList<Byte> bytes) { 250 if (bytes == null) { 251 throw new IllegalArgumentException("null ssid bytes"); 252 } 253 byte[] byteArray = byteArrayFromArrayList(bytes); 254 // Check for 0's in the byte stream in which case we cannot convert this into a string. 255 if (!bytes.contains(Byte.valueOf((byte) 0))) { 256 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); 257 try { 258 CharBuffer decoded = decoder.decode(ByteBuffer.wrap(byteArray)); 259 return "\"" + decoded.toString() + "\""; 260 } catch (CharacterCodingException cce) { 261 } 262 } 263 return hexStringFromByteArray(byteArray); 264 } 265 266 /** 267 * Converts an ssid string to an arraylist of UTF_8 byte values. 268 * These forms are acceptable: 269 * a) UTF-8 String encapsulated in quotes, or 270 * b) Hex string with no delimiters. 271 * 272 * @param ssidStr String to be converted. 273 * @throws IllegalArgumentException for null string. 274 */ decodeSsid(String ssidStr)275 public static ArrayList<Byte> decodeSsid(String ssidStr) { 276 ArrayList<Byte> ssidBytes = hexOrQuotedStringToBytes(ssidStr); 277 if (ssidBytes.size() > SSID_BYTES_MAX_LEN) { 278 throw new IllegalArgumentException("ssid bytes size out of range: " + ssidBytes.size()); 279 } 280 return ssidBytes; 281 } 282 283 /** 284 * Converts an ArrayList<Byte> of UTF_8 byte values to ssid string. 285 * The string will either be: 286 * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non null), 287 * or 288 * b) Hex string with no delimiters. 289 * 290 * @param ssidBytes List of bytes for ssid. 291 * @throws IllegalArgumentException for null bytes. 292 */ encodeSsid(ArrayList<Byte> ssidBytes)293 public static String encodeSsid(ArrayList<Byte> ssidBytes) { 294 if (ssidBytes.size() > SSID_BYTES_MAX_LEN) { 295 throw new IllegalArgumentException("ssid bytes size out of range: " + ssidBytes.size()); 296 } 297 return bytesToHexOrQuotedString(ssidBytes); 298 } 299 300 /** 301 * Convert from an array of primitive bytes to an array list of Byte. 302 */ byteArrayToArrayList(byte[] bytes)303 public static ArrayList<Byte> byteArrayToArrayList(byte[] bytes) { 304 ArrayList<Byte> byteList = new ArrayList<>(); 305 for (Byte b : bytes) { 306 byteList.add(b); 307 } 308 return byteList; 309 } 310 311 /** 312 * Convert from an array list of Byte to an array of primitive bytes. 313 */ byteArrayFromArrayList(ArrayList<Byte> bytes)314 public static byte[] byteArrayFromArrayList(ArrayList<Byte> bytes) { 315 byte[] byteArray = new byte[bytes.size()]; 316 int i = 0; 317 for (Byte b : bytes) { 318 byteArray[i++] = b; 319 } 320 return byteArray; 321 } 322 323 /** 324 * Converts a hex string to byte array. 325 * 326 * @param hexStr String to be converted. 327 * @throws IllegalArgumentException for null string. 328 */ hexStringToByteArray(String hexStr)329 public static byte[] hexStringToByteArray(String hexStr) { 330 if (hexStr == null) { 331 throw new IllegalArgumentException("null hex string"); 332 } 333 return HexEncoding.decode(hexStr.toCharArray(), false); 334 } 335 336 /** 337 * Converts a byte array to hex string. 338 * 339 * @param bytes List of bytes for ssid. 340 * @throws IllegalArgumentException for null bytes. 341 */ hexStringFromByteArray(byte[] bytes)342 public static String hexStringFromByteArray(byte[] bytes) { 343 if (bytes == null) { 344 throw new IllegalArgumentException("null hex bytes"); 345 } 346 return new String(HexEncoding.encode(bytes)).toLowerCase(); 347 } 348 349 /** 350 * Converts an 8 byte array to a WPS device type string 351 * { 0, 1, 2, -1, 4, 5, 6, 7 } --> "1-02FF0405-1543"; 352 */ wpsDevTypeStringFromByteArray(byte[] devType)353 public static String wpsDevTypeStringFromByteArray(byte[] devType) { 354 byte[] a = devType; 355 int x = ((a[0] & 0xFF) << 8) | (a[1] & 0xFF); 356 String y = new String(HexEncoding.encode(Arrays.copyOfRange(devType, 2, 6))); 357 int z = ((a[6] & 0xFF) << 8) | (a[7] & 0xFF); 358 return String.format("%d-%s-%d", x, y, z); 359 } 360 } 361