1 /*
2  * Copyright (C) 2020 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.media;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.util.Log;
23 import android.util.Pair;
24 
25 import java.lang.reflect.ParameterizedType;
26 import java.nio.BufferUnderflowException;
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.nio.charset.Charset;
30 import java.nio.charset.StandardCharsets;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Set;
36 
37 /**
38  * AudioMetadata class is used to manage typed key-value pairs for
39  * configuration and capability requests within the Audio Framework.
40  */
41 public final class AudioMetadata {
42     private static final String TAG = "AudioMetadata";
43 
44     /**
45      * Key interface for the {@code AudioMetadata} map.
46      *
47      * <p>The presence of this {@code Key} interface on an object allows
48      * it to reference metadata in the Audio Framework.</p>
49      *
50      * <p>Vendors are allowed to implement this {@code Key} interface for their debugging or
51      * private application use. To avoid name conflicts, vendor key names should be qualified by
52      * the vendor company name followed by a dot; for example, "vendorCompany.someVolume".</p>
53      *
54      * @param <T> type of value associated with {@code Key}.
55      */
56     /*
57      * Internal details:
58      * Conceivably metadata keys exposing multiple interfaces
59      * could be eligible to work in multiple framework domains.
60      */
61     public interface Key<T> {
62         /**
63          * Returns the internal name of the key.  The name should be unique in the
64          * {@code AudioMetadata} namespace.  Vendors should prefix their keys with
65          * the company name followed by a dot.
66          */
67         @NonNull
getName()68         String getName();
69 
70         /**
71          * Returns the class type {@code T} of the associated value.  Valid class types for
72          * {@link android.os.Build.VERSION_CODES#R} are
73          * {@code Integer.class}, {@code Long.class}, {@code Float.class}, {@code Double.class},
74          * {@code String.class}.
75          */
76         @NonNull
getValueClass()77         Class<T> getValueClass();
78 
79         // TODO: consider adding bool isValid(@NonNull T value)
80     }
81 
82     /**
83      * Creates a {@link AudioMetadataMap} suitable for adding keys.
84      * @return an empty {@link AudioMetadataMap} instance.
85      */
86     @NonNull
createMap()87     public static AudioMetadataMap createMap() {
88         return new BaseMap();
89     }
90 
91     /**
92      * A container class for AudioMetadata Format keys.
93      *
94      * @see AudioTrack.OnCodecFormatChangedListener
95      */
96     public static class Format {
97         // The key name strings used here must match that of the native framework, but are
98         // allowed to change between API releases.  This due to the Java specification
99         // on what is a compile time constant.
100         //
101         // Key<?> are final variables but not constant variables (per Java spec 4.12.4) because
102         // the keys are not a primitive type nor a String initialized by a constant expression.
103         // Hence (per Java spec 13.1.3), they are not resolved at compile time,
104         // rather are picked up by applications at run time.
105         //
106         // So the contractual API behavior of AudioMetadata.Key<> are different than Strings
107         // initialized by a constant expression (for example MediaFormat.KEY_*).
108 
109         // See MediaFormat
110         /**
111          * A key representing the bitrate of the encoded stream used in
112          *
113          * If the stream is variable bitrate, this is the average bitrate of the stream.
114          * The unit is bits per second.
115          *
116          * An Integer value.
117          *
118          * @see MediaFormat#KEY_BIT_RATE
119          */
120         @NonNull public static final Key<Integer> KEY_BIT_RATE =
121                 createKey("bitrate", Integer.class);
122 
123         /**
124          * A key representing the audio channel mask of the stream.
125          *
126          * An Integer value.
127          *
128          * @see AudioTrack#getChannelConfiguration()
129          * @see MediaFormat#KEY_CHANNEL_MASK
130          */
131         @NonNull public static final Key<Integer> KEY_CHANNEL_MASK =
132                 createKey("channel-mask", Integer.class);
133 
134 
135         /**
136          * A key representing the codec mime string.
137          *
138          * A String value.
139          *
140          * @see MediaFormat#KEY_MIME
141          */
142         @NonNull public static final Key<String> KEY_MIME = createKey("mime", String.class);
143 
144         /**
145          * A key representing the audio sample rate in Hz of the stream.
146          *
147          * An Integer value.
148          *
149          * @see AudioFormat#getSampleRate()
150          * @see MediaFormat#KEY_SAMPLE_RATE
151          */
152         @NonNull public static final Key<Integer> KEY_SAMPLE_RATE =
153                 createKey("sample-rate", Integer.class);
154 
155         // Unique to Audio
156 
157         /**
158          * A key representing the bit width of an element of decoded data.
159          *
160          * An Integer value.
161          */
162         @NonNull public static final Key<Integer> KEY_BIT_WIDTH =
163                 createKey("bit-width", Integer.class);
164 
165         /**
166          * A key representing the presence of Atmos in an E-AC3 stream.
167          *
168          * A Boolean value which is true if Atmos is present in an E-AC3 stream.
169          */
170 
171         // Since Boolean isn't handled by Parceling, we translate
172         // internally to KEY_HAS_ATMOS when sending through JNI.
173         // Consider deprecating this key for KEY_HAS_ATMOS in the future.
174         //
175         @NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT =
176                 createKey("atmos-present", Boolean.class);
177 
178         /**
179          * A key representing the presence of Atmos in an E-AC3 stream.
180          *
181          * An Integer value which is nonzero if Atmos is present in an E-AC3 stream.
182          * The integer representation is used for communication to the native side.
183          * @hide
184          */
185         @NonNull public static final Key<Integer> KEY_HAS_ATMOS =
186                 createKey("has-atmos", Integer.class);
187 
188         /**
189          * A key representing the audio encoding used for the stream.
190          * This is the same encoding used in {@link AudioFormat#getEncoding()}.
191          *
192          * An Integer value.
193          *
194          * @see AudioFormat#getEncoding()
195          */
196         @NonNull public static final Key<Integer> KEY_AUDIO_ENCODING =
197                 createKey("audio-encoding", Integer.class);
198 
199 
200         /**
201          * A key representing the audio presentation id being decoded by a next generation
202          * audio decoder.
203          *
204          * An Integer value representing presentation id.
205          *
206          * @see AudioPresentation#getPresentationId()
207          */
208         @NonNull public static final Key<Integer> KEY_PRESENTATION_ID =
209                 createKey("presentation-id", Integer.class);
210 
211          /**
212          * A key representing the audio program id being decoded by a next generation
213          * audio decoder.
214          *
215          * An Integer value representing program id.
216          *
217          * @see AudioPresentation#getProgramId()
218          */
219         @NonNull public static final Key<Integer> KEY_PROGRAM_ID =
220                 createKey("program-id", Integer.class);
221 
222 
223          /**
224          * A key representing the audio presentation content classifier being rendered
225          * by a next generation audio decoder.
226          *
227          * An Integer value representing presentation content classifier.
228          *
229          * @see AudioPresentation.ContentClassifier
230          * One of {@link AudioPresentation#CONTENT_UNKNOWN},
231          *     {@link AudioPresentation#CONTENT_MAIN},
232          *     {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS},
233          *     {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED},
234          *     {@link AudioPresentation#CONTENT_HEARING_IMPAIRED},
235          *     {@link AudioPresentation#CONTENT_DIALOG},
236          *     {@link AudioPresentation#CONTENT_COMMENTARY},
237          *     {@link AudioPresentation#CONTENT_EMERGENCY},
238          *     {@link AudioPresentation#CONTENT_VOICEOVER}.
239          */
240         @NonNull public static final Key<Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER =
241                 createKey("presentation-content-classifier", Integer.class);
242 
243         /**
244          * A key representing the audio presentation language being rendered by a next
245          * generation audio decoder.
246          *
247          * A String value representing ISO 639-2 (three letter code).
248          *
249          * @see AudioPresentation#getLocale()
250          */
251         @NonNull public static final Key<String> KEY_PRESENTATION_LANGUAGE =
252                 createKey("presentation-language", String.class);
253 
Format()254         private Format() {} // delete constructor
255     }
256 
257     /////////////////////////////////////////////////////////////////////////
258     // Hidden methods and functions.
259 
260     /**
261      * Returns a Key object with the correct interface for the AudioMetadata.
262      *
263      * An interface with the same name and type will be treated as
264      * identical for the purposes of value storage, even though
265      * other methods or hidden parameters may return different values.
266      *
267      * @param name The name of the key.
268      * @param type The class type of the value represented by the key.
269      * @param <T> The type of value.
270      * @return a new key interface.
271      *
272      * Creating keys is currently only allowed by the Framework.
273      * @hide
274      */
275     @NonNull
createKey(@onNull String name, @NonNull Class<T> type)276     public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) {
277         // Implementation specific.
278         return new Key<T>() {
279             private final String mName = name;
280             private final Class<T> mType = type;
281 
282             @Override
283             @NonNull
284             public String getName() {
285                 return mName;
286             }
287 
288             @Override
289             @NonNull
290             public Class<T> getValueClass() {
291                 return mType;
292             }
293 
294             /**
295              * Return true if the name and the type of two objects are the same.
296              */
297             @Override
298             public boolean equals(Object obj) {
299                 if (obj == this) {
300                     return true;
301                 }
302                 if (!(obj instanceof Key)) {
303                     return false;
304                 }
305                 Key<?> other = (Key<?>) obj;
306                 return mName.equals(other.getName()) && mType.equals(other.getValueClass());
307             }
308 
309             @Override
310             public int hashCode() {
311                 return Objects.hash(mName, mType);
312             }
313         };
314     }
315 
316     /**
317      * @hide
318      *
319      * AudioMetadata is based on interfaces in order to allow multiple inheritance
320      * and maximum flexibility in implementation.
321      *
322      * Here, we provide a simple implementation of {@link Map} interface;
323      * Note that the Keys are not specific to this Map implementation.
324      *
325      * It is possible to require the keys to be of a certain class
326      * before allowing a set or get operation.
327      */
328     public static class BaseMap implements AudioMetadataMap {
329         @Override
containsKey(@onNull Key<T> key)330         public <T> boolean containsKey(@NonNull Key<T> key) {
331             Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key));
332             return valuePair != null;
333         }
334 
335         @Override
336         @NonNull
dup()337         public AudioMetadataMap dup() {
338             BaseMap map = new BaseMap();
339             map.mHashMap.putAll(this.mHashMap);
340             return map;
341         }
342 
343         @Override
344         @Nullable
get(@onNull Key<T> key)345         public <T> T get(@NonNull Key<T> key) {
346             Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key));
347             return (T) getValueFromValuePair(valuePair);
348         }
349 
350         @Override
351         @NonNull
keySet()352         public Set<Key<?>> keySet() {
353             HashSet<Key<?>> set = new HashSet();
354             for (Pair<Key<?>, Object> pair : mHashMap.values()) {
355                 set.add(pair.first);
356             }
357             return set;
358         }
359 
360         @Override
361         @Nullable
remove(@onNull Key<T> key)362         public <T> T remove(@NonNull Key<T> key) {
363             Pair<Key<?>, Object> valuePair = mHashMap.remove(pairFromKey(key));
364             return (T) getValueFromValuePair(valuePair);
365         }
366 
367         @Override
368         @Nullable
set(@onNull Key<T> key, @NonNull T value)369         public <T> T set(@NonNull Key<T> key, @NonNull T value) {
370             Objects.requireNonNull(value);
371             Pair<Key<?>, Object> valuePair = mHashMap
372                     .put(pairFromKey(key), new Pair<Key<?>, Object>(key, value));
373             return (T) getValueFromValuePair(valuePair);
374         }
375 
376         @Override
size()377         public int size() {
378             return mHashMap.size();
379         }
380 
381         /**
382          * Return true if the object is a BaseMap and the content from two BaseMap are the same.
383          * Note: Need to override the equals functions of Key<T> for HashMap comparison.
384          */
385         @Override
equals(Object obj)386         public boolean equals(Object obj) {
387             if (obj == this) {
388                 return true;
389             }
390             if (!(obj instanceof BaseMap)) {
391                 return false;
392             }
393             BaseMap other = (BaseMap) obj;
394             return mHashMap.equals(other.mHashMap);
395         }
396 
397         @Override
hashCode()398         public int hashCode() {
399             return Objects.hash(mHashMap);
400         }
401 
402         /*
403          * Implementation specific.
404          *
405          * To store the value in the HashMap we need to convert the Key interface
406          * to a hashcode() / equals() compliant Pair.
407          */
408         @NonNull
pairFromKey(@onNull Key<T> key)409         private static <T> Pair<String, Class<?>> pairFromKey(@NonNull Key<T> key) {
410             Objects.requireNonNull(key);
411             return new Pair<String, Class<?>>(key.getName(), key.getValueClass());
412         }
413 
414         /*
415          * Implementation specific.
416          *
417          * We store in a Pair (valuePair) the key along with the Object value.
418          * This helper returns the Object value from the value pair.
419          */
420         @Nullable
getValueFromValuePair(@ullable Pair<Key<?>, Object> valuePair)421         private static Object getValueFromValuePair(@Nullable Pair<Key<?>, Object> valuePair) {
422             if (valuePair == null) {
423                 return null;
424             }
425             return valuePair.second;
426         }
427 
428         /*
429          * Implementation specific.
430          *
431          * We use a HashMap to back the AudioMetadata BaseMap object.
432          * This is not locked, so concurrent reads are permitted if all threads
433          * have a ReadMap; this is risky with a Map.
434          */
435         private final HashMap<Pair<String, Class<?>>, Pair<Key<?>, Object>> mHashMap =
436                 new HashMap();
437     }
438 
439     // The audio metadata object type index should be kept the same as
440     // the ones in audio_utils::metadata::metadata_types
441     private static final int AUDIO_METADATA_OBJ_TYPE_NONE = 0;
442     private static final int AUDIO_METADATA_OBJ_TYPE_INT = 1;
443     private static final int AUDIO_METADATA_OBJ_TYPE_LONG = 2;
444     private static final int AUDIO_METADATA_OBJ_TYPE_FLOAT = 3;
445     private static final int AUDIO_METADATA_OBJ_TYPE_DOUBLE = 4;
446     private static final int AUDIO_METADATA_OBJ_TYPE_STRING = 5;
447     // BaseMap is corresponding to audio_utils::metadata::Data
448     private static final int AUDIO_METADATA_OBJ_TYPE_BASEMAP = 6;
449 
450     private static final Map<Class, Integer> AUDIO_METADATA_OBJ_TYPES = Map.of(
451             Integer.class, AUDIO_METADATA_OBJ_TYPE_INT,
452             Long.class, AUDIO_METADATA_OBJ_TYPE_LONG,
453             Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT,
454             Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE,
455             String.class, AUDIO_METADATA_OBJ_TYPE_STRING,
456             BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP);
457 
458     private static final Charset AUDIO_METADATA_CHARSET = StandardCharsets.UTF_8;
459 
460     /**
461      * An auto growing byte buffer
462      */
463     private static class AutoGrowByteBuffer {
464         private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
465         private static final int LONG_BYTE_COUNT = Long.SIZE / Byte.SIZE;
466         private static final int FLOAT_BYTE_COUNT = Float.SIZE / Byte.SIZE;
467         private static final int DOUBLE_BYTE_COUNT = Double.SIZE / Byte.SIZE;
468 
469         private ByteBuffer mBuffer;
470 
AutoGrowByteBuffer()471         AutoGrowByteBuffer() {
472             this(1024);
473         }
474 
AutoGrowByteBuffer(@ntRangefrom = 0) int initialCapacity)475         AutoGrowByteBuffer(@IntRange(from = 0) int initialCapacity) {
476             mBuffer = ByteBuffer.allocateDirect(initialCapacity);
477         }
478 
getRawByteBuffer()479         public ByteBuffer getRawByteBuffer() {
480             // Slice the buffer from 0 to position.
481             int limit = mBuffer.limit();
482             int position = mBuffer.position();
483             mBuffer.limit(position);
484             mBuffer.position(0);
485             ByteBuffer buffer = mBuffer.slice();
486 
487             // Restore position and limit.
488             mBuffer.limit(limit);
489             mBuffer.position(position);
490             return buffer;
491         }
492 
order()493         public ByteOrder order() {
494             return mBuffer.order();
495         }
496 
position()497         public int position() {
498             return mBuffer.position();
499         }
500 
position(int newPosition)501         public AutoGrowByteBuffer position(int newPosition) {
502             mBuffer.position(newPosition);
503             return this;
504         }
505 
order(ByteOrder order)506         public AutoGrowByteBuffer order(ByteOrder order) {
507             mBuffer.order(order);
508             return this;
509         }
510 
putInt(int value)511         public AutoGrowByteBuffer putInt(int value) {
512             ensureCapacity(INTEGER_BYTE_COUNT);
513             mBuffer.putInt(value);
514             return this;
515         }
516 
putLong(long value)517         public AutoGrowByteBuffer putLong(long value) {
518             ensureCapacity(LONG_BYTE_COUNT);
519             mBuffer.putLong(value);
520             return this;
521         }
522 
putFloat(float value)523         public AutoGrowByteBuffer putFloat(float value) {
524             ensureCapacity(FLOAT_BYTE_COUNT);
525             mBuffer.putFloat(value);
526             return this;
527         }
528 
putDouble(double value)529         public AutoGrowByteBuffer putDouble(double value) {
530             ensureCapacity(DOUBLE_BYTE_COUNT);
531             mBuffer.putDouble(value);
532             return this;
533         }
534 
put(byte[] src)535         public AutoGrowByteBuffer put(byte[] src) {
536             ensureCapacity(src.length);
537             mBuffer.put(src);
538             return this;
539         }
540 
541         /**
542          * Ensures capacity to append at least <code>count</code> values.
543          */
ensureCapacity(@ntRange int count)544         private void ensureCapacity(@IntRange int count) {
545             if (mBuffer.remaining() < count) {
546                 int newCapacity = mBuffer.position() + count;
547                 if (newCapacity > Integer.MAX_VALUE >> 1) {
548                     throw new IllegalStateException(
549                             "Item memory requirements too large: " + newCapacity);
550                 }
551                 newCapacity <<= 1;
552                 ByteBuffer buffer = ByteBuffer.allocateDirect(newCapacity);
553                 buffer.order(mBuffer.order());
554 
555                 // Copy data from old buffer to new buffer
556                 mBuffer.flip();
557                 buffer.put(mBuffer);
558 
559                 // Set buffer to new buffer
560                 mBuffer = buffer;
561             }
562         }
563     }
564 
565     /**
566      * @hide
567      * Describes a unpacking/packing contract of type {@code T} out of a {@link ByteBuffer}
568      *
569      * @param <T> the type being unpack
570      */
571     private interface DataPackage<T> {
572         /**
573          * Read an item from a {@link ByteBuffer}.
574          *
575          * The parceling format is assumed the same as the one described in
576          * audio_utils::Metadata.h. Copied here as a reference.
577          * All values are native endian order.
578          *
579          * Datum = { (type_size_t)  Type (the type index from type_as_value<T>.)
580          *           (datum_size_t) Size (size of datum, including the size field)
581          *           (byte string)  Payload<Type>
582          *         }
583          *
584          * Primitive types:
585          * Payload<Type> = { bytes in native endian order }
586          *
587          * Vector, Map, Container types:
588          * Payload<Type> = { (index_size_t) number of elements
589          *                   (byte string)  Payload<Element_Type> * number
590          *                 }
591          *
592          * Pair container types:
593          * Payload<Type> = { (byte string) Payload<first>,
594          *                   (byte string) Payload<second>
595          *                 }
596          *
597          * @param buffer the byte buffer to read from
598          * @return an object, which types is given type for {@link DataPackage}
599          * @throws BufferUnderflowException when there is no enough data remaining
600          *      in the buffer for unpacking.
601          */
602         @Nullable
unpack(ByteBuffer buffer)603         T unpack(ByteBuffer buffer);
604 
605         /**
606          * Pack the item into a byte array. This is the reversed way of unpacking.
607          *
608          * @param output is the stream to which to write the data
609          * @param obj the item to pack
610          * @return true if packing successfully. Otherwise, return false.
611          */
pack(AutoGrowByteBuffer output, T obj)612         boolean pack(AutoGrowByteBuffer output, T obj);
613 
614         /**
615          * Return what kind of data is contained in the package.
616          */
getMyType()617         default Class getMyType() {
618             return (Class) ((ParameterizedType) getClass().getGenericInterfaces()[0])
619                     .getActualTypeArguments()[0];
620         }
621     }
622 
623     /*****************************************************************************************
624      * Following class are common {@link DataPackage} implementations, which include types
625      * that are defined in audio_utils::metadata::metadata_types
626      *
627      * For Java
628      *     int32_t corresponds to Integer
629      *     int64_t corresponds to Long
630      *     float corresponds to Float
631      *     double corresponds to Double
632      *     std::string corresponds to String
633      *     Data corresponds to BaseMap
634      *     Datum corresponds to Object
635      ****************************************************************************************/
636 
637     private static final Map<Integer, DataPackage<?>> DATA_PACKAGES = Map.of(
638             AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage<Integer>() {
639                 @Override
640                 @Nullable
641                 public Integer unpack(ByteBuffer buffer) {
642                     return buffer.getInt();
643                 }
644 
645                 @Override
646                 public boolean pack(AutoGrowByteBuffer output, Integer obj) {
647                     output.putInt(obj);
648                     return true;
649                 }
650             },
651             AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage<Long>() {
652                 @Override
653                 @Nullable
654                 public Long unpack(ByteBuffer buffer) {
655                     return buffer.getLong();
656                 }
657 
658                 @Override
659                 public boolean pack(AutoGrowByteBuffer output, Long obj) {
660                     output.putLong(obj);
661                     return true;
662                 }
663             },
664             AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage<Float>() {
665                 @Override
666                 @Nullable
667                 public Float unpack(ByteBuffer buffer) {
668                     return buffer.getFloat();
669                 }
670 
671                 @Override
672                 public boolean pack(AutoGrowByteBuffer output, Float obj) {
673                     output.putFloat(obj);
674                     return true;
675                 }
676             },
677             AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage<Double>() {
678                 @Override
679                 @Nullable
680                 public Double unpack(ByteBuffer buffer) {
681                     return buffer.getDouble();
682                 }
683 
684                 @Override
685                 public boolean pack(AutoGrowByteBuffer output, Double obj) {
686                     output.putDouble(obj);
687                     return true;
688                 }
689             },
690             AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage<String>() {
691                 @Override
692                 @Nullable
693                 public String unpack(ByteBuffer buffer) {
694                     int dataSize = buffer.getInt();
695                     if (buffer.position() + dataSize > buffer.limit()) {
696                         return null;
697                     }
698                     byte[] valueArr = new byte[dataSize];
699                     buffer.get(valueArr);
700                     String value = new String(valueArr, AUDIO_METADATA_CHARSET);
701                     return value;
702                 }
703 
704                 /**
705                  * This is a reversed operation of unpack. It is needed to write the String
706                  * at bytes encoded with AUDIO_METADATA_CHARSET. There should be an integer
707                  * value representing the length of the bytes written before the bytes.
708                  */
709                 @Override
710                 public boolean pack(AutoGrowByteBuffer output, String obj) {
711                     byte[] valueArr = obj.getBytes(AUDIO_METADATA_CHARSET);
712                     output.putInt(valueArr.length);
713                     output.put(valueArr);
714                     return true;
715                 }
716             },
717             AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage());
718 
719     // ObjectPackage is a special case that it is expected to unpack audio_utils::metadata::Datum,
720     // which contains data type and data size besides the payload for the data.
721     private static final ObjectPackage OBJECT_PACKAGE = new ObjectPackage();
722 
723     private static class ObjectPackage implements DataPackage<Pair<Class, Object>> {
724         /**
725          * The {@link ObjectPackage} will unpack byte string for audio_utils::metadata::Datum.
726          * Since the Datum is a std::any, {@link Object} is used to carrying the data. The
727          * data type is stored in the data package header. In that case, a {@link Class}
728          * will also be returned to indicate the actual type for the object.
729          */
730         @Override
731         @Nullable
unpack(ByteBuffer buffer)732         public Pair<Class, Object> unpack(ByteBuffer buffer) {
733             int dataType = buffer.getInt();
734             DataPackage dataPackage = DATA_PACKAGES.get(dataType);
735             if (dataPackage == null) {
736                 Log.e(TAG, "Cannot find DataPackage for type:" + dataType);
737                 return null;
738             }
739             int dataSize = buffer.getInt();
740             int position = buffer.position();
741             Object obj = dataPackage.unpack(buffer);
742             if (buffer.position() - position != dataSize) {
743                 Log.e(TAG, "Broken data package");
744                 return null;
745             }
746             return new Pair<Class, Object>(dataPackage.getMyType(), obj);
747         }
748 
749         @Override
pack(AutoGrowByteBuffer output, Pair<Class, Object> obj)750         public boolean pack(AutoGrowByteBuffer output, Pair<Class, Object> obj) {
751             final Integer dataType = AUDIO_METADATA_OBJ_TYPES.get(obj.first);
752             if (dataType == null) {
753                 Log.e(TAG, "Cannot find data type for " + obj.first);
754                 return false;
755             }
756             DataPackage dataPackage = DATA_PACKAGES.get(dataType);
757             if (dataPackage == null) {
758                 Log.e(TAG, "Cannot find DataPackage for type:" + dataType);
759                 return false;
760             }
761             output.putInt(dataType);
762             int position = output.position(); // Keep current position.
763             output.putInt(0); // Keep a place for the size of payload.
764             int payloadIdx = output.position();
765             if (!dataPackage.pack(output, obj.second)) {
766                 Log.i(TAG, "Failed to pack object: " + obj.second);
767                 return false;
768             }
769             // Put the actual payload size.
770             int currentPosition = output.position();
771             output.position(position);
772             output.putInt(currentPosition - payloadIdx);
773             output.position(currentPosition);
774             return true;
775         }
776     }
777 
778     /**
779      * BaseMap will be corresponding to audio_utils::metadata::Data.
780      */
781     private static class BaseMapPackage implements DataPackage<BaseMap> {
782         @Override
783         @Nullable
unpack(ByteBuffer buffer)784         public BaseMap unpack(ByteBuffer buffer) {
785             BaseMap ret = new BaseMap();
786             int mapSize = buffer.getInt();
787             DataPackage<String> strDataPackage =
788                     (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING);
789             if (strDataPackage == null) {
790                 Log.e(TAG, "Cannot find DataPackage for String");
791                 return null;
792             }
793             for (int i = 0; i < mapSize; i++) {
794                 String key = strDataPackage.unpack(buffer);
795                 if (key == null) {
796                     Log.e(TAG, "Failed to unpack key for map");
797                     return null;
798                 }
799                 Pair<Class, Object> value = OBJECT_PACKAGE.unpack(buffer);
800                 if (value == null) {
801                     Log.e(TAG, "Failed to unpack value for map");
802                     return null;
803                 }
804 
805                 // Special handling of KEY_ATMOS_PRESENT.
806                 if (key.equals(Format.KEY_HAS_ATMOS.getName())
807                         && value.first == Format.KEY_HAS_ATMOS.getValueClass()) {
808                     ret.set(Format.KEY_ATMOS_PRESENT,
809                             (Boolean) ((int) value.second != 0));  // Translate Integer to Boolean
810                     continue; // Should we store both keys in the java table?
811                 }
812 
813                 ret.set(createKey(key, value.first), value.first.cast(value.second));
814             }
815             return ret;
816         }
817 
818         @Override
pack(AutoGrowByteBuffer output, BaseMap obj)819         public boolean pack(AutoGrowByteBuffer output, BaseMap obj) {
820             output.putInt(obj.size());
821             DataPackage<String> strDataPackage =
822                     (DataPackage<String>) DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_STRING);
823             if (strDataPackage == null) {
824                 Log.e(TAG, "Cannot find DataPackage for String");
825                 return false;
826             }
827             for (Key<?> key : obj.keySet()) {
828                 Object value = obj.get(key);
829 
830                 // Special handling of KEY_ATMOS_PRESENT.
831                 if (key == Format.KEY_ATMOS_PRESENT) {
832                     key = Format.KEY_HAS_ATMOS;
833                     value = (Integer) ((boolean) value ? 1 : 0); // Translate Boolean to Integer
834                 }
835 
836                 if (!strDataPackage.pack(output, key.getName())) {
837                     Log.i(TAG, "Failed to pack key: " + key.getName());
838                     return false;
839                 }
840                 if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), value))) {
841                     Log.i(TAG, "Failed to pack value: " + obj.get(key));
842                     return false;
843                 }
844             }
845             return true;
846         }
847     }
848 
849     /**
850      * @hide
851      * Extract a {@link BaseMap} from a given {@link ByteBuffer}
852      * @param buffer is a byte string that contains information to unpack.
853      * @return a {@link BaseMap} object if extracting successfully from given byte buffer.
854      *     Otherwise, returns {@code null}.
855      */
856     @Nullable
fromByteBuffer(ByteBuffer buffer)857     public static BaseMap fromByteBuffer(ByteBuffer buffer) {
858         DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP);
859         if (dataPackage == null) {
860             Log.e(TAG, "Cannot find DataPackage for BaseMap");
861             return null;
862         }
863         try {
864             return (BaseMap) dataPackage.unpack(buffer);
865         } catch (BufferUnderflowException e) {
866             Log.e(TAG, "No enough data to unpack");
867         }
868         return null;
869     }
870 
871     /**
872      * @hide
873      * Pack a {link BaseMap} to a {@link ByteBuffer}
874      * @param data is the object for packing
875      * @param order is the byte order
876      * @return a {@link ByteBuffer} if successfully packing the data.
877      *     Otherwise, returns {@code null};
878      */
879     @Nullable
toByteBuffer(BaseMap data, ByteOrder order)880     public static ByteBuffer toByteBuffer(BaseMap data, ByteOrder order) {
881         DataPackage dataPackage = DATA_PACKAGES.get(AUDIO_METADATA_OBJ_TYPE_BASEMAP);
882         if (dataPackage == null) {
883             Log.e(TAG, "Cannot find DataPackage for BaseMap");
884             return null;
885         }
886         AutoGrowByteBuffer output = new AutoGrowByteBuffer();
887         output.order(order);
888         if (dataPackage.pack(output, data)) {
889             return output.getRawByteBuffer();
890         }
891         return null;
892     }
893 
894     // Delete the constructor as there is nothing to implement here.
AudioMetadata()895     private AudioMetadata() {}
896 }
897