CTA2075: create loudness params for AAC MPEG-4 and MPEG-D

The bundle values are defined in the CTA-2075 standard.

Currently taking as input signals: isDownmixing, metadataType and
transducer spl range. We assume the environment noise and user input is
"unknown".

Test: adb shell device_config put media_audio android.media.audio.loudness_configurator_api true
Test: atest LoundessCodecHelperTest
Bug: 298463873
Change-Id: Ia450fde816cdc5dcf898b4a5fd597b7e77768ff9
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 3c67e9d..c306e3c 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -21,6 +21,11 @@
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
 import static android.media.AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -41,6 +46,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -70,10 +76,14 @@
     private static final String SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE =
             "audio.loudness.builtin-speaker-spl-range-size";
 
-    private static final int SPL_RANGE_UNKNOWN = 0;
-    private static final int SPL_RANGE_SMALL = 1;
-    private static final int SPL_RANGE_MEDIUM = 2;
-    private static final int SPL_RANGE_LARGE = 3;
+    @VisibleForTesting
+    static final int SPL_RANGE_UNKNOWN = 0;
+    @VisibleForTesting
+    static final int SPL_RANGE_SMALL = 1;
+    @VisibleForTesting
+    static final int SPL_RANGE_MEDIUM = 2;
+    @VisibleForTesting
+    static final int SPL_RANGE_LARGE = 3;
 
     /** The possible transducer SPL ranges as defined in CTA2075 */
     @IntDef({
@@ -125,7 +135,8 @@
     private final AudioService mAudioService;
 
     /** Contains the properties necessary to compute the codec loudness related parameters. */
-    private static final class LoudnessCodecInputProperties {
+    @VisibleForTesting
+    static final class LoudnessCodecInputProperties {
         private final int mMetadataType;
 
         private final boolean mIsDownmixing;
@@ -200,10 +211,53 @@
         }
 
         PersistableBundle createLoudnessParameters() {
-            // TODO: create bundle with new parameters
-            return new PersistableBundle();
-        }
+            PersistableBundle loudnessParams = new PersistableBundle();
 
+            switch (mDeviceSplRange) {
+                case SPL_RANGE_LARGE:
+                    // corresponds to -31dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 124);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+                case SPL_RANGE_MEDIUM:
+                    // corresponds to -24dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+                case SPL_RANGE_SMALL:
+                    // corresponds to -16dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 64);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 1);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // limited playback range compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 3);
+                    }
+                    break;
+                default:
+                    // corresponds to -24dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+            }
+
+            return loudnessParams;
+        }
     }
 
     @GuardedBy("mLock")
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 749b07d..42c5205 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -19,6 +19,16 @@
 import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
 import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
 import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
+
+import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -34,11 +44,15 @@
 import android.media.LoudnessCodecInfo;
 import android.media.PlayerBase;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
+import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -200,6 +214,108 @@
                 any());
     }
 
+    @Test
+    public void checkParcelableBundle_forMpeg4CodecInputProperties() {
+        PersistableBundle loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+    }
+
+    @Test
+    public void checkParcelableBundle_forMpegDCodecInputProperties() {
+        PersistableBundle loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+    }
+
     private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
         final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
 
@@ -220,6 +336,12 @@
         return apcList;
     }
 
+    private static LoudnessCodecInputProperties createInputProperties(
+            int metadataType, boolean isDownmixing, @DeviceSplRange int splRange) {
+        return new LoudnessCodecInputProperties.Builder().setMetadataType(
+                metadataType).setIsDownmixing(isDownmixing).setDeviceSplRange(splRange).build();
+    }
+
     private static LoudnessCodecInfo getLoudnessInfo(int mediaCodecHash, boolean isDownmixing,
             int metadataType) {
         LoudnessCodecInfo info = new LoudnessCodecInfo();