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