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