1 /* 2 * Copyright 2022 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.bluetooth.bass_client; 18 19 import android.util.Log; 20 import android.util.Pair; 21 22 import java.nio.ByteBuffer; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.HashMap; 26 import java.util.Iterator; 27 import java.util.LinkedHashSet; 28 import java.util.Locale; 29 import java.util.Map; 30 import java.util.MissingResourceException; 31 import java.util.Set; 32 33 /** 34 * Helper class to parse the Broadcast Announcement BASE data 35 */ 36 class BaseData { 37 private static final String TAG = "Bassclient-BaseData"; 38 private static final byte UNKNOWN_CODEC = (byte) 0xFE; 39 private static final int METADATA_LEVEL1 = 1; 40 private static final int METADATA_LEVEL2 = 2; 41 private static final int METADATA_LEVEL3 = 3; 42 private static final int METADATA_PRESENTATIONDELAY_LENGTH = 3; 43 private static final int METADATA_CODEC_LENGTH = 5; 44 private static final int METADATA_UNKNOWN_CODEC_LENGTH = 1; 45 private static final int CODEC_CAPABILITIES_SAMPLE_RATE_TYPE = 1; 46 private static final int CODEC_CAPABILITIES_FRAME_DURATION_TYPE = 2; 47 private static final int CODEC_CAPABILITIES_CHANNEL_COUNT_TYPE = 3; 48 private static final int CODEC_CAPABILITIES_OCTETS_PER_FRAME_TYPE = 4; 49 private static final int CODEC_CAPABILITIES_MAX_FRAMES_PER_SDU_TYPE = 5; 50 private static final int CODEC_CONFIGURATION_SAMPLE_RATE_TYPE = 0x01; 51 private static final int CODEC_CONFIGURATION_FRAME_DURATION_TYPE = 0x02; 52 private static final int CODEC_CONFIGURATION_CHANNEL_ALLOCATION_TYPE = 0x03; 53 private static final int CODEC_CONFIGURATION_OCTETS_PER_FRAME_TYPE = 0x04; 54 private static final int CODEC_CONFIGURATION_BLOCKS_PER_SDU_TYPE = 0x05; 55 private static final int METADATA_PREFERRED_CONTEXTS_TYPE = 0x01; 56 private static final int METADATA_STREAMING_CONTEXTS_TYPE = 0x02; 57 private static final int METADATA_PROGRAM_INFO_TYPE = 0x03; 58 private static final int METADATA_LANGUAGE_TYPE = 0x04; 59 private static final int METADATA_CCID_LIST_TYPE = 0x05; 60 private static final int METADATA_PARENTAL_RATING_TYPE = 0x06; 61 private static final int METADATA_PROGRAM_INFO_URI_TYPE = 0x07; 62 private static final int METADATA_EXTENDED_TYPE = 0xFE; 63 private static final int METADATA_VENDOR_TYPE = 0xFF; 64 private static final int CODEC_AUDIO_LOCATION_FRONT_LEFT = 0x01000000; 65 private static final int CODEC_AUDIO_LOCATION_FRONT_RIGHT = 0x02000000; 66 private static final int CODEC_AUDIO_SAMPLE_RATE_8K = 0x01; 67 private static final int CODEC_AUDIO_SAMPLE_RATE_16K = 0x03; 68 private static final int CODEC_AUDIO_SAMPLE_RATE_24K = 0x05; 69 private static final int CODEC_AUDIO_SAMPLE_RATE_32K = 0x06; 70 private static final int CODEC_AUDIO_SAMPLE_RATE_44P1K = 0x07; 71 private static final int CODEC_AUDIO_SAMPLE_RATE_48K = 0x08; 72 private static final int CODEC_AUDIO_FRAME_DURATION_7P5MS = 0x00; 73 private static final int CODEC_AUDIO_FRAME_DURATION_10MS = 0x01; 74 75 private final BaseInformation mLevelOne; 76 private final ArrayList<BaseInformation> mLevelTwo; 77 private final ArrayList<BaseInformation> mLevelThree; 78 79 private int mNumBISIndices = 0; 80 81 public static class BaseInformation { 82 public byte[] presentationDelay = new byte[3]; 83 public byte[] codecId = new byte[5]; 84 public byte codecConfigLength; 85 public byte[] codecConfigInfo; 86 public byte metaDataLength; 87 public byte[] metaData; 88 public byte numSubGroups; 89 public byte[] bisIndices; 90 public byte index; 91 public int subGroupId; 92 public int level; 93 public LinkedHashSet<String> keyCodecCfgDiff; 94 public LinkedHashSet<String> keyMetadataDiff; 95 public String diffText; 96 public String description; 97 public byte[] consolidatedCodecId; 98 public Set<String> consolidatedMetadata; 99 public Set<String> consolidatedCodecInfo; 100 public HashMap<Integer, String> consolidatedUniqueCodecInfo; 101 public HashMap<Integer, String> consolidatedUniqueMetadata; 102 BaseInformation()103 BaseInformation() { 104 presentationDelay = new byte[3]; 105 codecId = new byte[5]; 106 codecConfigLength = 0; 107 codecConfigInfo = new byte[0]; 108 metaDataLength = 0; 109 metaData = new byte[0]; 110 numSubGroups = 0; 111 bisIndices = null; 112 index = (byte) 0xFF; 113 level = 0; 114 keyCodecCfgDiff = new LinkedHashSet<String>(); 115 keyMetadataDiff = new LinkedHashSet<String>(); 116 consolidatedMetadata = new LinkedHashSet<String>(); 117 consolidatedCodecInfo = new LinkedHashSet<String>(); 118 consolidatedCodecId = new byte[5]; 119 consolidatedUniqueMetadata = new HashMap<Integer, String>(); 120 consolidatedUniqueCodecInfo = new HashMap<Integer, String>(); 121 diffText = new String(""); 122 description = new String(""); 123 log("BaseInformation is Initialized"); 124 } 125 isCodecIdUnknown()126 boolean isCodecIdUnknown() { 127 return (codecId != null && codecId[4] == (byte) UNKNOWN_CODEC); 128 } 129 print()130 void print() { 131 log("**BEGIN: Base Information**"); 132 log("**Level: " + level + "***"); 133 if (level == 1) { 134 log("presentationDelay: " + Arrays.toString(presentationDelay)); 135 } 136 if (level == 2) { 137 log("codecId: " + Arrays.toString(codecId)); 138 } 139 if (level == 2 || level == 3) { 140 log("codecConfigLength: " + codecConfigLength); 141 log("subGroupId: " + subGroupId); 142 } 143 if (codecConfigLength != (byte) 0) { 144 log("codecConfigInfo: " + Arrays.toString(codecConfigInfo)); 145 } 146 if (level == 2) { 147 log("metaDataLength: " + metaDataLength); 148 if (metaDataLength != (byte) 0) { 149 log("metaData: " + Arrays.toString(metaData)); 150 } 151 if (level == 1 || level == 2) { 152 log("numSubGroups: " + numSubGroups); 153 } 154 } 155 if (level == 2) { 156 log("Level2: Key Metadata differentiators"); 157 if (keyMetadataDiff != null) { 158 Iterator<String> itr = keyMetadataDiff.iterator(); 159 for (int k = 0; itr.hasNext(); k++) { 160 log("keyMetadataDiff:[" + k + "]:" 161 + Arrays.toString(itr.next().getBytes())); 162 } 163 } 164 log("END: Level2: Key Metadata differentiators"); 165 log("Level2: Key CodecConfig differentiators"); 166 if (keyCodecCfgDiff != null) { 167 Iterator<String> itr = keyCodecCfgDiff.iterator(); 168 for (int k = 0; itr.hasNext(); k++) { 169 log("LEVEL2: keyCodecCfgDiff:[" + k + "]:" 170 + Arrays.toString(itr.next().getBytes())); 171 } 172 } 173 log("END: Level2: Key CodecConfig differentiators"); 174 log("LEVEL2: diffText: " + diffText); 175 } 176 if (level == 3) { 177 log("Level3: Key CodecConfig differentiators"); 178 if (keyCodecCfgDiff != null) { 179 Iterator<String> itr = keyCodecCfgDiff.iterator(); 180 for (int k = 0; itr.hasNext(); k++) { 181 log("LEVEL3: keyCodecCfgDiff:[" + k + "]:" 182 + Arrays.toString(itr.next().getBytes())); 183 } 184 } 185 log("END: Level3: Key CodecConfig differentiators"); 186 log("index: " + index); 187 log("LEVEL3: diffText: " + diffText); 188 } 189 log("**END: Base Information****"); 190 } 191 } 192 BaseData(BaseInformation levelOne, ArrayList<BaseInformation> levelTwo, ArrayList<BaseInformation> levelThree, int numOfBISIndices)193 BaseData(BaseInformation levelOne, ArrayList<BaseInformation> levelTwo, 194 ArrayList<BaseInformation> levelThree, int numOfBISIndices) { 195 mLevelOne = levelOne; 196 mLevelTwo = levelTwo; 197 mLevelThree = levelThree; 198 mNumBISIndices = numOfBISIndices; 199 } 200 parseBaseData(byte[] serviceData)201 static BaseData parseBaseData(byte[] serviceData) { 202 if (serviceData == null) { 203 Log.e(TAG, "Invalid service data for BaseData construction"); 204 throw new IllegalArgumentException("Basedata: serviceData is null"); 205 } 206 BaseInformation levelOne = new BaseInformation(); 207 ArrayList<BaseInformation> levelTwo = new ArrayList<BaseInformation>(); 208 ArrayList<BaseInformation> levelThree = new ArrayList<BaseInformation>(); 209 int numOfBISIndices = 0; 210 log("BASE input" + Arrays.toString(serviceData)); 211 212 // Parse Level 1 base 213 levelOne.level = METADATA_LEVEL1; 214 int offset = 0; 215 System.arraycopy(serviceData, offset, levelOne.presentationDelay, 0, 3); 216 offset += METADATA_PRESENTATIONDELAY_LENGTH; 217 levelOne.numSubGroups = serviceData[offset++]; 218 levelOne.print(); 219 log("levelOne subgroups" + levelOne.numSubGroups); 220 for (int i = 0; i < (int) levelOne.numSubGroups; i++) { 221 Pair<BaseInformation, Integer> pair1 = 222 parseLevelTwo(serviceData, i, offset); 223 BaseInformation node2 = pair1.first; 224 if (node2 == null) { 225 Log.e(TAG, "Error: parsing Level 2"); 226 return null; 227 } 228 numOfBISIndices += node2.numSubGroups; 229 levelTwo.add(node2); 230 node2.print(); 231 offset = pair1.second; 232 for (int k = 0; k < node2.numSubGroups; k++) { 233 Pair<BaseInformation, Integer> pair2 = 234 parseLevelThree(serviceData, offset); 235 BaseInformation node3 = pair2.first; 236 offset = pair2.second; 237 if (node3 == null) { 238 Log.e(TAG, "Error: parsing Level 3"); 239 return null; 240 } 241 levelThree.add(node3); 242 node3.print(); 243 } 244 } 245 consolidateBaseofLevelTwo(levelTwo, levelThree); 246 return new BaseData(levelOne, levelTwo, levelThree, numOfBISIndices); 247 } 248 249 private static Pair<BaseInformation, Integer> parseLevelTwo(byte[] serviceData, int groupIndex, int offset)250 parseLevelTwo(byte[] serviceData, int groupIndex, int offset) { 251 log("Parsing Level 2"); 252 BaseInformation node = new BaseInformation(); 253 node.level = METADATA_LEVEL2; 254 node.subGroupId = groupIndex; 255 node.numSubGroups = serviceData[offset++]; 256 if (serviceData[offset] == (byte) UNKNOWN_CODEC) { 257 // Place It in the last byte of codecID 258 System.arraycopy(serviceData, offset, node.codecId, 259 METADATA_CODEC_LENGTH - 1, METADATA_UNKNOWN_CODEC_LENGTH); 260 offset += METADATA_UNKNOWN_CODEC_LENGTH; 261 log("codecId is FE"); 262 } else { 263 System.arraycopy(serviceData, offset, node.codecId, 264 0, METADATA_CODEC_LENGTH); 265 offset += METADATA_CODEC_LENGTH; 266 } 267 node.codecConfigLength = serviceData[offset++]; 268 if (node.codecConfigLength != 0) { 269 node.codecConfigInfo = new byte[(int) node.codecConfigLength]; 270 System.arraycopy(serviceData, offset, node.codecConfigInfo, 271 0, (int) node.codecConfigLength); 272 offset += node.codecConfigLength; 273 } 274 node.metaDataLength = serviceData[offset++]; 275 if (node.metaDataLength != 0) { 276 node.metaData = new byte[(int) node.metaDataLength]; 277 System.arraycopy(serviceData, offset, 278 node.metaData, 0, (int) node.metaDataLength); 279 offset += node.metaDataLength; 280 } 281 return new Pair<BaseInformation, Integer>(node, offset); 282 } 283 284 private static Pair<BaseInformation, Integer> parseLevelThree(byte[] serviceData, int offset)285 parseLevelThree(byte[] serviceData, int offset) { 286 log("Parsing Level 3"); 287 BaseInformation node = new BaseInformation(); 288 node.level = METADATA_LEVEL3; 289 node.index = serviceData[offset++]; 290 node.codecConfigLength = serviceData[offset++]; 291 if (node.codecConfigLength != 0) { 292 node.codecConfigInfo = new byte[(int) node.codecConfigLength]; 293 System.arraycopy(serviceData, offset, 294 node.codecConfigInfo, 0, (int) node.codecConfigLength); 295 offset += node.codecConfigLength; 296 } 297 return new Pair<BaseInformation, Integer>(node, offset); 298 } 299 consolidateBaseofLevelTwo(ArrayList<BaseInformation> levelTwo, ArrayList<BaseInformation> levelThree)300 static void consolidateBaseofLevelTwo(ArrayList<BaseInformation> levelTwo, 301 ArrayList<BaseInformation> levelThree) { 302 int startIdx = 0; 303 int children = 0; 304 for (int i = 0; i < levelTwo.size(); i++) { 305 startIdx = startIdx + children; 306 children = children + levelTwo.get(i).numSubGroups; 307 consolidateBaseofLevelThree(levelTwo, levelThree, 308 i, startIdx, levelTwo.get(i).numSubGroups); 309 } 310 // Eliminate Duplicates at Level 3 311 for (int i = 0; i < levelThree.size(); i++) { 312 Map<Integer, String> uniqueMds = new HashMap<Integer, String>(); 313 Map<Integer, String> uniqueCcis = new HashMap<Integer, String>(); 314 Set<String> Csfs = levelThree.get(i).consolidatedCodecInfo; 315 if (Csfs.size() > 0) { 316 Iterator<String> itr = Csfs.iterator(); 317 for (int j = 0; itr.hasNext(); j++) { 318 byte[] ltvEntries = itr.next().getBytes(); 319 int k = 0; 320 byte length = ltvEntries[k++]; 321 byte[] ltv = new byte[length + 1]; 322 ltv[0] = length; 323 System.arraycopy(ltvEntries, k, ltv, 1, length); 324 int type = (int) ltv[1]; 325 String s = uniqueCcis.get(type); 326 String ltvS = new String(ltv); 327 if (s == null) { 328 uniqueCcis.put(type, ltvS); 329 } else { 330 // if same type exists, replace 331 uniqueCcis.replace(type, ltvS); 332 } 333 } 334 } 335 Set<String> Mds = levelThree.get(i).consolidatedMetadata; 336 if (Mds.size() > 0) { 337 Iterator<String> itr = Mds.iterator(); 338 for (int j = 0; itr.hasNext(); j++) { 339 byte[] ltvEntries = itr.next().getBytes(); 340 int k = 0; 341 byte length = ltvEntries[k++]; 342 byte[] ltv = new byte[length + 1]; 343 ltv[0] = length; 344 System.arraycopy(ltvEntries, k, ltv, 1, length); 345 int type = (int) ltv[1]; 346 String s = uniqueCcis.get(type); 347 String ltvS = new String(ltv); 348 if (s == null) { 349 uniqueMds.put(type, ltvS); 350 } else { 351 uniqueMds.replace(type, ltvS); 352 } 353 } 354 } 355 levelThree.get(i).consolidatedUniqueMetadata = new HashMap<Integer, String>(uniqueMds); 356 levelThree.get(i).consolidatedUniqueCodecInfo = 357 new HashMap<Integer, String>(uniqueCcis); 358 } 359 } 360 consolidateBaseofLevelThree(ArrayList<BaseInformation> levelTwo, ArrayList<BaseInformation> levelThree, int parentSubgroup, int startIdx, int numNodes)361 static void consolidateBaseofLevelThree(ArrayList<BaseInformation> levelTwo, 362 ArrayList<BaseInformation> levelThree, int parentSubgroup, int startIdx, int numNodes) { 363 for (int i = startIdx; i < startIdx + numNodes || i < levelThree.size(); i++) { 364 levelThree.get(i).subGroupId = levelTwo.get(parentSubgroup).subGroupId; 365 log("Copy Codec Id from Level2 Parent" + parentSubgroup); 366 System.arraycopy( 367 levelTwo.get(parentSubgroup).consolidatedCodecId, 368 0, levelThree.get(i).consolidatedCodecId, 0, 5); 369 // Metadata clone from Parent 370 levelThree.get(i).consolidatedMetadata = 371 new LinkedHashSet<String>(levelTwo.get(parentSubgroup).consolidatedMetadata); 372 // CCI clone from Parent 373 levelThree.get(i).consolidatedCodecInfo = 374 new LinkedHashSet<String>(levelTwo.get(parentSubgroup).consolidatedCodecInfo); 375 // Append Level 2 Codec Config 376 if (levelThree.get(i).codecConfigLength != 0) { 377 log("append level 3 cci to level 3 cons:" + i); 378 String s = new String(levelThree.get(i).codecConfigInfo); 379 levelThree.get(i).consolidatedCodecInfo.add(s); 380 } 381 } 382 } 383 getNumberOfIndices()384 public int getNumberOfIndices() { 385 return mNumBISIndices; 386 } 387 getLevelOne()388 public BaseInformation getLevelOne() { 389 return mLevelOne; 390 } 391 getLevelTwo()392 public ArrayList<BaseInformation> getLevelTwo() { 393 return mLevelTwo; 394 } 395 getLevelThree()396 public ArrayList<BaseInformation> getLevelThree() { 397 return mLevelThree; 398 } 399 getNumberOfSubgroupsofBIG()400 public byte getNumberOfSubgroupsofBIG() { 401 byte ret = 0; 402 if (mLevelOne != null) { 403 ret = mLevelOne.numSubGroups; 404 } 405 return ret; 406 } 407 getBISIndexInfos()408 public ArrayList<BaseInformation> getBISIndexInfos() { 409 return mLevelThree; 410 } 411 getMetadata(int subGroup)412 byte[] getMetadata(int subGroup) { 413 if (mLevelTwo != null) { 414 return mLevelTwo.get(subGroup).metaData; 415 } 416 return null; 417 } 418 getMetadataString(byte[] metadataBytes)419 String getMetadataString(byte[] metadataBytes) { 420 String ret = ""; 421 switch (metadataBytes[1]) { 422 case METADATA_LANGUAGE_TYPE: 423 char[] lang = new char[3]; 424 System.arraycopy(metadataBytes, 1, lang, 0, 3); 425 Locale locale = new Locale(String.valueOf(lang)); 426 try { 427 ret = locale.getISO3Language(); 428 } catch (MissingResourceException e) { 429 ret = "UNKNOWN LANGUAGE"; 430 } 431 break; 432 default: 433 ret = "UNKNOWN METADATA TYPE"; 434 } 435 log("getMetadataString: " + ret); 436 return ret; 437 } 438 getCodecParamString(byte[] csiBytes)439 String getCodecParamString(byte[] csiBytes) { 440 String ret = ""; 441 switch (csiBytes[1]) { 442 case CODEC_CONFIGURATION_CHANNEL_ALLOCATION_TYPE: 443 byte[] location = new byte[4]; 444 System.arraycopy(csiBytes, 2, location, 0, 4); 445 ByteBuffer wrapped = ByteBuffer.wrap(location); 446 int audioLocation = wrapped.getInt(); 447 log("audioLocation: " + audioLocation); 448 switch (audioLocation) { 449 case CODEC_AUDIO_LOCATION_FRONT_LEFT: 450 ret = "LEFT"; 451 break; 452 case CODEC_AUDIO_LOCATION_FRONT_RIGHT: 453 ret = "RIGHT"; 454 break; 455 case CODEC_AUDIO_LOCATION_FRONT_LEFT 456 | CODEC_AUDIO_LOCATION_FRONT_RIGHT: 457 ret = "LR"; 458 break; 459 } 460 break; 461 case CODEC_CONFIGURATION_SAMPLE_RATE_TYPE: 462 switch (csiBytes[2]) { 463 case CODEC_AUDIO_SAMPLE_RATE_8K: 464 ret = "8K"; 465 break; 466 case CODEC_AUDIO_SAMPLE_RATE_16K: 467 ret = "16K"; 468 break; 469 case CODEC_AUDIO_SAMPLE_RATE_24K: 470 ret = "24K"; 471 break; 472 case CODEC_AUDIO_SAMPLE_RATE_32K: 473 ret = "32K"; 474 break; 475 case CODEC_AUDIO_SAMPLE_RATE_44P1K: 476 ret = "44.1K"; 477 break; 478 case CODEC_AUDIO_SAMPLE_RATE_48K: 479 ret = "48K"; 480 break; 481 } 482 break; 483 case CODEC_CONFIGURATION_FRAME_DURATION_TYPE: 484 switch (csiBytes[2]) { 485 case CODEC_AUDIO_FRAME_DURATION_7P5MS: 486 ret = "7.5ms"; 487 break; 488 case CODEC_AUDIO_FRAME_DURATION_10MS: 489 ret = "10ms"; 490 break; 491 } 492 break; 493 case CODEC_CONFIGURATION_OCTETS_PER_FRAME_TYPE: 494 ret = "OPF_" + String.valueOf((int) csiBytes[2]); 495 break; 496 default: 497 ret = "UNKNOWN PARAMETER"; 498 } 499 log("getCodecParamString: " + ret); 500 return ret; 501 } 502 print()503 void print() { 504 mLevelOne.print(); 505 log("----- Level TWO BASE ----"); 506 for (int i = 0; i < mLevelTwo.size(); i++) { 507 mLevelTwo.get(i).print(); 508 } 509 log("----- Level THREE BASE ----"); 510 for (int i = 0; i < mLevelThree.size(); i++) { 511 mLevelThree.get(i).print(); 512 } 513 } 514 log(String msg)515 static void log(String msg) { 516 if (BassConstants.BASS_DBG) { 517 Log.d(TAG, msg); 518 } 519 } 520 } 521