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