• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.car.bluetooth;
18 
19 import android.bluetooth.BluetoothA2dpSink;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothHeadsetClient;
23 import android.bluetooth.BluetoothMapClient;
24 import android.bluetooth.BluetoothPan;
25 import android.bluetooth.BluetoothPbapClient;
26 import android.bluetooth.BluetoothProfile;
27 import android.bluetooth.BluetoothUuid;
28 import android.bluetooth.le.AdvertisingSetCallback;
29 import android.os.ParcelUuid;
30 import android.util.SparseArray;
31 
32 import java.util.HashMap;
33 
34 /** Utils for Bluetooth */
35 public final class BluetoothUtils {
BluetoothUtils()36     private BluetoothUtils() {
37         throw new UnsupportedOperationException();
38     }
39 
40     public static final String A2DP_SINK_CONNECTION_STATE_CHANGED =
41             BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED;
42     public static final String HFP_CLIENT_CONNECTION_STATE_CHANGED =
43             BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED;
44     public static final String MAP_CLIENT_CONNECTION_STATE_CHANGED =
45             BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
46     public static final String PAN_CONNECTION_STATE_CHANGED =
47             BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
48     public static final String PBAP_CLIENT_CONNECTION_STATE_CHANGED =
49             BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED;
50 
51     /*
52      * Maps of types and status to human readable strings
53      */
54 
55     private static final SparseArray<String> sAdapterStates = new SparseArray<String>(4);
56     private static final SparseArray<String> sBondStates = new SparseArray<String>(3);
57     private static final SparseArray<String> sConnectionStates = new SparseArray<String>(4);
58     private static final SparseArray<String> sScanModes = new SparseArray<String>(3);
59     private static final SparseArray<String> sAdvertiseCallbackStatuses =
60             new SparseArray<String>(3);
61     private static final SparseArray<String> sProfileNames = new SparseArray<String>(6);
62     private static final HashMap<String, Integer> sProfileActions = new HashMap<String, Integer>(5);
63     static {
64         // Bluetooth Adapter states
sAdapterStates.put(BluetoothAdapter.STATE_ON, "On")65         sAdapterStates.put(BluetoothAdapter.STATE_ON, "On");
sAdapterStates.put(BluetoothAdapter.STATE_OFF, "Off")66         sAdapterStates.put(BluetoothAdapter.STATE_OFF, "Off");
sAdapterStates.put(BluetoothAdapter.STATE_TURNING_ON, "Turning On")67         sAdapterStates.put(BluetoothAdapter.STATE_TURNING_ON, "Turning On");
sAdapterStates.put(BluetoothAdapter.STATE_TURNING_OFF, "Turning Off")68         sAdapterStates.put(BluetoothAdapter.STATE_TURNING_OFF, "Turning Off");
sAdapterStates.put(BluetoothAdapter.ERROR, "Error")69         sAdapterStates.put(BluetoothAdapter.ERROR, "Error");
70 
71         // Device Bonding states
sBondStates.put(BluetoothDevice.BOND_BONDED, "Bonded")72         sBondStates.put(BluetoothDevice.BOND_BONDED, "Bonded");
sBondStates.put(BluetoothDevice.BOND_BONDING, "Bonding")73         sBondStates.put(BluetoothDevice.BOND_BONDING, "Bonding");
sBondStates.put(BluetoothDevice.BOND_NONE, "Unbonded")74         sBondStates.put(BluetoothDevice.BOND_NONE, "Unbonded");
75 
76         // Device and Profile Connection states
sConnectionStates.put(BluetoothAdapter.STATE_CONNECTED, "Connected")77         sConnectionStates.put(BluetoothAdapter.STATE_CONNECTED, "Connected");
sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTED, "Disconnected")78         sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTED, "Disconnected");
sConnectionStates.put(BluetoothAdapter.STATE_CONNECTING, "Connecting")79         sConnectionStates.put(BluetoothAdapter.STATE_CONNECTING, "Connecting");
sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTING, "Disconnecting")80         sConnectionStates.put(BluetoothAdapter.STATE_DISCONNECTING, "Disconnecting");
81 
82         // Scan Mode Names
sScanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, "Connectable/Discoverable")83         sScanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
84                 "Connectable/Discoverable");
sScanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE, "Connectable")85         sScanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE, "Connectable");
sScanModes.put(BluetoothAdapter.SCAN_MODE_NONE, "None")86         sScanModes.put(BluetoothAdapter.SCAN_MODE_NONE, "None");
sScanModes.put(BluetoothAdapter.ERROR, "Error")87         sScanModes.put(BluetoothAdapter.ERROR, "Error");
88 
89         // Advertising Callback Status Codes
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_ALREADY_STARTED, "ADVERTISE_FAILED_ALREADY_STARTED")90         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_ALREADY_STARTED,
91                 "ADVERTISE_FAILED_ALREADY_STARTED");
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_DATA_TOO_LARGE, "ADVERTISE_FAILED_DATA_TOO_LARGE")92         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_DATA_TOO_LARGE,
93                 "ADVERTISE_FAILED_DATA_TOO_LARGE");
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED, "ADVERTISE_FAILED_FEATURE_UNSUPPORTED")94         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED,
95                 "ADVERTISE_FAILED_FEATURE_UNSUPPORTED");
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR, "ADVERTISE_FAILED_INTERNAL_ERROR")96         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR,
97                 "ADVERTISE_FAILED_INTERNAL_ERROR");
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS, "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS")98         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS,
99                 "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS");
sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_SUCCESS, "ADVERTISE_SUCCESS")100         sAdvertiseCallbackStatuses.put(AdvertisingSetCallback.ADVERTISE_SUCCESS,
101                 "ADVERTISE_SUCCESS");
102 
103         // Profile Names
sProfileNames.put(BluetoothProfile.PAN, "PAN")104         sProfileNames.put(BluetoothProfile.PAN, "PAN");
sProfileNames.put(BluetoothProfile.A2DP_SINK, "A2DP Sink")105         sProfileNames.put(BluetoothProfile.A2DP_SINK, "A2DP Sink");
sProfileNames.put(BluetoothProfile.AVRCP_CONTROLLER, "AVRCP Controller")106         sProfileNames.put(BluetoothProfile.AVRCP_CONTROLLER, "AVRCP Controller");
sProfileNames.put(BluetoothProfile.HEADSET_CLIENT, "HFP Client")107         sProfileNames.put(BluetoothProfile.HEADSET_CLIENT, "HFP Client");
sProfileNames.put(BluetoothProfile.PBAP_CLIENT, "PBAP Client")108         sProfileNames.put(BluetoothProfile.PBAP_CLIENT, "PBAP Client");
sProfileNames.put(BluetoothProfile.MAP_CLIENT, "MAP Client")109         sProfileNames.put(BluetoothProfile.MAP_CLIENT, "MAP Client");
110 
111         // Profile actions to ints
sProfileActions.put(A2DP_SINK_CONNECTION_STATE_CHANGED, BluetoothProfile.A2DP_SINK)112         sProfileActions.put(A2DP_SINK_CONNECTION_STATE_CHANGED, BluetoothProfile.A2DP_SINK);
sProfileActions.put(HFP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.HEADSET_CLIENT)113         sProfileActions.put(HFP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.HEADSET_CLIENT);
sProfileActions.put(MAP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.MAP_CLIENT)114         sProfileActions.put(MAP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.MAP_CLIENT);
sProfileActions.put(PAN_CONNECTION_STATE_CHANGED, BluetoothProfile.PAN)115         sProfileActions.put(PAN_CONNECTION_STATE_CHANGED, BluetoothProfile.PAN);
sProfileActions.put(PBAP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.PBAP_CLIENT)116         sProfileActions.put(PBAP_CLIENT_CONNECTION_STATE_CHANGED, BluetoothProfile.PBAP_CLIENT);
117     }
118 
getBytesFromAddress(String address)119     static byte[] getBytesFromAddress(String address) {
120         int i, j = 0;
121         byte[] output = new byte[6]; // 6 byte Bluetooth Address
122 
123         for (i = 0; i < address.length(); i++) {
124             if (address.charAt(i) != ':') {
125                 output[j] = (byte) Integer.parseInt(address.substring(i, i + 2), 16 /* base 16 */);
126                 j++;
127                 i++;
128             }
129         }
130         return output;
131     }
132 
133 
getDeviceDebugInfo(BluetoothDevice device)134     static String getDeviceDebugInfo(BluetoothDevice device) {
135         if (device == null) {
136             return "(null)";
137         }
138         return "(name = " + device.getName() + ", addr = " + device.getAddress() + ")";
139     }
140 
getProfileName(int profile)141     static String getProfileName(int profile) {
142         String name = sProfileNames.get(profile, "Unknown");
143         return "(" + profile + ") " + name;
144     }
145 
getConnectionStateName(int state)146     static String getConnectionStateName(int state) {
147         String name = sConnectionStates.get(state, "Unknown");
148         return "(" + state + ") " + name;
149     }
150 
getBondStateName(int state)151     static String getBondStateName(int state) {
152         String name = sBondStates.get(state, "Unknown");
153         return "(" + state + ") " + name;
154     }
155 
getAdapterStateName(int state)156     static String getAdapterStateName(int state) {
157         String name = sAdapterStates.get(state, "Unknown");
158         return "(" + state + ") " + name;
159     }
160 
getScanModeName(int mode)161     static String getScanModeName(int mode) {
162         String name = sScanModes.get(mode, "Unknown");
163         return "(" + mode + ") " + name;
164     }
165 
getAdvertisingCallbackStatusName(int status)166     static String getAdvertisingCallbackStatusName(int status) {
167         String name = sAdvertiseCallbackStatuses.get(status, "Unknown");
168         return "(" + status + ") " + name;
169     }
170 
getConnectionPolicyName(int priority)171     static String getConnectionPolicyName(int priority) {
172         String name = "";
173         switch (priority) {
174             case BluetoothProfile.CONNECTION_POLICY_ALLOWED:
175                 name = "CONNECTION_POLICY_ALLOWED";
176                 break;
177             case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN:
178                 name = "CONNECTION_POLICY_FORBIDDEN";
179                 break;
180             case BluetoothProfile.CONNECTION_POLICY_UNKNOWN:
181                 name = "CONNECTION_POLICY_UNKNOWN";
182                 break;
183             default:
184                 name = "Unknown";
185                 break;
186         }
187         return "(" + priority + ") " + name;
188     }
189 
getProfileFromConnectionAction(String action)190     static int getProfileFromConnectionAction(String action) {
191         Integer profile = sProfileActions.get(action);
192         return profile != null ? profile.intValue() : -1;
193     }
194 
isProfileSupported(BluetoothDevice device, int profile)195     static boolean isProfileSupported(BluetoothDevice device, int profile) {
196         if (device == null) return false;
197 
198         ParcelUuid[] uuids = device.getUuids();
199 
200         if (uuids == null || uuids.length == 0) {
201             return false;
202         }
203         switch (profile) {
204             case BluetoothProfile.A2DP_SINK:
205                 return BluetoothUuid.containsAnyUuid(uuids,
206                         new ParcelUuid[]{BluetoothUuid.ADV_AUDIO_DIST, BluetoothUuid.A2DP_SOURCE});
207             case BluetoothProfile.HEADSET_CLIENT:
208                 return BluetoothUuid.containsAnyUuid(uuids,
209                         new ParcelUuid[]{BluetoothUuid.HFP_AG, BluetoothUuid.HSP_AG});
210             case BluetoothProfile.MAP_CLIENT:
211                 return BluetoothUuid.containsAnyUuid(uuids,
212                         new ParcelUuid[]{BluetoothUuid.MAS});
213             case BluetoothProfile.PAN:
214                 return BluetoothUuid.containsAnyUuid(uuids,
215                         new ParcelUuid[]{BluetoothUuid.PANU});
216             case BluetoothProfile.PBAP_CLIENT:
217                 return BluetoothUuid.containsAnyUuid(uuids,
218                         new ParcelUuid[]{BluetoothUuid.PBAP_PSE});
219             default:
220                 return false;
221         }
222     }
223 
isAProfileAction(String action)224     static boolean isAProfileAction(String action) {
225         return sProfileActions.containsKey(action);
226     }
227 
getManagedProfilesIds()228     static int[] getManagedProfilesIds() {
229         int[] profileIds = new int[sProfileActions.size()];
230         int i = 0;
231         for (HashMap.Entry record : sProfileActions.entrySet()) {
232             profileIds[i] = ((Integer) record.getValue()).intValue();
233             i += 1;
234         }
235         return profileIds;
236     }
237 }
238