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