1 /* 2 * Copyright (C) 2014 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 android.net.wifi.util; 18 19 /** 20 * Hexadecimal encoding where each byte is represented by two hexadecimal digits. 21 * 22 * Note: this is copied from {@link libcore.util.HexEncoding}. 23 * 24 * @hide 25 */ 26 public class HexEncoding { 27 28 private static final char[] LOWER_CASE_DIGITS = { 29 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 30 }; 31 32 private static final char[] UPPER_CASE_DIGITS = { 33 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 34 }; 35 36 /** Hidden constructor to prevent instantiation. */ HexEncoding()37 private HexEncoding() {} 38 39 /** 40 * Encodes the provided byte as a two-digit hexadecimal String value. 41 */ encodeToString(byte b, boolean upperCase)42 public static String encodeToString(byte b, boolean upperCase) { 43 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 44 char[] buf = new char[2]; // We always want two digits. 45 buf[0] = digits[(b >> 4) & 0xf]; 46 buf[1] = digits[b & 0xf]; 47 return new String(buf, 0, 2); 48 } 49 50 /** 51 * Encodes the provided data as a sequence of hexadecimal characters. 52 */ encode(byte[] data)53 public static char[] encode(byte[] data) { 54 return encode(data, 0, data.length, true /* upperCase */); 55 } 56 57 /** 58 * Encodes the provided data as a sequence of hexadecimal characters. 59 */ encode(byte[] data, boolean upperCase)60 public static char[] encode(byte[] data, boolean upperCase) { 61 return encode(data, 0, data.length, upperCase); 62 } 63 64 /** 65 * Encodes the provided data as a sequence of hexadecimal characters. 66 */ encode(byte[] data, int offset, int len)67 public static char[] encode(byte[] data, int offset, int len) { 68 return encode(data, offset, len, true /* upperCase */); 69 } 70 71 /** 72 * Encodes the provided data as a sequence of hexadecimal characters. 73 */ encode(byte[] data, int offset, int len, boolean upperCase)74 private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { 75 char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; 76 char[] result = new char[len * 2]; 77 for (int i = 0; i < len; i++) { 78 byte b = data[offset + i]; 79 int resultIndex = 2 * i; 80 result[resultIndex] = (digits[(b >> 4) & 0x0f]); 81 result[resultIndex + 1] = (digits[b & 0x0f]); 82 } 83 84 return result; 85 } 86 87 /** 88 * Encodes the provided data as a sequence of hexadecimal characters. 89 */ encodeToString(byte[] data)90 public static String encodeToString(byte[] data) { 91 return encodeToString(data, true /* upperCase */); 92 } 93 94 /** 95 * Encodes the provided data as a sequence of hexadecimal characters. 96 */ encodeToString(byte[] data, boolean upperCase)97 public static String encodeToString(byte[] data, boolean upperCase) { 98 return new String(encode(data, upperCase)); 99 } 100 101 /** 102 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 103 * are not allowed. 104 * 105 * Throws an {@code IllegalArgumentException} if the input is malformed. 106 */ decode(String encoded)107 public static byte[] decode(String encoded) throws IllegalArgumentException { 108 return decode(encoded.toCharArray()); 109 } 110 111 /** 112 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 113 * is {@code true} odd-length inputs are allowed and the first character is interpreted 114 * as the lower bits of the first result byte. 115 * 116 * Throws an {@code IllegalArgumentException} if the input is malformed. 117 */ decode(String encoded, boolean allowSingleChar)118 public static byte[] decode(String encoded, boolean allowSingleChar) 119 throws IllegalArgumentException { 120 return decode(encoded.toCharArray(), allowSingleChar); 121 } 122 123 /** 124 * Decodes the provided hexadecimal string into a byte array. Odd-length inputs 125 * are not allowed. 126 * 127 * Throws an {@code IllegalArgumentException} if the input is malformed. 128 */ decode(char[] encoded)129 public static byte[] decode(char[] encoded) throws IllegalArgumentException { 130 return decode(encoded, false); 131 } 132 133 /** 134 * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar} 135 * is {@code true} odd-length inputs are allowed and the first character is interpreted 136 * as the lower bits of the first result byte. 137 * 138 * Throws an {@code IllegalArgumentException} if the input is malformed. 139 */ decode(char[] encoded, boolean allowSingleChar)140 public static byte[] decode(char[] encoded, boolean allowSingleChar) 141 throws IllegalArgumentException { 142 int encodedLength = encoded.length; 143 int resultLengthBytes = (encodedLength + 1) / 2; 144 byte[] result = new byte[resultLengthBytes]; 145 146 int resultOffset = 0; 147 int i = 0; 148 if (allowSingleChar) { 149 if ((encodedLength % 2) != 0) { 150 // Odd number of digits -- the first digit is the lower 4 bits of the first result 151 // byte. 152 result[resultOffset++] = (byte) toDigit(encoded, i); 153 i++; 154 } 155 } else { 156 if ((encodedLength % 2) != 0) { 157 throw new IllegalArgumentException("Invalid input length: " + encodedLength); 158 } 159 } 160 161 for (; i < encodedLength; i += 2) { 162 result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); 163 } 164 165 return result; 166 } 167 toDigit(char[] str, int offset)168 private static int toDigit(char[] str, int offset) throws IllegalArgumentException { 169 // NOTE: that this isn't really a code point in the traditional sense, since we're 170 // just rejecting surrogate pairs outright. 171 int pseudoCodePoint = str[offset]; 172 173 if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { 174 return pseudoCodePoint - '0'; 175 } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { 176 return 10 + (pseudoCodePoint - 'a'); 177 } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { 178 return 10 + (pseudoCodePoint - 'A'); 179 } 180 181 throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); 182 } 183 } 184