• 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.bluetooth.BluetoothUtils.TypeValueEntry;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.nio.ByteBuffer;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.Objects;
31 
32 /**
33  * A class representing the codec specific config metadata information defined in the Basic Audio
34  * Profile.
35  *
36  * @hide
37  */
38 @SystemApi
39 public final class BluetoothLeAudioCodecConfigMetadata implements Parcelable {
40     private static final int AUDIO_CHANNEL_LOCATION_TYPE = 0x03;
41 
42     private final long mAudioLocation;
43     private final byte[] mRawMetadata;
44 
BluetoothLeAudioCodecConfigMetadata(long audioLocation, byte[] rawMetadata)45     private BluetoothLeAudioCodecConfigMetadata(long audioLocation, byte[] rawMetadata) {
46         mAudioLocation = audioLocation;
47         mRawMetadata = rawMetadata;
48     }
49 
50     @Override
equals(@ullable Object o)51     public boolean equals(@Nullable Object o) {
52         if (o != null && o instanceof BluetoothLeAudioCodecConfigMetadata) {
53             final BluetoothLeAudioCodecConfigMetadata oth = (BluetoothLeAudioCodecConfigMetadata) o;
54             return mAudioLocation == oth.getAudioLocation()
55                 && Arrays.equals(mRawMetadata, oth.getRawMetadata());
56         }
57         return false;
58     }
59 
60     @Override
hashCode()61     public int hashCode() {
62         return Objects.hash(mAudioLocation, Arrays.hashCode(mRawMetadata));
63     }
64 
65     /**
66      * Get the audio location information as defined in the Generic Audio section of Bluetooth
67      * Assigned numbers.
68      *
69      * @return configured audio location, -1 if this metadata does not exist
70      * @hide
71      */
72     @SystemApi
getAudioLocation()73     public long getAudioLocation() {
74         return mAudioLocation;
75     }
76 
77     /**
78      * Get the raw bytes of stream metadata in Bluetooth LTV format.
79      *
80      * Bluetooth LTV format for stream metadata is defined in the Generic Audio
81      * section of <a href="https://www.bluetooth.com/specifications/assigned-numbers/">Bluetooth Assigned Numbers</a>,
82      * including metadata that was not covered by the getter methods in this class.
83      *
84      * @return raw bytes of stream metadata in Bluetooth LTV format
85      * @hide
86      */
87     @SystemApi
getRawMetadata()88     public @NonNull byte[] getRawMetadata() {
89         return mRawMetadata;
90     }
91 
92     /**
93      * {@inheritDoc}
94      * @hide
95      */
96     @Override
describeContents()97     public int describeContents() {
98         return 0;
99     }
100 
101     /**
102      * {@inheritDoc}
103      * @hide
104      */
105     @Override
writeToParcel(Parcel out, int flags)106     public void writeToParcel(Parcel out, int flags) {
107         out.writeLong(mAudioLocation);
108         if (mRawMetadata != null) {
109             out.writeInt(mRawMetadata.length);
110             out.writeByteArray(mRawMetadata);
111         } else {
112             out.writeInt(-1);
113         }
114     }
115 
116     /**
117      * A {@link Parcelable.Creator} to create {@link BluetoothLeAudioCodecConfigMetadata} from
118      * parcel.
119      * @hide
120      */
121     @SystemApi
122     public static final @NonNull Parcelable.Creator<BluetoothLeAudioCodecConfigMetadata> CREATOR =
123             new Parcelable.Creator<BluetoothLeAudioCodecConfigMetadata>() {
124                 @NonNull
125                 public BluetoothLeAudioCodecConfigMetadata createFromParcel(@NonNull Parcel in) {
126                     long audioLocation = in.readLong();
127                     int rawMetadataLen = in.readInt();
128                     byte[] rawMetadata;
129                     if (rawMetadataLen != -1) {
130                         rawMetadata = new byte[rawMetadataLen];
131                         in.readByteArray(rawMetadata);
132                     } else {
133                         rawMetadata = new byte[0];
134                     }
135                     return new BluetoothLeAudioCodecConfigMetadata(audioLocation, rawMetadata);
136                 }
137 
138                 public @NonNull BluetoothLeAudioCodecConfigMetadata[] newArray(int size) {
139                     return new BluetoothLeAudioCodecConfigMetadata[size];
140                 }
141             };
142 
143     /**
144      * Construct a {@link BluetoothLeAudioCodecConfigMetadata} from raw bytes.
145      *
146      * The byte array will be parsed and values for each getter will be populated
147      *
148      * Raw metadata cannot be set using builder in order to maintain raw bytes and getter value
149      * consistency
150      *
151      * @param rawBytes raw bytes of stream metadata in Bluetooth LTV format
152      * @return parsed {@link BluetoothLeAudioCodecConfigMetadata} object
153      * @throws IllegalArgumentException if <var>rawBytes</var> is null or when the raw bytes cannot
154      * be parsed to build the object
155      * @hide
156      */
157     @SystemApi
fromRawBytes( @onNull byte[] rawBytes)158     public static @NonNull BluetoothLeAudioCodecConfigMetadata fromRawBytes(
159             @NonNull byte[] rawBytes) {
160         if (rawBytes == null) {
161             throw new IllegalArgumentException("Raw bytes cannot be null");
162         }
163         List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes);
164         if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) {
165             throw new IllegalArgumentException("No LTV entries are found from rawBytes of size "
166                     + rawBytes.length);
167         }
168         long audioLocation = 0;
169         for (TypeValueEntry entry : entries) {
170             if (entry.getType() == AUDIO_CHANNEL_LOCATION_TYPE) {
171                 byte[] bytes = entry.getValue();
172                 // Get unsigned uint32_t to long
173                 audioLocation = ((bytes[0] & 0xFF) <<  0) | ((bytes[1] & 0xFF) <<  8)
174                         | ((bytes[2] & 0xFF) << 16) | ((long) (bytes[3] & 0xFF) << 24);
175             }
176         }
177         return new BluetoothLeAudioCodecConfigMetadata(audioLocation, rawBytes);
178     }
179 
180     /**
181      * Builder for {@link BluetoothLeAudioCodecConfigMetadata}.
182      * @hide
183      */
184     @SystemApi
185     public static final class Builder {
186         private long mAudioLocation = 0;
187         private byte[] mRawMetadata = null;
188 
189         /**
190          * Create an empty builder.
191          * @hide
192          */
193         @SystemApi
Builder()194         public Builder() {}
195 
196         /**
197          * Create a builder with copies of information from original object.
198          *
199          * @param original original object
200          * @hide
201          */
202         @SystemApi
Builder(@onNull BluetoothLeAudioCodecConfigMetadata original)203         public Builder(@NonNull BluetoothLeAudioCodecConfigMetadata original) {
204             mAudioLocation = original.getAudioLocation();
205             mRawMetadata = original.getRawMetadata();
206         }
207 
208         /**
209          * Set the audio location information as defined in the Generic Audio section of Bluetooth
210          * Assigned numbers.
211          *
212          * @param audioLocation configured audio location, -1 if does not exist
213          * @return this builder
214          * @hide
215          */
216         @SystemApi
setAudioLocation(long audioLocation)217         public @NonNull Builder setAudioLocation(long audioLocation) {
218             mAudioLocation = audioLocation;
219             return this;
220         }
221 
222         /**
223          * Build {@link BluetoothLeAudioCodecConfigMetadata}.
224          *
225          * @return constructed {@link BluetoothLeAudioCodecConfigMetadata}
226          * @throws IllegalArgumentException if the object cannot be built
227          * @hide
228          */
229         @SystemApi
build()230         public @NonNull BluetoothLeAudioCodecConfigMetadata build() {
231             List<TypeValueEntry> entries = new ArrayList<>();
232             if (mRawMetadata != null) {
233                 entries = BluetoothUtils.parseLengthTypeValueBytes(mRawMetadata);
234                 if (mRawMetadata.length > 0 && mRawMetadata[0] > 0 && entries.isEmpty()) {
235                     throw new IllegalArgumentException("No LTV entries are found from rawBytes of"
236                             + " size " + mRawMetadata.length + " please check the original object"
237                             + " passed to Builder's copy constructor");
238                 }
239             }
240             if (mAudioLocation != 0) {
241                 entries.removeIf(entry -> entry.getType() == AUDIO_CHANNEL_LOCATION_TYPE);
242                 entries.add(new TypeValueEntry(AUDIO_CHANNEL_LOCATION_TYPE,
243                         ByteBuffer.allocate(Long.BYTES).putLong(mAudioLocation).array()));
244             }
245             byte[] rawBytes = BluetoothUtils.serializeTypeValue(entries);
246             if (rawBytes == null) {
247                 throw new IllegalArgumentException("Failed to serialize entries to bytes");
248             }
249             return new BluetoothLeAudioCodecConfigMetadata(mAudioLocation, rawBytes);
250         }
251     }
252 }
253