• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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