1 /* 2 * Copyright 2020 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.le_audio; 19 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothLeAudioCodecConfig; 22 import android.bluetooth.BluetoothLeBroadcastMetadata; 23 24 import java.util.List; 25 26 /** 27 * Stack event sent via a callback from JNI to Java, or generated internally by the LeAudio State 28 * Machine. 29 */ 30 public class LeAudioStackEvent { 31 // Event types for STACK_EVENT message (coming from native in bt_le_audio.h) 32 private static final int EVENT_TYPE_NONE = 0; 33 public static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 34 public static final int EVENT_TYPE_GROUP_STATUS_CHANGED = 2; 35 public static final int EVENT_TYPE_GROUP_NODE_STATUS_CHANGED = 3; 36 public static final int EVENT_TYPE_AUDIO_CONF_CHANGED = 4; 37 public static final int EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE = 5; 38 public static final int EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED = 6; 39 public static final int EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED = 7; 40 public static final int EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED = 8; 41 public static final int EVENT_TYPE_NATIVE_INITIALIZED = 9; 42 public static final int EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION = 10; 43 public static final int EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION = 11; 44 public static final int EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS = 12; 45 public static final int EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED = 13; 46 // -------- DO NOT PUT ANY NEW UNICAST EVENTS BELOW THIS LINE------------- 47 public static final int EVENT_TYPE_UNICAST_MAX = 14; 48 49 // Broadcast related events 50 public static final int EVENT_TYPE_BROADCAST_CREATED = EVENT_TYPE_UNICAST_MAX + 1; 51 public static final int EVENT_TYPE_BROADCAST_DESTROYED = EVENT_TYPE_UNICAST_MAX + 2; 52 public static final int EVENT_TYPE_BROADCAST_STATE = EVENT_TYPE_UNICAST_MAX + 3; 53 public static final int EVENT_TYPE_BROADCAST_METADATA_CHANGED = EVENT_TYPE_UNICAST_MAX + 4; 54 55 // Do not modify without updating the HAL bt_le_audio.h files. 56 // Match up with GroupStatus enum of bt_le_audio.h 57 static final int CONNECTION_STATE_DISCONNECTED = 0; 58 static final int CONNECTION_STATE_CONNECTING = 1; 59 static final int CONNECTION_STATE_CONNECTED = 2; 60 static final int CONNECTION_STATE_DISCONNECTING = 3; 61 62 // Health based recommendation 63 static final int HEALTH_RECOMMENDATION_ACTION_NONE = 0; 64 static final int HEALTH_RECOMMENDATION_ACTION_DISABLE = 1; 65 static final int HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING = 2; 66 static final int HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP = 3; 67 68 static final int GROUP_STATUS_INACTIVE = 0; 69 static final int GROUP_STATUS_ACTIVE = 1; 70 static final int GROUP_STATUS_TURNED_IDLE_DURING_CALL = 2; 71 72 static final int GROUP_NODE_ADDED = 1; 73 static final int GROUP_NODE_REMOVED = 2; 74 75 // Do not modify without updating the HAL bt_le_audio.h files. 76 // Match up with BroadcastState enum of bt_le_audio.h 77 static final int BROADCAST_STATE_STOPPED = 0; 78 static final int BROADCAST_STATE_CONFIGURING = 1; 79 static final int BROADCAST_STATE_PAUSED = 2; 80 static final int BROADCAST_STATE_STOPPING = 3; 81 static final int BROADCAST_STATE_STREAMING = 4; 82 83 // Do not modify without updating the HAL bt_le_audio.h files. 84 // Match up with UnicastMonitorModeStatus enum of bt_le_audio.h 85 static final int STATUS_LOCAL_STREAM_REQUESTED = 0; 86 static final int STATUS_LOCAL_STREAM_STREAMING = 1; 87 static final int STATUS_LOCAL_STREAM_SUSPENDED = 2; 88 static final int STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE = 3; 89 90 // Do not modify without updating le_audio_types.h 91 // Match up with defines of le_audio_types.h 92 static final int DIRECTION_SINK = 1; 93 static final int DIRECTION_SOURCE = 2; 94 95 static final int GROUP_STREAM_STATUS_IDLE = 0; 96 static final int GROUP_STREAM_STATUS_STREAMING = 1; 97 98 public int type = EVENT_TYPE_NONE; 99 public BluetoothDevice device; 100 public int valueInt1 = 0; 101 public int valueInt2 = 0; 102 public int valueInt3 = 0; 103 public int valueInt4 = 0; 104 public int valueInt5 = 0; 105 public boolean valueBool1 = false; 106 public BluetoothLeAudioCodecConfig valueCodec1; 107 public BluetoothLeAudioCodecConfig valueCodec2; 108 public List<BluetoothLeAudioCodecConfig> valueCodecList1; 109 public List<BluetoothLeAudioCodecConfig> valueCodecList2; 110 public BluetoothLeBroadcastMetadata broadcastMetadata; 111 LeAudioStackEvent(int type)112 LeAudioStackEvent(int type) { 113 this.type = type; 114 } 115 116 @Override toString()117 public String toString() { 118 // event dump 119 StringBuilder result = new StringBuilder(); 120 result.append("LeAudioStackEvent {type:" + eventTypeToString(type)); 121 result.append(", device:" + device); 122 123 if (type != EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED) { 124 result.append(", value1:" + eventTypeValue1ToString(type, valueInt1)); 125 result.append(", value2:" + eventTypeValue2ToString(type, valueInt2)); 126 result.append(", value3:" + eventTypeValue3ToString(type, valueInt3)); 127 result.append(", value4:" + eventTypeValue4ToString(type, valueInt4)); 128 result.append(", value5:" + eventTypeValue5ToString(type, valueInt5)); 129 result.append(", valueBool1:" + eventTypeValueBool1ToString(type, valueBool1)); 130 } else { 131 result.append( 132 ", valueCodecList1:" + eventTypeValueCodecList1ToString(type, valueCodecList1)); 133 result.append( 134 ", valueCodecList2:" + eventTypeValueCodecList2ToString(type, valueCodecList2)); 135 } 136 137 if (type == EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED) { 138 result.append(", valueCodec1:" + eventTypeValueCodec1ToString(type, valueCodec1)); 139 result.append(", valueCodec2:" + eventTypeValueCodec2ToString(type, valueCodec2)); 140 } 141 142 if (type == EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED) { 143 result.append( 144 ", valueCodecList1:" + eventTypeValueCodecList1ToString(type, valueCodecList1)); 145 result.append( 146 ", valueCodecList2:" + eventTypeValueCodecList2ToString(type, valueCodecList2)); 147 } 148 149 if (type == EVENT_TYPE_BROADCAST_METADATA_CHANGED) { 150 result.append( 151 ", broadcastMetadata:" 152 + eventTypeValueBroadcastMetadataToString(broadcastMetadata)); 153 } 154 result.append("}"); 155 return result.toString(); 156 } 157 eventTypeToString(int type)158 private static String eventTypeToString(int type) { 159 switch (type) { 160 case EVENT_TYPE_NONE: 161 return "EVENT_TYPE_NONE"; 162 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 163 return "EVENT_TYPE_CONNECTION_STATE_CHANGED"; 164 case EVENT_TYPE_GROUP_STATUS_CHANGED: 165 return "EVENT_TYPE_GROUP_STATUS_CHANGED"; 166 case EVENT_TYPE_GROUP_NODE_STATUS_CHANGED: 167 return "EVENT_TYPE_GROUP_NODE_STATUS_CHANGED"; 168 case EVENT_TYPE_AUDIO_CONF_CHANGED: 169 return "EVENT_TYPE_AUDIO_CONF_CHANGED"; 170 case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: 171 return "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE"; 172 case EVENT_TYPE_BROADCAST_CREATED: 173 return "EVENT_TYPE_BROADCAST_CREATED"; 174 case EVENT_TYPE_BROADCAST_DESTROYED: 175 return "EVENT_TYPE_BROADCAST_DESTROYED"; 176 case EVENT_TYPE_BROADCAST_STATE: 177 return "EVENT_TYPE_BROADCAST_STATE"; 178 case EVENT_TYPE_BROADCAST_METADATA_CHANGED: 179 return "EVENT_TYPE_BROADCAST_METADATA_CHANGED"; 180 case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED: 181 return "EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED"; 182 case EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED: 183 return "EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED"; 184 case EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED: 185 return "EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED"; 186 case EVENT_TYPE_NATIVE_INITIALIZED: 187 return "EVENT_TYPE_NATIVE_INITIALIZED"; 188 case EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION: 189 return "EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION"; 190 case EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION: 191 return "EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION"; 192 case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS: 193 return "EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS"; 194 case EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED: 195 return "EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED"; 196 default: 197 return "EVENT_TYPE_UNKNOWN:" + type; 198 } 199 } 200 eventTypeValue1ToString(int type, int value)201 private static String eventTypeValue1ToString(int type, int value) { 202 switch (type) { 203 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 204 switch (value) { 205 case CONNECTION_STATE_DISCONNECTED: 206 return "CONNECTION_STATE_DISCONNECTED"; 207 case CONNECTION_STATE_CONNECTING: 208 return "CONNECTION_STATE_CONNECTING"; 209 case CONNECTION_STATE_CONNECTED: 210 return "CONNECTION_STATE_CONNECTED"; 211 case CONNECTION_STATE_DISCONNECTING: 212 return "CONNECTION_STATE_DISCONNECTING"; 213 default: 214 return "UNKNOWN"; 215 } 216 case EVENT_TYPE_GROUP_NODE_STATUS_CHANGED: 217 // same as EVENT_TYPE_GROUP_STATUS_CHANGED 218 case EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED: 219 case EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED: 220 // same as EVENT_TYPE_GROUP_STATUS_CHANGED 221 case EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED: 222 // same as EVENT_TYPE_GROUP_STATUS_CHANGED 223 case EVENT_TYPE_GROUP_STATUS_CHANGED: 224 return "{group_id:" + Integer.toString(value) + "}"; 225 case EVENT_TYPE_AUDIO_CONF_CHANGED: 226 // FIXME: It should have proper direction names here 227 return "{direction:" + value + "}"; 228 case EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE: 229 return "{sink_audio_location:" + value + "}"; 230 case EVENT_TYPE_BROADCAST_CREATED: 231 // same as EVENT_TYPE_BROADCAST_STATE 232 case EVENT_TYPE_BROADCAST_DESTROYED: 233 // same as EVENT_TYPE_BROADCAST_STATE 234 case EVENT_TYPE_BROADCAST_METADATA_CHANGED: 235 // same as EVENT_TYPE_BROADCAST_STATE 236 case EVENT_TYPE_BROADCAST_STATE: 237 return "{broadcastId:" + value + "}"; 238 case EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION: 239 return "{group_id: " + value + "}"; 240 case EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION: 241 switch (value) { 242 case HEALTH_RECOMMENDATION_ACTION_DISABLE: 243 return "ACTION_DISABLE"; 244 case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: 245 return "ACTION_CONSIDER_DISABLING"; 246 case HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: 247 return "ACTION_INACTIVATE_GROUP"; 248 default: 249 return "UNKNOWN"; 250 } 251 case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS: 252 switch (value) { 253 case DIRECTION_SINK: 254 return "DIRECTION_SINK"; 255 case DIRECTION_SOURCE: 256 return "DIRECTION_SOURCE"; 257 default: 258 return "UNKNOWN"; 259 } 260 default: 261 break; 262 } 263 return Integer.toString(value); 264 } 265 eventTypeValue2ToString(int type, int value)266 private static String eventTypeValue2ToString(int type, int value) { 267 switch (type) { 268 case EVENT_TYPE_GROUP_STATUS_CHANGED: 269 switch (value) { 270 case GROUP_STATUS_ACTIVE: 271 return "GROUP_STATUS_ACTIVE"; 272 case GROUP_STATUS_INACTIVE: 273 return "GROUP_STATUS_INACTIVE"; 274 case GROUP_STATUS_TURNED_IDLE_DURING_CALL: 275 return "GROUP_STATUS_TURNED_IDLE_DURING_CALL"; 276 default: 277 break; 278 } 279 break; 280 case EVENT_TYPE_GROUP_NODE_STATUS_CHANGED: 281 switch (value) { 282 case GROUP_NODE_ADDED: 283 return "GROUP_NODE_ADDED"; 284 case GROUP_NODE_REMOVED: 285 return "GROUP_NODE_REMOVED"; 286 default: 287 return "UNKNOWN"; 288 } 289 case EVENT_TYPE_AUDIO_CONF_CHANGED: 290 return "{group_id:" + Integer.toString(value) + "}"; 291 case EVENT_TYPE_BROADCAST_STATE: 292 return "{state:" + broadcastStateToString(value) + "}"; 293 case EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION: 294 switch (value) { 295 case HEALTH_RECOMMENDATION_ACTION_DISABLE: 296 return "ACTION_DISABLE"; 297 case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: 298 return "ACTION_CONSIDER_DISABLING"; 299 case HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: 300 return "ACTION_INACTIVATE_GROUP"; 301 default: 302 return "UNKNOWN"; 303 } 304 case EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS: 305 switch (value) { 306 case STATUS_LOCAL_STREAM_REQUESTED: 307 return "STATUS_LOCAL_STREAM_REQUESTED"; 308 case STATUS_LOCAL_STREAM_STREAMING: 309 return "STATUS_LOCAL_STREAM_STREAMING"; 310 case STATUS_LOCAL_STREAM_SUSPENDED: 311 return "STATUS_LOCAL_STREAM_SUSPENDED"; 312 case STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE: 313 return "STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE"; 314 default: 315 return "UNKNOWN"; 316 } 317 case EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED: 318 switch (value) { 319 case GROUP_STREAM_STATUS_IDLE: 320 return "GROUP_STREAM_STATUS_IDLE"; 321 case GROUP_STREAM_STATUS_STREAMING: 322 return "GROUP_STREAM_STATUS_STREAMING"; 323 default: 324 return "UNKNOWN"; 325 } 326 default: 327 break; 328 } 329 return Integer.toString(value); 330 } 331 eventTypeValue3ToString(int type, int value)332 private static String eventTypeValue3ToString(int type, int value) { 333 switch (type) { 334 case EVENT_TYPE_AUDIO_CONF_CHANGED: 335 // FIXME: It should have proper location names here 336 return "{snk_audio_loc:" + value + "}"; 337 default: 338 break; 339 } 340 return Integer.toString(value); 341 } 342 eventTypeValue4ToString(int type, int value)343 private static String eventTypeValue4ToString(int type, int value) { 344 switch (type) { 345 case EVENT_TYPE_AUDIO_CONF_CHANGED: 346 // FIXME: It should have proper location names here 347 return "{src_audio_loc:" + value + "}"; 348 default: 349 break; 350 } 351 return Integer.toString(value); 352 } 353 eventTypeValue5ToString(int type, int value)354 private static String eventTypeValue5ToString(int type, int value) { 355 switch (type) { 356 case EVENT_TYPE_AUDIO_CONF_CHANGED: 357 return "{available_contexts:" + Integer.toBinaryString(value) + "}"; 358 default: 359 break; 360 } 361 return Integer.toString(value); 362 } 363 eventTypeValueBool1ToString(int type, boolean value)364 private static String eventTypeValueBool1ToString(int type, boolean value) { 365 switch (type) { 366 case EVENT_TYPE_BROADCAST_CREATED: 367 return "{success:" + value + "}"; 368 default: 369 return "<unused>"; 370 } 371 } 372 eventTypeValueCodec1ToString( int type, BluetoothLeAudioCodecConfig value)373 private static String eventTypeValueCodec1ToString( 374 int type, BluetoothLeAudioCodecConfig value) { 375 switch (type) { 376 case EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED: 377 return "{input codec =" + value + "}"; 378 default: 379 return "<unused>"; 380 } 381 } 382 eventTypeValueCodec2ToString( int type, BluetoothLeAudioCodecConfig value)383 private static String eventTypeValueCodec2ToString( 384 int type, BluetoothLeAudioCodecConfig value) { 385 switch (type) { 386 case EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED: 387 return "{output codec =" + value + "}"; 388 default: 389 return "<unused>"; 390 } 391 } 392 eventTypeValueCodecList1ToString( int type, List<BluetoothLeAudioCodecConfig> value)393 private static String eventTypeValueCodecList1ToString( 394 int type, List<BluetoothLeAudioCodecConfig> value) { 395 String valueStr = ""; 396 switch (type) { 397 case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED: 398 for (BluetoothLeAudioCodecConfig n : value) { 399 valueStr = valueStr.concat(n.toString() + "\n"); 400 } 401 return "{input local capa codec = \n" + valueStr + "}"; 402 case EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED: 403 for (BluetoothLeAudioCodecConfig n : value) { 404 valueStr = valueStr.concat(n.toString() + "\n"); 405 } 406 return "{input selectable codec =" + valueStr + "}"; 407 default: 408 return "<unused>"; 409 } 410 } 411 eventTypeValueCodecList2ToString( int type, List<BluetoothLeAudioCodecConfig> value)412 private static String eventTypeValueCodecList2ToString( 413 int type, List<BluetoothLeAudioCodecConfig> value) { 414 String valueStr = ""; 415 switch (type) { 416 case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED: 417 for (BluetoothLeAudioCodecConfig n : value) { 418 valueStr = valueStr.concat(n.toString() + "\n"); 419 } 420 return "{output local capa codec = \n" + valueStr + "}"; 421 case EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED: 422 for (BluetoothLeAudioCodecConfig n : value) { 423 valueStr = valueStr.concat(n.toString() + "\n"); 424 } 425 return "{output selectable codec =" + valueStr + "}"; 426 default: 427 return "<unused>"; 428 } 429 } 430 broadcastStateToString(int state)431 private static String broadcastStateToString(int state) { 432 switch (state) { 433 case BROADCAST_STATE_STOPPED: 434 return "BROADCAST_STATE_STOPPED"; 435 case BROADCAST_STATE_CONFIGURING: 436 return "BROADCAST_STATE_CONFIGURING"; 437 case BROADCAST_STATE_PAUSED: 438 return "BROADCAST_STATE_PAUSED"; 439 case BROADCAST_STATE_STOPPING: 440 return "BROADCAST_STATE_STOPPING"; 441 case BROADCAST_STATE_STREAMING: 442 return "BROADCAST_STATE_STREAMING"; 443 default: 444 return "UNKNOWN"; 445 } 446 } 447 eventTypeValueBroadcastMetadataToString( BluetoothLeBroadcastMetadata meta)448 private static String eventTypeValueBroadcastMetadataToString( 449 BluetoothLeBroadcastMetadata meta) { 450 return meta.toString(); 451 } 452 encodeHexString(byte[] pduData)453 protected static String encodeHexString(byte[] pduData) { 454 StringBuilder out = new StringBuilder(pduData.length * 2); 455 for (int i = 0; i < pduData.length; i++) { 456 // MS-nibble first 457 out.append(Integer.toString((pduData[i] >> 4) & 0x0f, 16)); 458 out.append(Integer.toString(pduData[i] & 0x0f, 16)); 459 } 460 return out.toString(); 461 } 462 } 463