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