Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43bb0cd3 authored by Ján Sebechlebský's avatar Ján Sebechlebský Committed by Android (Google) Code Review
Browse files

Merge "Rewrite AudioMix validation & add more test coverage"

parents 49402212 1ec38fbd
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1341,6 +1341,11 @@ public class AudioSystem
                || isBluetoothLeInDevice(deviceType);
    }

    /** @hide */
    public static boolean isRemoteSubmixDevice(int deviceType) {
        return deviceType == DEVICE_IN_REMOTE_SUBMIX || deviceType == DEVICE_OUT_REMOTE_SUBMIX;
    }

    /** @hide */
    public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0";

+39 −23
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.media.audiopolicy;

import static android.media.AudioSystem.getDeviceName;
import static android.media.AudioSystem.isRemoteSubmixDevice;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -445,36 +448,36 @@ public class AudioMix {
                    // CHANNEL_IN_FRONT_BACK is hidden, should not appear.
                }
            }
            if ((mDeviceSystemType != AudioSystem.DEVICE_NONE)
                    && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)
                    && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) {
                if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) {
                    throw new IllegalArgumentException(
                            "Can't have audio device without flag ROUTE_FLAG_RENDER");
                }
                if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
                    throw new IllegalArgumentException("Unsupported device on non-playback mix");

            if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) {
                if (mDeviceSystemType == AudioSystem.DEVICE_NONE) {
                    // If there was no device type explicitly set, configure it based on mix type.
                    mDeviceSystemType = getLoopbackDeviceSystemTypeForAudioMixingRule(mRule);
                } else if (!isRemoteSubmixDevice(mDeviceSystemType)) {
                    // Loopback mode only supports remote submix devices.
                    throw new IllegalArgumentException("Device " + getDeviceName(mDeviceSystemType)
                            + "is not supported for loopback mix.");
                }
            } else if (mDeviceSystemType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) {
                if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
                    throw new IllegalArgumentException(
                            "DEVICE_OUT_REMOTE_SUBMIX device is not supported on non-playback mix");
            }
            } else {
                if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) {

            if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) {
                if (mDeviceSystemType == AudioSystem.DEVICE_NONE) {
                    throw new IllegalArgumentException(
                            "Can't have flag ROUTE_FLAG_RENDER without an audio device");
                }
                if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) {
                    if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) {
                        mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                    } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
                        mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX;
                    } else {
                        throw new IllegalArgumentException("Unknown mixing rule type");

                if (AudioSystem.DEVICE_IN_ALL_SET.contains(mDeviceSystemType)) {
                    throw new IllegalArgumentException(
                            "Input device is not supported with ROUTE_FLAG_RENDER");
                }

                if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
                    throw new IllegalArgumentException(
                            "ROUTE_FLAG_RENDER/ROUTE_FLAG_LOOP_BACK_RENDER is not supported for "
                                    + "non-playback mix rule");
                }
            }

            if (mRule.allowPrivilegedMediaPlaybackCapture()) {
                String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat);
                if (error != null) {
@@ -484,5 +487,18 @@ public class AudioMix {
            return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
                    mDeviceAddress);
        }

        private int getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule) {
            switch (mRule.getTargetMixType()) {
                case MIX_TYPE_PLAYERS:
                    return AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                case MIX_TYPE_RECORDERS:
                    return AudioSystem.DEVICE_IN_REMOTE_SUBMIX;
                default:
                    throw new IllegalArgumentException(
                            "Unknown mixing rule type - 0x" + Integer.toHexString(
                                    rule.getTargetMixType()));
            }
        }
    }
}
+36 −2
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ public class AudioMixUnitTests {
        // --- Equality group 3
        final AudioMix recordingAudioMixWithSessionId42AndUid123Render =
                new AudioMix.Builder(new AudioMixingRule.Builder()
                        .setTargetMixRole(MIX_ROLE_INJECTOR)
                        .setTargetMixRole(MIX_ROLE_PLAYERS)
                        .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
                        .addMixRule(RULE_MATCH_UID, 123).build())
                        .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
@@ -205,7 +205,19 @@ public class AudioMixUnitTests {
    }

    @Test
    public void buildLoopbackWithDevice_throws() {
    public void buildLoopbackForInjectorMix_success() {
        final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
                .setTargetMixRole(MIX_ROLE_INJECTOR)
                .addMixRule(RULE_MATCH_UID, 42).build())
                .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
                .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();

        assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
        assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags());
    }

    @Test
    public void buildLoopbackWithIncompatibleDevice_throws() {
        assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
                new AudioMixingRule.Builder()
                        .setTargetMixRole(MIX_ROLE_PLAYERS)
@@ -225,6 +237,28 @@ public class AudioMixUnitTests {
                .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER).build());
    }

    @Test
    public void buildRenderWithInputDevice_throws() {
        assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
                new AudioMixingRule.Builder()
                        .setTargetMixRole(MIX_ROLE_PLAYERS)
                        .addMixRule(RULE_MATCH_UID, 42).build())
                .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
                .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
                .setDevice(AudioSystem.DEVICE_IN_BUILTIN_MIC, /*address=*/"").build());
    }

    @Test
    public void buildRenderWithInjectorMix_throws() {
        assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
                new AudioMixingRule.Builder()
                        .setTargetMixRole(MIX_ROLE_INJECTOR)
                        .addMixRule(RULE_MATCH_UID, 42).build())
                .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
                .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
                .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build());
    }



    private static AudioMix writeToAndFromParcel(AudioMix audioMix) {