1 /* 2 * Copyright (C) 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 android.bluetooth; 18 19 import static android.bluetooth.BluetoothLeAudioCodecConfig.FRAME_DURATION_10000; 20 import static android.bluetooth.BluetoothLeAudioCodecConfig.FRAME_DURATION_7500; 21 import static android.bluetooth.BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE; 22 import static android.bluetooth.BluetoothLeAudioCodecConfig.FrameDuration; 23 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000; 24 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000; 25 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_32000; 26 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_44100; 27 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000; 28 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_8000; 29 import static android.bluetooth.BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE; 30 import static android.bluetooth.BluetoothLeAudioCodecConfig.SampleRate; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.annotation.SystemApi; 35 import android.bluetooth.BluetoothUtils.TypeValueEntry; 36 import android.os.Parcel; 37 import android.os.Parcelable; 38 39 import java.nio.ByteBuffer; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.List; 43 import java.util.Objects; 44 45 /** 46 * A class representing the codec specific config metadata information defined in the Basic Audio 47 * Profile. 48 * 49 * @hide 50 */ 51 @SystemApi 52 public final class BluetoothLeAudioCodecConfigMetadata implements Parcelable { 53 private static final int SAMPLING_FREQUENCY_TYPE = 0x01; 54 private static final int FRAME_DURATION_TYPE = 0x02; 55 private static final int AUDIO_CHANNEL_LOCATION_TYPE = 0x03; 56 private static final int OCTETS_PER_FRAME_TYPE = 0x04; 57 58 private final long mAudioLocation; 59 private final @SampleRate int mSampleRate; 60 private final @FrameDuration int mFrameDuration; 61 private final int mOctetsPerFrame; 62 private final byte[] mRawMetadata; 63 64 /** 65 * Audio codec sampling frequency from metadata. 66 */ 67 private static final int CONFIG_SAMPLING_FREQUENCY_UNKNOWN = 0; 68 private static final int CONFIG_SAMPLING_FREQUENCY_8000 = 0x01; 69 private static final int CONFIG_SAMPLING_FREQUENCY_16000 = 0x03; 70 private static final int CONFIG_SAMPLING_FREQUENCY_24000 = 0x05; 71 private static final int CONFIG_SAMPLING_FREQUENCY_32000 = 0x06; 72 private static final int CONFIG_SAMPLING_FREQUENCY_44100 = 0x07; 73 private static final int CONFIG_SAMPLING_FREQUENCY_48000 = 0x08; 74 75 /** 76 * Audio codec config frame duration from metadata. 77 */ 78 private static final int CONFIG_FRAME_DURATION_UNKNOWN = -1; 79 private static final int CONFIG_FRAME_DURATION_7500 = 0x00; 80 private static final int CONFIG_FRAME_DURATION_10000 = 0x01; 81 BluetoothLeAudioCodecConfigMetadata(long audioLocation, @SampleRate int sampleRate, @FrameDuration int frameDuration, int octetsPerFrame, byte[] rawMetadata)82 private BluetoothLeAudioCodecConfigMetadata(long audioLocation, 83 @SampleRate int sampleRate, @FrameDuration int frameDuration, 84 int octetsPerFrame, byte[] rawMetadata) { 85 mAudioLocation = audioLocation; 86 mSampleRate = sampleRate; 87 mFrameDuration = frameDuration; 88 mOctetsPerFrame = octetsPerFrame; 89 mRawMetadata = rawMetadata; 90 } 91 92 @Override equals(@ullable Object o)93 public boolean equals(@Nullable Object o) { 94 if (o != null && o instanceof BluetoothLeAudioCodecConfigMetadata) { 95 final BluetoothLeAudioCodecConfigMetadata oth = (BluetoothLeAudioCodecConfigMetadata) o; 96 return mAudioLocation == oth.getAudioLocation() 97 && mSampleRate == oth.getSampleRate() 98 && mFrameDuration == oth.getFrameDuration() 99 && mOctetsPerFrame == oth.getOctetsPerFrame() 100 && Arrays.equals(mRawMetadata, oth.getRawMetadata()); 101 } 102 return false; 103 } 104 105 @Override hashCode()106 public int hashCode() { 107 return Objects.hash(mAudioLocation, mSampleRate, mFrameDuration, 108 mOctetsPerFrame, Arrays.hashCode(mRawMetadata)); 109 } 110 111 /** 112 * Get the audio location information as defined in the Generic Audio section of Bluetooth 113 * Assigned numbers. 114 * 115 * @return configured audio location, -1 if this metadata does not exist 116 * @hide 117 */ 118 @SystemApi getAudioLocation()119 public long getAudioLocation() { 120 return mAudioLocation; 121 } 122 123 /** 124 * Get the audio sample rate information as defined in the Generic Audio section of 125 * Bluetooth Assigned numbers 6.12.4.1 Supported_Sampling_Frequencies. 126 * 127 * Internally this is converted from Sampling_Frequency values as defined in 128 * 6.12.5.1 129 * 130 * @return configured sample rate from meta data, 131 * {@link BluetoothLeAudioCodecConfig#SAMPLE_RATE_NONE} 132 * if this metadata does not exist 133 * @hide 134 */ 135 @SystemApi getSampleRate()136 public @SampleRate int getSampleRate() { 137 return mSampleRate; 138 } 139 140 /** 141 * Get the audio frame duration information as defined in the Generic Audio section of 142 * Bluetooth Assigned numbers 6.12.5.2 Frame_Duration. 143 * 144 * Internally this is converted from Frame_Durations values as defined in 145 * 6.12.4.2 146 * 147 * @return configured frame duration from meta data, 148 * {@link BluetoothLeAudioCodecConfig#FRAME_DURATION_NONE} 149 * if this metadata does not exist 150 * @hide 151 */ 152 @SystemApi getFrameDuration()153 public @FrameDuration int getFrameDuration() { 154 return mFrameDuration; 155 } 156 157 /** 158 * Get the audio octets per frame information as defined in the Generic Audio section of 159 * Bluetooth Assigned numbers. 160 * 161 * @return configured octets per frame from meta data 162 * 0 if this metadata does not exist 163 * @hide 164 */ 165 @SystemApi getOctetsPerFrame()166 public int getOctetsPerFrame() { 167 return mOctetsPerFrame; 168 } 169 170 /** 171 * Get the raw bytes of stream metadata in Bluetooth LTV format. 172 * 173 * Bluetooth LTV format for stream metadata is defined in the Generic Audio 174 * section of <a href="https://www.bluetooth.com/specifications/assigned-numbers/">Bluetooth Assigned Numbers</a>, 175 * including metadata that was not covered by the getter methods in this class. 176 * 177 * @return raw bytes of stream metadata in Bluetooth LTV format 178 * @hide 179 */ 180 @SystemApi getRawMetadata()181 public @NonNull byte[] getRawMetadata() { 182 return mRawMetadata; 183 } 184 185 /** 186 * {@inheritDoc} 187 * @hide 188 */ 189 @Override describeContents()190 public int describeContents() { 191 return 0; 192 } 193 194 /** 195 * {@inheritDoc} 196 * @hide 197 */ 198 @Override writeToParcel(Parcel out, int flags)199 public void writeToParcel(Parcel out, int flags) { 200 out.writeLong(mAudioLocation); 201 if (mRawMetadata != null) { 202 out.writeInt(mRawMetadata.length); 203 out.writeByteArray(mRawMetadata); 204 } else { 205 out.writeInt(-1); 206 } 207 out.writeInt(mSampleRate); 208 out.writeInt(mFrameDuration); 209 out.writeInt(mOctetsPerFrame); 210 } 211 212 /** 213 * A {@link Parcelable.Creator} to create {@link BluetoothLeAudioCodecConfigMetadata} from 214 * parcel. 215 * @hide 216 */ 217 @SystemApi 218 @NonNull 219 public static final Creator<BluetoothLeAudioCodecConfigMetadata> CREATOR = new Creator<>() { 220 public @NonNull BluetoothLeAudioCodecConfigMetadata createFromParcel(@NonNull Parcel in) { 221 long audioLocation = in.readLong(); 222 int rawMetadataLen = in.readInt(); 223 byte[] rawMetadata; 224 if (rawMetadataLen != -1) { 225 rawMetadata = new byte[rawMetadataLen]; 226 in.readByteArray(rawMetadata); 227 } else { 228 rawMetadata = new byte[0]; 229 } 230 int sampleRate = in.readInt(); 231 int frameDuration = in.readInt(); 232 int octetsPerFrame = in.readInt(); 233 return new BluetoothLeAudioCodecConfigMetadata(audioLocation, sampleRate, 234 frameDuration, octetsPerFrame, rawMetadata); 235 } 236 237 public @NonNull BluetoothLeAudioCodecConfigMetadata[] newArray(int size) { 238 return new BluetoothLeAudioCodecConfigMetadata[size]; 239 } 240 }; 241 242 /** 243 * Construct a {@link BluetoothLeAudioCodecConfigMetadata} from raw bytes. 244 * 245 * The byte array will be parsed and values for each getter will be populated 246 * 247 * Raw metadata cannot be set using builder in order to maintain raw bytes and getter value 248 * consistency 249 * 250 * @param rawBytes raw bytes of stream metadata in Bluetooth LTV format 251 * @return parsed {@link BluetoothLeAudioCodecConfigMetadata} object 252 * @throws IllegalArgumentException if <var>rawBytes</var> is null or when the raw bytes cannot 253 * be parsed to build the object 254 * @hide 255 */ 256 @SystemApi 257 @NonNull fromRawBytes(@onNull byte[] rawBytes)258 public static BluetoothLeAudioCodecConfigMetadata fromRawBytes(@NonNull byte[] rawBytes) { 259 if (rawBytes == null) { 260 throw new IllegalArgumentException("Raw bytes cannot be null"); 261 } 262 List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes); 263 if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) { 264 throw new IllegalArgumentException("No LTV entries are found from rawBytes of size " 265 + rawBytes.length); 266 } 267 long audioLocation = 0; 268 int samplingFrequency = CONFIG_SAMPLING_FREQUENCY_UNKNOWN; 269 int frameDuration = CONFIG_FRAME_DURATION_UNKNOWN; 270 int octetsPerFrame = 0; 271 for (TypeValueEntry entry : entries) { 272 if (entry.getType() == AUDIO_CHANNEL_LOCATION_TYPE) { 273 byte[] bytes = entry.getValue(); 274 // Get unsigned uint32_t to long 275 audioLocation = ((bytes[0] & 0xFF) << 0) | ((bytes[1] & 0xFF) << 8) 276 | ((bytes[2] & 0xFF) << 16) | ((long) (bytes[3] & 0xFF) << 24); 277 } else if (entry.getType() == SAMPLING_FREQUENCY_TYPE) { 278 byte[] bytes = entry.getValue(); 279 // Get one byte for sampling frequency in value 280 samplingFrequency = (int) (bytes[0] & 0xFF); 281 } else if (entry.getType() == FRAME_DURATION_TYPE) { 282 byte[] bytes = entry.getValue(); 283 // Get one byte for frame duration in value 284 frameDuration = (int) (bytes[0] & 0xFF); 285 } else if (entry.getType() == OCTETS_PER_FRAME_TYPE) { 286 byte[] bytes = entry.getValue(); 287 // Get two bytes for octets per frame to int 288 octetsPerFrame = ((bytes[0] & 0xFF) << 0) | ((int) (bytes[1] & 0xFF) << 8); 289 } 290 } 291 return new BluetoothLeAudioCodecConfigMetadata(audioLocation, 292 convertToSampleRateBitset(samplingFrequency), 293 convertToFrameDurationBitset(frameDuration), 294 octetsPerFrame, rawBytes); 295 } 296 297 /** 298 * Builder for {@link BluetoothLeAudioCodecConfigMetadata}. 299 * @hide 300 */ 301 @SystemApi 302 public static final class Builder { 303 private long mAudioLocation = 0; 304 private int mSampleRate = SAMPLE_RATE_NONE; 305 private int mFrameDuration = FRAME_DURATION_NONE; 306 private int mOctetsPerFrame = 0; 307 private byte[] mRawMetadata = null; 308 309 /** 310 * Create an empty builder. 311 * @hide 312 */ 313 @SystemApi Builder()314 public Builder() {} 315 316 /** 317 * Create a builder with copies of information from original object. 318 * 319 * @param original original object 320 * @hide 321 */ 322 @SystemApi Builder(@onNull BluetoothLeAudioCodecConfigMetadata original)323 public Builder(@NonNull BluetoothLeAudioCodecConfigMetadata original) { 324 mAudioLocation = original.getAudioLocation(); 325 mSampleRate = original.getSampleRate(); 326 mFrameDuration = original.getFrameDuration(); 327 mOctetsPerFrame = original.getOctetsPerFrame(); 328 mRawMetadata = original.getRawMetadata(); 329 } 330 331 /** 332 * Set the audio location information as defined in the Generic Audio section of Bluetooth 333 * Assigned numbers. 334 * 335 * @param audioLocation configured audio location, -1 if does not exist 336 * @return this builder 337 * @hide 338 */ 339 @SystemApi 340 @NonNull setAudioLocation(long audioLocation)341 public Builder setAudioLocation(long audioLocation) { 342 mAudioLocation = audioLocation; 343 return this; 344 } 345 346 /** 347 * Set the audio sample rate information as defined in the Generic Audio section of 348 * Bluetooth Assigned 6.12.4.1 Supported_Sampling_Frequencies. 349 * 350 * Internally this will be converted to Sampling_Frequency values as defined in 351 * 6.12.5.1 352 * 353 * @param sampleRate configured sample rate in meta data 354 * @return this builder 355 * @throws IllegalArgumentException if sample rate is invalid value 356 * @hide 357 */ 358 @SystemApi 359 @NonNull setSampleRate(@ampleRate int sampleRate)360 public Builder setSampleRate(@SampleRate int sampleRate) { 361 if (sampleRate != SAMPLE_RATE_NONE 362 && sampleRate != SAMPLE_RATE_8000 363 && sampleRate != SAMPLE_RATE_16000 364 && sampleRate != SAMPLE_RATE_24000 365 && sampleRate != SAMPLE_RATE_32000 366 && sampleRate != SAMPLE_RATE_44100 367 && sampleRate != SAMPLE_RATE_48000) { 368 throw new IllegalArgumentException("Invalid sample rate " 369 + sampleRate); 370 } 371 mSampleRate = sampleRate; 372 return this; 373 } 374 375 /** 376 * Set the audio frame duration information as defined in the Generic Audio section of 377 * Bluetooth Assigned numbers 6.12.5.2 Frame_Duration. 378 * 379 * Internally this will be converted to Frame_Durations values as defined in 380 * 6.12.4.2 381 * 382 * @param frameDuration configured frame duration in meta data 383 * @return this builder 384 * @throws IllegalArgumentException if frameDuration is invalid value 385 * @hide 386 */ 387 @SystemApi 388 @NonNull setFrameDuration(@rameDuration int frameDuration)389 public Builder setFrameDuration(@FrameDuration 390 int frameDuration) { 391 if (frameDuration != FRAME_DURATION_NONE 392 && frameDuration != FRAME_DURATION_7500 393 && frameDuration != FRAME_DURATION_10000) { 394 throw new IllegalArgumentException("Invalid frame duration " + frameDuration); 395 } 396 mFrameDuration = frameDuration; 397 return this; 398 } 399 400 /** 401 * Set the audio octets per frame information as defined in the Generic Audio section of 402 * Bluetooth Assigned numbers. 403 * 404 * @param octetsPerFrame configured octets per frame in meta data 405 * @return this builder 406 * @throws IllegalArgumentException if octetsPerFrame is invalid value 407 * @hide 408 */ 409 @SystemApi 410 @NonNull setOctetsPerFrame(int octetsPerFrame)411 public Builder setOctetsPerFrame(int octetsPerFrame) { 412 if (octetsPerFrame < 0) { 413 throw new IllegalArgumentException("Invalid octetsPerFrame " + octetsPerFrame); 414 } 415 mOctetsPerFrame = octetsPerFrame; 416 return this; 417 } 418 419 /** 420 * Build {@link BluetoothLeAudioCodecConfigMetadata}. 421 * 422 * @return constructed {@link BluetoothLeAudioCodecConfigMetadata} 423 * @throws IllegalArgumentException if the object cannot be built 424 * @hide 425 */ 426 @SystemApi build()427 public @NonNull BluetoothLeAudioCodecConfigMetadata build() { 428 List<TypeValueEntry> entries = new ArrayList<>(); 429 if (mRawMetadata != null) { 430 entries = BluetoothUtils.parseLengthTypeValueBytes(mRawMetadata); 431 if (mRawMetadata.length > 0 && mRawMetadata[0] > 0 && entries.isEmpty()) { 432 throw new IllegalArgumentException("No LTV entries are found from rawBytes of" 433 + " size " + mRawMetadata.length + " please check the original object" 434 + " passed to Builder's copy constructor"); 435 } 436 } 437 if (mSampleRate != SAMPLE_RATE_NONE) { 438 int samplingFrequency = convertToSamplingFrequencyValue(mSampleRate); 439 entries.removeIf(entry -> entry.getType() == SAMPLING_FREQUENCY_TYPE); 440 entries.add(new TypeValueEntry(SAMPLING_FREQUENCY_TYPE, 441 ByteBuffer.allocate(1) 442 .put((byte) (samplingFrequency & 0xFF)).array())); 443 } 444 if (mFrameDuration != FRAME_DURATION_NONE) { 445 int frameDuration = convertToFrameDurationValue(mFrameDuration); 446 entries.removeIf(entry -> entry.getType() == FRAME_DURATION_TYPE); 447 entries.add(new TypeValueEntry(FRAME_DURATION_TYPE, 448 ByteBuffer.allocate(1) 449 .put((byte) (frameDuration & 0xFF)).array())); 450 } 451 if (mAudioLocation != -1) { 452 entries.removeIf(entry -> entry.getType() == AUDIO_CHANNEL_LOCATION_TYPE); 453 entries.add(new TypeValueEntry(AUDIO_CHANNEL_LOCATION_TYPE, 454 ByteBuffer.allocate(4) 455 .putInt((int) (mAudioLocation & 0xFFFFFFFF)).array())); 456 } 457 if (mOctetsPerFrame != 0) { 458 entries.removeIf(entry -> entry.getType() == OCTETS_PER_FRAME_TYPE); 459 entries.add(new TypeValueEntry(OCTETS_PER_FRAME_TYPE, 460 ByteBuffer.allocate(2) 461 .putShort((short) (mOctetsPerFrame & 0xFFFF)).array())); 462 } 463 byte[] rawBytes = BluetoothUtils.serializeTypeValue(entries); 464 if (rawBytes == null) { 465 throw new IllegalArgumentException("Failed to serialize entries to bytes"); 466 } 467 return new BluetoothLeAudioCodecConfigMetadata(mAudioLocation, mSampleRate, 468 mFrameDuration, mOctetsPerFrame, rawBytes); 469 } 470 } 471 convertToSampleRateBitset(int samplingFrequencyValue)472 private static int convertToSampleRateBitset(int samplingFrequencyValue) { 473 switch (samplingFrequencyValue) { 474 case CONFIG_SAMPLING_FREQUENCY_8000: 475 return SAMPLE_RATE_8000; 476 case CONFIG_SAMPLING_FREQUENCY_16000: 477 return SAMPLE_RATE_16000; 478 case CONFIG_SAMPLING_FREQUENCY_24000: 479 return SAMPLE_RATE_24000; 480 case CONFIG_SAMPLING_FREQUENCY_32000: 481 return SAMPLE_RATE_32000; 482 case CONFIG_SAMPLING_FREQUENCY_44100: 483 return SAMPLE_RATE_44100; 484 case CONFIG_SAMPLING_FREQUENCY_48000: 485 return SAMPLE_RATE_48000; 486 default: 487 return SAMPLE_RATE_NONE; 488 } 489 } 490 convertToSamplingFrequencyValue(int sampleRateBitSet)491 private static int convertToSamplingFrequencyValue(int sampleRateBitSet) { 492 switch (sampleRateBitSet) { 493 case SAMPLE_RATE_8000: 494 return CONFIG_SAMPLING_FREQUENCY_8000; 495 case SAMPLE_RATE_16000: 496 return CONFIG_SAMPLING_FREQUENCY_16000; 497 case SAMPLE_RATE_24000: 498 return CONFIG_SAMPLING_FREQUENCY_24000; 499 case SAMPLE_RATE_32000: 500 return CONFIG_SAMPLING_FREQUENCY_32000; 501 case SAMPLE_RATE_44100: 502 return CONFIG_SAMPLING_FREQUENCY_44100; 503 case SAMPLE_RATE_48000: 504 return CONFIG_SAMPLING_FREQUENCY_48000; 505 default: 506 return CONFIG_SAMPLING_FREQUENCY_UNKNOWN; 507 } 508 } 509 convertToFrameDurationBitset(int frameDurationValue)510 private static int convertToFrameDurationBitset(int frameDurationValue) { 511 switch (frameDurationValue) { 512 case CONFIG_FRAME_DURATION_7500: 513 return FRAME_DURATION_7500; 514 case CONFIG_FRAME_DURATION_10000: 515 return FRAME_DURATION_10000; 516 default: 517 return FRAME_DURATION_NONE; 518 } 519 } 520 convertToFrameDurationValue(int frameDurationBitset)521 private static int convertToFrameDurationValue(int frameDurationBitset) { 522 switch (frameDurationBitset) { 523 case FRAME_DURATION_7500: 524 return CONFIG_FRAME_DURATION_7500; 525 case FRAME_DURATION_10000: 526 return CONFIG_FRAME_DURATION_10000; 527 default: 528 return CONFIG_FRAME_DURATION_UNKNOWN; 529 } 530 } 531 } 532