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