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