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

Commit f28b1c66 authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

Add support for audio session id based routing

Bug: 233910083
Test: atest AudioServiceHostTest AudioHostTest
Test: atest com.android.audiopolicytest
Change-Id: Ica38e5c4cd3b2cc0c6c739dbd47fa0790d4393e2
parent 1eb8a45e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6534,6 +6534,7 @@ package android.media.audiopolicy {
    field public static final int MIX_ROLE_PLAYERS = 0; // 0x0
    field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
    field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
    field public static final int RULE_MATCH_AUDIO_SESSION_ID = 16; // 0x10
    field public static final int RULE_MATCH_UID = 4; // 0x4
    field public static final int RULE_MATCH_USERID = 8; // 0x8
  }
+5 −0
Original line number Diff line number Diff line
@@ -2160,6 +2160,11 @@ static jint convertAudioMixToNative(JNIEnv *env,
            nCriterion.mValue.mUserId =
                    env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mIntProp);
            break;
        case RULE_MATCH_AUDIO_SESSION_ID: {
            jint jAudioSessionId =
                    env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mIntProp);
            nCriterion.mValue.mAudioSessionId = static_cast<audio_session_t>(jAudioSessionId);
        } break;
        case RULE_MATCH_ATTRIBUTE_USAGE:
        case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
            jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
+28 −4
Original line number Diff line number Diff line
@@ -87,6 +87,13 @@ public class AudioMixingRule {
     * parameter is an instance of {@link java.lang.Integer}.
     */
    public static final int RULE_MATCH_USERID = 0x1 << 3;
    /**
     * A rule requiring the audio session id of the audio stream to match that specified.
     * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where Object
     * parameter is an instance of {@link java.lang.Integer}.
     * @see android.media.AudioTrack.Builder#setSessionId
     */
    public static final int RULE_MATCH_AUDIO_SESSION_ID = 0x1 << 4;

    private final static int RULE_EXCLUSION_MASK = 0x8000;
    /**
@@ -115,6 +122,13 @@ public class AudioMixingRule {
    public static final int RULE_EXCLUDE_USERID =
            RULE_EXCLUSION_MASK | RULE_MATCH_USERID;

    /**
     * @hide
     * A rule requiring the audio session id information to differ.
     */
    public static final int RULE_EXCLUDE_AUDIO_SESSION_ID =
            RULE_EXCLUSION_MASK | RULE_MATCH_AUDIO_SESSION_ID;

    /** @hide */
    public static final class AudioMixMatchCriterion {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -166,6 +180,7 @@ public class AudioMixingRule {
                    break;
                case RULE_MATCH_UID:
                case RULE_MATCH_USERID:
                case RULE_MATCH_AUDIO_SESSION_ID:
                    dest.writeInt(mIntProp);
                    break;
                default:
@@ -315,6 +330,7 @@ public class AudioMixingRule {
            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
            case RULE_MATCH_UID:
            case RULE_MATCH_USERID:
            case RULE_MATCH_AUDIO_SESSION_ID:
                return true;
            default:
                return false;
@@ -338,6 +354,7 @@ public class AudioMixingRule {
            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
            case RULE_MATCH_UID:
            case RULE_MATCH_USERID:
            case RULE_MATCH_AUDIO_SESSION_ID:
                return true;
            default:
                return false;
@@ -445,7 +462,8 @@ public class AudioMixingRule {
         * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
         *     {@link AudioMixingRule#RULE_MATCH_UID} or
         *     {@link AudioMixingRule#RULE_MATCH_USERID}.
         *     {@link AudioMixingRule#RULE_MATCH_USERID} or
         *     {@link AudioMixingRule#RULE_MATCH_AUDIO_SESSION_ID}.
         * @param property see the definition of each rule for the type to use (either an
         *     {@link AudioAttributes} or an {@link java.lang.Integer}).
         * @return the same Builder instance.
@@ -476,7 +494,8 @@ public class AudioMixingRule {
         * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
         *     {@link AudioMixingRule#RULE_MATCH_UID} or
         *     {@link AudioMixingRule#RULE_MATCH_USERID}.
         *     {@link AudioMixingRule#RULE_MATCH_USERID} or
         *     {@link AudioMixingRule#RULE_MATCH_AUDIO_SESSION_ID}.
         * @param property see the definition of each rule for the type to use (either an
         *     {@link AudioAttributes} or an {@link java.lang.Integer}).
         * @return the same Builder instance.
@@ -606,9 +625,12 @@ public class AudioMixingRule {
         * @param intProp an integer property to match or exclude, null if not used.
         * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE},
         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET},
         *     {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET},
         *     {@link AudioMixingRule#RULE_MATCH_UID}, {@link AudioMixingRule#RULE_EXCLUDE_UID}.
         *     {@link AudioMixingRule#RULE_MATCH_UID},
         *     {@link AudioMixingRule#RULE_EXCLUDE_UID},
         *     {@link AudioMixingRule#RULE_MATCH_AUDIO_SESSION_ID},
         *     {@link AudioMixingRule#RULE_EXCLUDE_AUDIO_SESSION_ID}
         *     {@link AudioMixingRule#RULE_MATCH_USERID},
         *     {@link AudioMixingRule#RULE_EXCLUDE_USERID}.
         * @return the same Builder instance.
@@ -645,6 +667,7 @@ public class AudioMixingRule {
                        break;
                    case RULE_MATCH_UID:
                    case RULE_MATCH_USERID:
                    case RULE_MATCH_AUDIO_SESSION_ID:
                        mCriteria.add(new AudioMixMatchCriterion(intProp, rule));
                        break;
                    default:
@@ -666,6 +689,7 @@ public class AudioMixingRule {
                    break;
                case RULE_MATCH_UID:
                case RULE_MATCH_USERID:
                case RULE_MATCH_AUDIO_SESSION_ID:
                    intProp = new Integer(in.readInt());
                    break;
                default:
+8 −0
Original line number Diff line number Diff line
@@ -217,6 +217,14 @@ public class AudioPolicyConfig implements Parcelable {
                        textDump += "  exclude userId ";
                        textDump += criterion.mIntProp;
                        break;
                    case AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID:
                        textDump += " match audio session id";
                        textDump += criterion.mIntProp;
                        break;
                    case AudioMixingRule.RULE_EXCLUDE_AUDIO_SESSION_ID:
                        textDump += " exclude audio session id ";
                        textDump += criterion.mIntProp;
                        break;
                    default:
                        textDump += "invalid rule!";
                }
+56 −1
Original line number Diff line number Diff line
@@ -22,9 +22,11 @@ import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR;
import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS;
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET;
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE;
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_AUDIO_SESSION_ID;
import static android.media.audiopolicy.AudioMixingRule.RULE_EXCLUDE_UID;
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE;
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID;
import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID;

import static org.hamcrest.MatcherAssert.assertThat;
@@ -61,12 +63,14 @@ public class AudioMixingRuleUnitTests {
            new AudioAttributes.Builder().setCapturePreset(VOICE_RECOGNITION).build();
    private static final int TEST_UID = 42;
    private static final int OTHER_UID = 77;
    private static final int TEST_SESSION_ID = 1234;

    @Test
    public void testConstructValidRule() {
        AudioMixingRule rule = new AudioMixingRule.Builder()
                .addMixRule(RULE_MATCH_ATTRIBUTE_USAGE, USAGE_MEDIA_AUDIO_ATTRIBUTES)
                .addMixRule(RULE_MATCH_UID, TEST_UID)
                .excludeMixRule(RULE_MATCH_AUDIO_SESSION_ID, TEST_SESSION_ID)
                .build();

        // Based on the rules, the mix type should fall back to MIX_ROLE_PLAYERS,
@@ -74,7 +78,8 @@ public class AudioMixingRuleUnitTests {
        assertEquals(rule.getTargetMixRole(), MIX_ROLE_PLAYERS);
        assertThat(rule.getCriteria(), containsInAnyOrder(
                isAudioMixMatchUsageCriterion(USAGE_MEDIA),
                isAudioMixMatchUidCriterion(TEST_UID)));
                isAudioMixMatchUidCriterion(TEST_UID),
                isAudioMixExcludeSessionCriterion(TEST_SESSION_ID)));
    }

    @Test
@@ -183,6 +188,30 @@ public class AudioMixingRuleUnitTests {
                        .build());
    }

    @Test
    public void sessionIdRuleCompatibleWithPlayersMix() {
        int sessionId = 42;
        AudioMixingRule rule = new AudioMixingRule.Builder()
                .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, sessionId)
                .setTargetMixRole(MIX_ROLE_PLAYERS)
                .build();

        assertEquals(rule.getTargetMixRole(), MIX_ROLE_PLAYERS);
        assertThat(rule.getCriteria(), containsInAnyOrder(isAudioMixSessionCriterion(sessionId)));
    }

    @Test
    public void sessionIdRuleCompatibleWithInjectorMix() {
        AudioMixingRule rule = new AudioMixingRule.Builder()
                .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, TEST_SESSION_ID)
                .setTargetMixRole(MIX_ROLE_INJECTOR)
                .build();

        assertEquals(rule.getTargetMixRole(), MIX_ROLE_INJECTOR);
        assertThat(rule.getCriteria(),
                containsInAnyOrder(isAudioMixSessionCriterion(TEST_SESSION_ID)));
    }


    private static Matcher isAudioMixUidCriterion(int uid, boolean exclude) {
        return new CustomTypeSafeMatcher<AudioMixMatchCriterion>("uid mix criterion") {
@@ -257,5 +286,31 @@ public class AudioMixingRuleUnitTests {
        return isAudioMixUsageCriterion(usage, /*exclude=*/ false);
    }

    private static Matcher isAudioMixSessionCriterion(int sessionId, boolean exclude) {
        return new CustomTypeSafeMatcher<AudioMixMatchCriterion>("sessionId mix criterion") {
            @Override
            public boolean matchesSafely(AudioMixMatchCriterion item) {
                int excludeRule =
                        exclude ? RULE_EXCLUDE_AUDIO_SESSION_ID : RULE_MATCH_AUDIO_SESSION_ID;
                return item.getRule() == excludeRule && item.getIntProp() == sessionId;
            }

            @Override
            public void describeMismatchSafely(
                    AudioMixMatchCriterion item, Description mismatchDescription) {
                mismatchDescription.appendText(
                        String.format("is not %s criterion with session id %d",
                        exclude ? "exclude" : "match", sessionId));
            }
        };
    }

    private static Matcher isAudioMixSessionCriterion(int sessionId) {
        return isAudioMixSessionCriterion(sessionId, /*exclude=*/ false);
    }

    private static Matcher isAudioMixExcludeSessionCriterion(int sessionId) {
        return isAudioMixSessionCriterion(sessionId, /*exclude=*/ true);
    }

}