Home | History | Annotate | Download | only in util
      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 
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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 enclosed quotes of the provided string.
    193      *
    194      * @param quotedStr String to be unquoted.
    195      * @return String without the enclosing quotes.
    196      */
    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 of the provided string.
    208      *
    209      * @param str String to be uoted.
    210      * @return String with the enclosing quotes.
    211      */
    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      */
    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      */
    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      */
    274     public static ArrayList<Byte> decodeSsid(String ssidStr) {
    275         return hexOrQuotedStringToBytes(ssidStr);
    276     }
    277 
    278     /**
    279      * Converts an ArrayList<Byte> of UTF_8 byte values to ssid string.
    280      * The string will either be:
    281      * a) UTF-8 String encapsulated in quotes (if all the bytes are UTF-8 encodeable and non null),
    282      * or
    283      * b) Hex string with no delimiters.
    284      *
    285      * @param ssidBytes List of bytes for ssid.
    286      * @throws IllegalArgumentException for null bytes.
    287      */
    288     public static String encodeSsid(ArrayList<Byte> ssidBytes) {
    289         return bytesToHexOrQuotedString(ssidBytes);
    290     }
    291 
    292     /**
    293      * Convert from an array of primitive bytes to an array list of Byte.
    294      */
    295     public static ArrayList<Byte> byteArrayToArrayList(byte[] bytes) {
    296         ArrayList<Byte> byteList = new ArrayList<>();
    297         for (Byte b : bytes) {
    298             byteList.add(b);
    299         }
    300         return byteList;
    301     }
    302 
    303     /**
    304      * Convert from an array list of Byte to an array of primitive bytes.
    305      */
    306     public static byte[] byteArrayFromArrayList(ArrayList<Byte> bytes) {
    307         byte[] byteArray = new byte[bytes.size()];
    308         int i = 0;
    309         for (Byte b : bytes) {
    310             byteArray[i++] = b;
    311         }
    312         return byteArray;
    313     }
    314 
    315     /**
    316      * Converts a hex string to byte array.
    317      *
    318      * @param hexStr String to be converted.
    319      * @throws IllegalArgumentException for null string.
    320      */
    321     public static byte[] hexStringToByteArray(String hexStr) {
    322         if (hexStr == null) {
    323             throw new IllegalArgumentException("null hex string");
    324         }
    325         return HexEncoding.decode(hexStr.toCharArray(), false);
    326     }
    327 
    328     /**
    329      * Converts a byte array to hex string.
    330      *
    331      * @param bytes List of bytes for ssid.
    332      * @throws IllegalArgumentException for null bytes.
    333      */
    334     public static String hexStringFromByteArray(byte[] bytes) {
    335         if (bytes == null) {
    336             throw new IllegalArgumentException("null hex bytes");
    337         }
    338         return new String(HexEncoding.encode(bytes)).toLowerCase();
    339     }
    340 
    341     /**
    342      * Converts an 8 byte array to a WPS device type string
    343      * { 0, 1, 2, -1, 4, 5, 6, 7 } --> "1-02FF0405-1543";
    344      */
    345     public static String wpsDevTypeStringFromByteArray(byte[] devType) {
    346         byte[] a = devType;
    347         int x = ((a[0] & 0xFF) << 8) | (a[1] & 0xFF);
    348         String y = new String(HexEncoding.encode(Arrays.copyOfRange(devType, 2, 6)));
    349         int z = ((a[6] & 0xFF) << 8) | (a[7] & 0xFF);
    350         return String.format("%d-%s-%d", x, y, z);
    351     }
    352 }
    353