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