• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car;
17 
18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SuppressLint;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothProfile;
26 import android.util.SparseArray;
27 
28 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
29 
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.nio.ByteBuffer;
33 import java.nio.ByteOrder;
34 import java.util.UUID;
35 import java.util.concurrent.ThreadLocalRandom;
36 
37 /**
38  * Some potentially useful static methods.
39  */
40 public class Utils {
41     static final Boolean DBG = false;
42     // https://developer.android.com/reference/java/util/UUID
43     private static final int UUID_LENGTH = 16;
44 
45 
46     /*
47      * Maps of types and status to human readable strings
48      */
49 
50     private static final SparseArray<String> sAdapterStates = new SparseArray<String>();
51     private static final SparseArray<String> sBondStates = new SparseArray<String>();
52     private static final SparseArray<String> sConnectionStates = new SparseArray<String>();
53     private static final SparseArray<String> sProfileNames = new SparseArray<String>();
54     static {
55 
56         // Bluetooth Adapter states
sAdapterStates.put(BluetoothAdapter.STATE_ON, "On")57         sAdapterStates.put(BluetoothAdapter.STATE_ON, "On");
sAdapterStates.put(BluetoothAdapter.STATE_OFF, "Off")58         sAdapterStates.put(BluetoothAdapter.STATE_OFF, "Off");
sAdapterStates.put(BluetoothAdapter.STATE_TURNING_ON, "Turning On")59         sAdapterStates.put(BluetoothAdapter.STATE_TURNING_ON, "Turning On");
sAdapterStates.put(BluetoothAdapter.STATE_TURNING_OFF, "Turning Off")60         sAdapterStates.put(BluetoothAdapter.STATE_TURNING_OFF, "Turning Off");
61 
62         // Device Bonding states
sBondStates.put(BluetoothDevice.BOND_BONDED, "Bonded")63         sBondStates.put(BluetoothDevice.BOND_BONDED, "Bonded");
sBondStates.put(BluetoothDevice.BOND_BONDING, "Bonding")64         sBondStates.put(BluetoothDevice.BOND_BONDING, "Bonding");
sBondStates.put(BluetoothDevice.BOND_NONE, "Unbonded")65         sBondStates.put(BluetoothDevice.BOND_NONE, "Unbonded");
66 
67         // Device and Profile Connection states
sConnectionStates.put(BluetoothAdapter.STATE_CONNECTED, "Connected")68         sConnectionStates.put(BluetoothAdapter.STATE_CONNECTED, "Connected");
sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTED, "Disconnected")69         sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTED, "Disconnected");
sConnectionStates.put(BluetoothAdapter.STATE_CONNECTING, "Connecting")70         sConnectionStates.put(BluetoothAdapter.STATE_CONNECTING, "Connecting");
sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTING, "Disconnecting")71         sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTING, "Disconnecting");
72 
73         // Profile Names
sProfileNames.put(BluetoothProfile.HEADSET, "HFP Server")74         sProfileNames.put(BluetoothProfile.HEADSET, "HFP Server");
sProfileNames.put(BluetoothProfile.A2DP, "A2DP Source")75         sProfileNames.put(BluetoothProfile.A2DP, "A2DP Source");
sProfileNames.put(BluetoothProfile.HEALTH, "HDP")76         sProfileNames.put(BluetoothProfile.HEALTH, "HDP");
sProfileNames.put(BluetoothProfile.HID_HOST, "HID Host")77         sProfileNames.put(BluetoothProfile.HID_HOST, "HID Host");
sProfileNames.put(BluetoothProfile.PAN, "PAN")78         sProfileNames.put(BluetoothProfile.PAN, "PAN");
sProfileNames.put(BluetoothProfile.PBAP, "PBAP Server")79         sProfileNames.put(BluetoothProfile.PBAP, "PBAP Server");
sProfileNames.put(BluetoothProfile.GATT, "GATT Client")80         sProfileNames.put(BluetoothProfile.GATT, "GATT Client");
sProfileNames.put(BluetoothProfile.GATT_SERVER, "GATT Server")81         sProfileNames.put(BluetoothProfile.GATT_SERVER, "GATT Server");
sProfileNames.put(BluetoothProfile.MAP, "MAP Server")82         sProfileNames.put(BluetoothProfile.MAP, "MAP Server");
sProfileNames.put(BluetoothProfile.SAP, "SAP")83         sProfileNames.put(BluetoothProfile.SAP, "SAP");
sProfileNames.put(BluetoothProfile.A2DP_SINK, "A2DP Sink")84         sProfileNames.put(BluetoothProfile.A2DP_SINK, "A2DP Sink");
sProfileNames.put(BluetoothProfile.AVRCP_CONTROLLER, "AVRCP Controller")85         sProfileNames.put(BluetoothProfile.AVRCP_CONTROLLER, "AVRCP Controller");
sProfileNames.put(BluetoothProfile.AVRCP, "AVRCP Target")86         sProfileNames.put(BluetoothProfile.AVRCP, "AVRCP Target");
sProfileNames.put(BluetoothProfile.HEADSET_CLIENT, "HFP Client")87         sProfileNames.put(BluetoothProfile.HEADSET_CLIENT, "HFP Client");
sProfileNames.put(BluetoothProfile.PBAP_CLIENT, "PBAP Client")88         sProfileNames.put(BluetoothProfile.PBAP_CLIENT, "PBAP Client");
sProfileNames.put(BluetoothProfile.MAP_CLIENT, "MAP Client")89         sProfileNames.put(BluetoothProfile.MAP_CLIENT, "MAP Client");
sProfileNames.put(BluetoothProfile.HID_DEVICE, "HID Device")90         sProfileNames.put(BluetoothProfile.HID_DEVICE, "HID Device");
sProfileNames.put(BluetoothProfile.OPP, "OPP")91         sProfileNames.put(BluetoothProfile.OPP, "OPP");
sProfileNames.put(BluetoothProfile.HEARING_AID, "Hearing Aid")92         sProfileNames.put(BluetoothProfile.HEARING_AID, "Hearing Aid");
93     }
94 
getDeviceDebugInfo(BluetoothDevice device)95     static String getDeviceDebugInfo(BluetoothDevice device) {
96         if (device == null) {
97             return "(null)";
98         }
99         return "(name = " + device.getName() + ", addr = " + device.getAddress() + ")";
100     }
101 
getProfileName(int profile)102     static String getProfileName(int profile) {
103         String name = sProfileNames.get(profile, "Unknown");
104         return "(" + profile + ") " + name;
105     }
106 
getConnectionStateName(int state)107     static String getConnectionStateName(int state) {
108         String name = sConnectionStates.get(state, "Unknown");
109         return "(" + state + ") " + name;
110     }
111 
getBondStateName(int state)112     static String getBondStateName(int state) {
113         String name = sBondStates.get(state, "Unknown");
114         return "(" + state + ") " + name;
115     }
116 
getAdapterStateName(int state)117     static String getAdapterStateName(int state) {
118         String name = sAdapterStates.get(state, "Unknown");
119         return "(" + state + ") " + name;
120     }
121 
getProfilePriorityName(int priority)122     static String getProfilePriorityName(int priority) {
123         String name = "";
124         if (priority >= BluetoothProfile.PRIORITY_AUTO_CONNECT) {
125             name = "PRIORITY_AUTO_CONNECT";
126         } else if (priority >= BluetoothProfile.PRIORITY_ON) {
127             name = "PRIORITY_ON";
128         } else if (priority >= BluetoothProfile.PRIORITY_OFF) {
129             name = "PRIORITY_OFF";
130         } else {
131             name = "PRIORITY_UNDEFINED";
132         }
133         return "(" + priority + ") " + name;
134     }
135 
136     /**
137      * An utility class to dump transition events across different car service components.
138      * The output will be of the form
139      * <p>
140      * "Time <svc name>: [optional context information] changed from <from state> to <to state>"
141      * This can be used in conjunction with the dump() method to dump this information through
142      * adb shell dumpsys activity service com.android.car
143      * <p>
144      * A specific service in CarService can choose to use a circular buffer of N records to keep
145      * track of the last N transitions.
146      */
147     public static final class TransitionLog {
148         private String mServiceName; // name of the service or tag
149         private Object mFromState; // old state
150         private Object mToState; // new state
151         private long mTimestampMs; // System.currentTimeMillis()
152         private String mExtra; // Additional information as a String
153 
TransitionLog(String name, Object fromState, Object toState, long timestamp, String extra)154         public TransitionLog(String name, Object fromState, Object toState, long timestamp,
155                 String extra) {
156             this(name, fromState, toState, timestamp);
157             mExtra = extra;
158         }
159 
TransitionLog(String name, Object fromState, Object toState, long timeStamp)160         public TransitionLog(String name, Object fromState, Object toState, long timeStamp) {
161             mServiceName = name;
162             mFromState = fromState;
163             mToState = toState;
164             mTimestampMs = timeStamp;
165         }
166 
timeToLog(long timestamp)167         private CharSequence timeToLog(long timestamp) {
168             return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp);
169         }
170 
171         @Override
toString()172         public String toString() {
173             return timeToLog(mTimestampMs) + " " + mServiceName + ": "
174                     + (mExtra != null ? mExtra + " " : "")
175                     + "changed from " + mFromState + " to " + mToState;
176         }
177     }
178 
179     /**
180      * Returns a byte buffer corresponding to the passed long argument.
181      *
182      * @param primitive data to convert format.
183      */
longToBytes(long primitive)184     public static byte[] longToBytes(long primitive) {
185         ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
186         buffer.putLong(primitive);
187         return buffer.array();
188     }
189 
190     /**
191      * Returns a byte buffer corresponding to the passed long argument.
192      *
193      * @param array data to convert format.
194      */
bytesToLong(byte[] array)195     public static long bytesToLong(byte[] array) {
196         ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
197         buffer.put(array);
198         buffer.flip();
199         long value = buffer.getLong();
200         return value;
201     }
202 
203     /**
204      * Returns a String in Hex format that is formed from the bytes in the byte array
205      * Useful for debugging
206      *
207      * @param array the byte array
208      * @return the Hex string version of the input byte array
209      */
byteArrayToHexString(byte[] array)210     public static String byteArrayToHexString(byte[] array) {
211         StringBuilder sb = new StringBuilder(array.length * 2);
212         for (byte b : array) {
213             sb.append(String.format("%02x", b));
214         }
215         return sb.toString();
216     }
217 
218     /**
219      * Convert UUID to Big Endian byte array
220      *
221      * @param uuid UUID to convert
222      * @return the byte array representing the UUID
223      */
224     @NonNull
uuidToBytes(@onNull UUID uuid)225     public static byte[] uuidToBytes(@NonNull UUID uuid) {
226 
227         return ByteBuffer.allocate(UUID_LENGTH)
228                 .order(ByteOrder.BIG_ENDIAN)
229                 .putLong(uuid.getMostSignificantBits())
230                 .putLong(uuid.getLeastSignificantBits())
231                 .array();
232     }
233 
234     /**
235      * Convert Big Endian byte array to UUID
236      *
237      * @param bytes byte array to convert
238      * @return the UUID representing the byte array, or null if not a valid UUID
239      */
240     @Nullable
bytesToUUID(@onNull byte[] bytes)241     public static UUID bytesToUUID(@NonNull byte[] bytes) {
242         if (bytes.length != UUID_LENGTH) {
243             return null;
244         }
245 
246         ByteBuffer buffer = ByteBuffer.wrap(bytes);
247         return new UUID(buffer.getLong(), buffer.getLong());
248     }
249 
250     /**
251      * Generate a random zero-filled string of given length
252      *
253      * @param length of string
254      * @return generated string
255      */
256     @SuppressLint("DefaultLocale")  // Should always have the same format regardless of locale
generateRandomNumberString(int length)257     public static String generateRandomNumberString(int length) {
258         return String.format("%0" + length + "d",
259                 ThreadLocalRandom.current().nextInt((int) Math.pow(10, length)));
260     }
261 
262 
263     /**
264      * Concatentate the given 2 byte arrays
265      *
266      * @param a input array 1
267      * @param b input array 2
268      * @return concatenated array of arrays 1 and 2
269      */
270     @Nullable
concatByteArrays(@ullable byte[] a, @Nullable byte[] b)271     public static byte[] concatByteArrays(@Nullable byte[] a, @Nullable byte[] b) {
272         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
273         try {
274             if (a != null) {
275                 outputStream.write(a);
276             }
277             if (b != null) {
278                 outputStream.write(b);
279             }
280         } catch (IOException e) {
281             return null;
282         }
283         return outputStream.toByteArray();
284     }
285 
286     @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE,
287             details = "private constructor")
Utils()288     private Utils() {
289         throw new UnsupportedOperationException("contains only static methods");
290     }
291 }
292