blob: bafd83dc551ae82c88e31f343d207d463c629ba1 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.cts;
import static org.junit.Assert.*;
import static org.testng.Assert.assertThrows;
import android.media.AudioMetadata;
import android.media.AudioMetadataMap;
import android.media.AudioMetadataReadMap;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@NonMediaMainlineTest
@RunWith(AndroidJUnit4.class)
public class AudioMetadataTest {
// Trial keys to test with.
private static final AudioMetadata.Key<Integer>
KEY_INTEGER = AudioMetadata.createKey("integer", Integer.class);
private static final AudioMetadata.Key<Number>
KEY_NUMBER = AudioMetadata.createKey("number", Number.class);
private static final AudioMetadata.Key<String>
KEY_STRING = AudioMetadata.createKey("string", String.class);
private static final AudioMetadata.Key<Long>
KEY_LONG = AudioMetadata.createKey("long", Long.class);
private static final AudioMetadata.Key<Float>
KEY_FLOAT = AudioMetadata.createKey("float", Float.class);
private static final AudioMetadata.Key<Double>
KEY_DOUBLE = AudioMetadata.createKey("double", Double.class);
private static final AudioMetadata.Key<AudioMetadata.BaseMap>
KEY_BASE_MAP = AudioMetadata.createKey("data", AudioMetadata.BaseMap.class);
@Test
public void testKey() throws Exception {
assertEquals("integer", KEY_INTEGER.getName());
assertEquals("number", KEY_NUMBER.getName());
assertEquals("string", KEY_STRING.getName());
assertEquals(Integer.class, KEY_INTEGER.getValueClass());
assertEquals(Number.class, KEY_NUMBER.getValueClass());
assertEquals(String.class, KEY_STRING.getValueClass());
}
@Test
public void testMap() throws Exception {
final AudioMetadataMap audioMetadata = AudioMetadata.createMap();
int ivalue;
String svalue;
audioMetadata.set(KEY_INTEGER, 10);
ivalue = audioMetadata.get(KEY_INTEGER);
assertEquals(10, ivalue);
// Because the get is typed, the following cannot compile.
// audioMetadata.set(KEY_INTEGER, "abc");
// String svalue = audioMetadata.get(KEY_INTEGER);
assertEquals(1, audioMetadata.size());
audioMetadata.set(KEY_STRING, "abc");
svalue = audioMetadata.get(KEY_STRING);
assertEquals("abc", svalue);
// Because the set is typed, the following cannot compile
// audioMetadata.set(KEY_STRING, 10);
// ivalue = audioMetadata.get(KEY_STRING);
assertEquals(2, audioMetadata.size());
assertTrue(audioMetadata.containsKey(KEY_STRING));
// We should be able to remove the string
svalue = audioMetadata.remove(KEY_STRING);
assertEquals("abc", svalue);
assertEquals(1, audioMetadata.size());
assertFalse(audioMetadata.containsKey(KEY_STRING));
assertTrue(audioMetadata.containsKey(KEY_INTEGER));
// Try a generic Number.
Number nvalue;
audioMetadata.set(KEY_NUMBER, 2.125f);
nvalue = audioMetadata.get(KEY_NUMBER);
assertEquals(2.125f, nvalue.floatValue(), 0.f);
// Verify we handle null properly.
assertThrows(NullPointerException.class,
() -> { audioMetadata.get(null); }
);
assertThrows(NullPointerException.class,
() -> { audioMetadata.set(null, 1); }
);
assertThrows(NullPointerException.class,
() -> { audioMetadata.set(KEY_NUMBER, null); }
);
// check creating a map from another map.
assertEquals(audioMetadata, audioMetadata.dup());
}
// Vendor keys created by direct override of the AudioMetadata interface.
private static final AudioMetadata.Key<Integer>
KEY_VENDOR_INTEGER = new AudioMetadata.Key<Integer>() {
@Override
public String getName() {
return "vendor.integerData";
}
@Override
public Class<Integer> getValueClass() {
return Integer.class; // matches Class<Integer>
}
};
private static final AudioMetadata.Key<Double>
KEY_VENDOR_DOUBLE = new AudioMetadata.Key<Double>() {
@Override
public String getName() {
return "vendor.doubleData";
}
@Override
public Class<Double> getValueClass() {
return Double.class; // matches Class<Double>
}
};
private static final AudioMetadata.Key<String>
KEY_VENDOR_STRING = new AudioMetadata.Key<String>() {
@Override
public String getName() {
return "vendor.stringData";
}
@Override
public Class<String> getValueClass() {
return String.class; // matches Class<String>
}
};
@Test
public void testVendorKeys() {
final AudioMetadataMap audioMetadata = AudioMetadata.createMap();
audioMetadata.set(KEY_VENDOR_INTEGER, 10);
final int ivalue = audioMetadata.get(KEY_VENDOR_INTEGER);
assertEquals(10, ivalue);
audioMetadata.set(KEY_VENDOR_DOUBLE, 11.5);
final double dvalue = audioMetadata.get(KEY_VENDOR_DOUBLE);
assertEquals(11.5, dvalue, 0. /* epsilon */);
audioMetadata.set(KEY_VENDOR_STRING, "alphabet");
final String svalue = audioMetadata.get(KEY_VENDOR_STRING);
assertEquals("alphabet", svalue);
}
// The byte buffer here is generated by audio_utils::metadata::byteStringFromData(Data).
// Data data = {
// "integer": (int32_t) 1,
// "long": (int64_t) 2,
// "float": (float) 3.1f,
// "double": (double) 4.11,
// "data": (Data) {
// "string": (std::string) "hello",
// }
// }
// Use to test compatibility of packing/unpacking audio metadata.
// DO NOT CHANGE after R
private static final byte[] BYTE_BUFFER_REFERENCE = new byte[] {
// Number of items
0x05, 0x00, 0x00, 0x00,
// Length of 1st key
0x04, 0x00, 0x00, 0x00,
// Payload of 1st key
0x64, 0x61, 0x74, 0x61,
// Data type of 1st value
0x06, 0x00, 0x00, 0x00,
// Length of 1st value
0x1f, 0x00, 0x00, 0x00,
// Payload of 1st value
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x05, 0x00,
0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
// Length of 2nd key
0x06, 0x00, 0x00, 0x00,
// Payload of 2nd key
0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65,
// Data type of 2nd value
0x04, 0x00, 0x00, 0x00,
// Length of 2nd value
0x08, 0x00, 0x00, 0x00,
// Payload of 2nd value
0x71, 0x3d, 0x0a, (byte) 0xd7, (byte) 0xa3, 0x70, 0x10, 0x40,
// Length of 3rd key
0x05, 0x00, 0x00, 0x00,
// Payload of 3rd key
0x66, 0x6c, 0x6f, 0x61, 0x74,
// Data type of 3rd value
0x03, 0x00, 0x00, 0x00,
// Length of 3rd value
0x04, 0x00, 0x00, 0x00,
// Payload of 3rd value
0x66, 0x66, 0x46, 0x40,
// Length of 4th key
0x07, 0x00, 0x00, 0x00,
// Payload of 4th key
0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72,
// Data type of 4th value
0x01, 0x00, 0x00, 0x00,
// Length of 4th value
0x04, 0x00, 0x00, 0x00,
// Payload of 4th value
0x01, 0x00, 0x00, 0x00,
// Length of 5th key
0x04, 0x00, 0x00, 0x00,
// Payload of 5th key
0x6c, 0x6f, 0x6e, 0x67,
// Data type of 5th value
0x02, 0x00, 0x00, 0x00,
// Length of 5th value
0x08, 0x00, 0x00, 0x00,
// Payload of 5th value
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// AUDIO_METADATA_REFERENCE corresponds to BYTE_BUFFER_REFERENCE.
// DO NOT CHANGE after R
private static final AudioMetadata.BaseMap AUDIO_METADATA_REFERENCE =
new AudioMetadata.BaseMap() {{
set(KEY_INTEGER, 1);
set(KEY_LONG, (long) 2);
set(KEY_FLOAT, 3.1f);
set(KEY_DOUBLE, 4.11);
set(KEY_BASE_MAP, new AudioMetadata.BaseMap() {{
set(KEY_STRING, "hello");
}});
}};
// Position of data type for first value in reference buffer.
// Will be used to set an invalid data type for test.
private static final int FIRST_VALUE_DATA_TYPE_POSITION_IN_REFERENCE = 12;
private static final byte INVALID_DATA_TYPE = (byte) 0xff;
@Test
public void testCompatibilityR() {
ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
AUDIO_METADATA_REFERENCE, ByteOrder.nativeOrder());
assertNotNull(bufferPackedAtJava);
ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
assertNotNull(buffer);
ByteBuffer refBuffer = ByteBuffer.allocate(BYTE_BUFFER_REFERENCE.length);
refBuffer.put(BYTE_BUFFER_REFERENCE);
refBuffer.position(0);
assertEquals(buffer, refBuffer);
}
@Test
public void testUnpackingByteBuffer() throws Exception {
ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
AUDIO_METADATA_REFERENCE, ByteOrder.nativeOrder());
assertNotNull(bufferPackedAtJava);
ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
assertNotNull(buffer);
buffer.order(ByteOrder.nativeOrder());
AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
assertNotNull(metadataFromByteBuffer);
assertEquals(metadataFromByteBuffer, AUDIO_METADATA_REFERENCE);
}
@Test
public void testUnpackingInvalidBuffer() throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(BYTE_BUFFER_REFERENCE.length);
buffer.put(BYTE_BUFFER_REFERENCE);
buffer.order(ByteOrder.nativeOrder());
// Manually modify the buffer to create an invalid buffer with an invalid data type,
// a null should be returned when unpacking.
buffer.position(FIRST_VALUE_DATA_TYPE_POSITION_IN_REFERENCE);
buffer.put(INVALID_DATA_TYPE);
buffer.position(0);
AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
assertNull(metadataFromByteBuffer);
}
static {
System.loadLibrary("audio_jni");
}
/**
* Passing a ByteBuffer that contains audio metadata. In native, the buffer will be
* unpacked as native audio metadata and then reconstructed as ByteBuffer back to Java.
* This is aimed at testing round trip (un)packing logic.
*/
private static native ByteBuffer nativeGetByteBuffer(ByteBuffer buffer, int sizeInBytes);
}