1 /*
2  * Copyright (C) 2021 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 package com.android.server.uwb.util;
17 
18 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_EXT_MAC_ADDRESS_LEN;
19 import static com.android.server.uwb.data.UwbUciConstants.UWB_DEVICE_SHORT_MAC_ADDRESS_LEN;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26 
27 /** Utility class for doing conversions, including bytes, hex strings, ints, and ASCII. */
28 public class DataTypeConversionUtil {
29 
30     private static final char[] HEX_ARRAY = {
31             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
32     };
33 
34     /**
35      * Conver the hex string to byte array.
36      */
hexStringToByteArray(String hex)37     public static byte[] hexStringToByteArray(String hex) {
38         // remove whitespace in the hex string.
39         hex = hex.replaceAll("\\s", "");
40 
41         int len = hex.length();
42         if (len % 2 != 0) {
43             // Pad the hex string with a leading zero.
44             hex = String.format("0%s", hex);
45             len++;
46         }
47         byte[] data = new byte[len / 2];
48         for (int i = 0; i < len; i += 2) {
49             data[i / 2] =
50                     (byte) ((Character.digit(hex.charAt(i), 16) << 4)
51                             | Character.digit(hex.charAt(i + 1), 16));
52         }
53         return data;
54     }
55 
56     /**
57      * Convert the byte array to hex string.
58      */
59     @NonNull
byteArrayToHexString(@ullable byte[] response)60     public static String byteArrayToHexString(@Nullable byte[] response) {
61         if (response == null) {
62             return "";
63         }
64         return byteArrayToHexString(response, 0, response.length);
65     }
66 
67     /**
68      * Convertt part of the byte array to hex string.
69      */
byteArrayToHexString( byte[] response, int startIndex, int endIndex)70     public static String byteArrayToHexString(
71             byte[] response, int startIndex, int endIndex) {
72         char[] hex = new char[(endIndex - startIndex) * 2];
73         int v;
74         for (int i = 0; i < endIndex - startIndex; i++) {
75             v = unsignedByteToInt(response[startIndex + i]);
76             hex[i * 2] = HEX_ARRAY[v >> 4];
77             hex[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
78         }
79         return new String(hex);
80     }
81 
82     /**
83      * Convert the byte to int.
84      */
unsignedByteToInt(byte b)85     public static int unsignedByteToInt(byte b) {
86         return b & 0xFF;
87     }
88 
89     /**
90      * Convert the int to byte.
91      */
unsignedIntToByte(int n)92     public static byte unsignedIntToByte(int n) {
93         return (byte) (n & 0xFF);
94     }
95 
96     /**
97      * Convert the byte array to int16 using big endian.
98      */
byteArrayToI16(byte[] bytes)99     public static short byteArrayToI16(byte[] bytes) {
100         if (bytes.length != 2) {
101             throw new NumberFormatException("Expected length 2 but was " + bytes.length);
102         }
103         return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getShort();
104     }
105 
106     /**
107      * Convert the byte array to int using big endian.
108      */
byteArrayToI32(byte[] bytes)109     public static int byteArrayToI32(byte[] bytes) {
110         if (bytes.length != 4) {
111             throw new NumberFormatException("Expected length 4 but was " + bytes.length);
112         }
113         return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt();
114     }
115 
116     /**
117      * Convert the byte array with arbitrary size less than 5 to int using big endian.
118      */
arbitraryByteArrayToI32(byte[] bytes)119     public static int arbitraryByteArrayToI32(byte[] bytes) {
120         if (bytes.length > 4 || bytes.length < 1) {
121             throw new NumberFormatException("Expected length less than 5 but was " + bytes.length);
122         }
123         ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES);
124         byteBuffer.position(Integer.BYTES - bytes.length);
125         byteBuffer.put(bytes).rewind();
126         return byteBuffer.getInt();
127     }
128 
129     /**
130      * Convert the int to byte array using big endian.
131      */
i32ToByteArray(int n)132     public static byte[] i32ToByteArray(int n) {
133         return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN).putInt(n).array();
134     }
135 
136     /**
137      * Convert the int to byte array using little endian.
138      */
i32ToLeByteArray(int n)139     public static byte[] i32ToLeByteArray(int n) {
140         return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(n).array();
141     }
142 
143     /**
144      * Convert the byte array (in Little Endian format) to a long. The input array could be: of
145      * shorter size (eg: 2 bytes, to represent a shortMacAddress). It could also have length of 8
146      * bytes, but have the MSB 6 bytes zeroed out (the 2 LSB bytes contain the MacAddress).
147      */
macAddressByteArrayToLong(byte[] bytes)148     public static long macAddressByteArrayToLong(byte[] bytes) {
149         if (bytes.length == 2) {
150             return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
151         } else if (bytes.length == 4) {
152             return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
153         } else if (bytes.length == 8) {
154             if (isExtendedMSBZeroedOut(bytes)) {
155                 return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
156             } else {
157                 return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
158             }
159         } else {
160             throw new NumberFormatException("Expected length one of (2, 4, 8) but was "
161                     + bytes.length);
162         }
163     }
164 
165     /**
166      * Convert the byte array that contains a Short MacAddress format (2 bytes long), into an
167      * Extended MacAddress format (8 bytes long), by padding it with 6 MSB zeroed-out bytes.
168      */
convertShortMacAddressBytesToExtended(byte[] bytes)169     public static byte[] convertShortMacAddressBytesToExtended(byte[] bytes) {
170         if (bytes.length == UWB_DEVICE_SHORT_MAC_ADDRESS_LEN) {
171             return ByteBuffer.allocate(UWB_DEVICE_EXT_MAC_ADDRESS_LEN).put(bytes).array();
172         } else if (bytes.length == UWB_DEVICE_EXT_MAC_ADDRESS_LEN) {
173             return bytes;
174         } else {
175             throw new NumberFormatException("Expected length one of (2, 8) but was "
176                     + bytes.length);
177         }
178     }
179 
180     // Check if the MSB bytes are zeroed out.
isExtendedMSBZeroedOut(byte[] bytes)181     private static boolean isExtendedMSBZeroedOut(byte[] bytes) {
182         for (int i = UWB_DEVICE_SHORT_MAC_ADDRESS_LEN; i < UWB_DEVICE_EXT_MAC_ADDRESS_LEN; i++) {
183             if (bytes[i] != 0) return false;
184         }
185         return true;
186     }
DataTypeConversionUtil()187     private DataTypeConversionUtil() {}
188 }
189