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

Commit 0da795fa authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

Make AudioMix, AudioMixMatchCriterion & AudioMixingRule Parcelable

Bug: 293874525
Test: atest AudioMixUnitTests AudioMixingRuleUnitTests
Test: atest AudioHostTest AudioServiceHostTest
Change-Id: Id84c587f1edf5754496ed7f803c217017b43e605
parent 7c119c64
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -7276,8 +7276,11 @@ package android.media.audiofx {
package android.media.audiopolicy {
  public class AudioMix {
  public class AudioMix implements android.os.Parcelable {
    method public int describeContents();
    method public int getMixState();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioMix> CREATOR;
    field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff
    field public static final int MIX_STATE_IDLE = 0; // 0x0
    field public static final int MIX_STATE_MIXING = 1; // 0x1
@@ -7286,15 +7289,18 @@ package android.media.audiopolicy {
  }
  public static class AudioMix.Builder {
    ctor public AudioMix.Builder(android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException;
    ctor public AudioMix.Builder(@NonNull android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix build() throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix.Builder setDevice(@NonNull android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix.Builder setFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix.Builder setFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException;
  }
  public class AudioMixingRule {
  public class AudioMixingRule implements android.os.Parcelable {
    method public int describeContents();
    method public int getTargetMixRole();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioMixingRule> CREATOR;
    field public static final int MIX_ROLE_INJECTOR = 1; // 0x1
    field public static final int MIX_ROLE_PLAYERS = 0; // 0x0
    field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
+3 −0
Original line number Diff line number Diff line
package android.media.audiopolicy;

parcelable AudioMix;
 No newline at end of file
+58 −11
Original line number Diff line number Diff line
@@ -21,12 +21,15 @@ import static android.media.AudioSystem.isRemoteSubmixDevice;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioSystem;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.annotations.VisibleForTesting;

@@ -38,12 +41,12 @@ import java.util.Objects;
 * @hide
 */
@SystemApi
public class AudioMix {
public class AudioMix implements Parcelable {

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private AudioMixingRule mRule;
    private @NonNull AudioMixingRule mRule;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private AudioFormat mFormat;
    private @NonNull AudioFormat mFormat;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private int mRouteFlags;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -54,7 +57,7 @@ public class AudioMix {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    int mCallbackFlags;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    String mDeviceAddress;
    @NonNull String mDeviceAddress;

    // initialized in constructor, read by AudioPolicyConfig
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -63,10 +66,11 @@ public class AudioMix {
    /**
     * All parameters are guaranteed valid through the Builder.
     */
    private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags,
            int deviceType, String deviceAddress) {
        mRule = rule;
        mFormat = format;
    private AudioMix(@NonNull AudioMixingRule rule, @NonNull AudioFormat format,
            int routeFlags, int callbackFlags,
            int deviceType, @Nullable String deviceAddress) {
        mRule = Objects.requireNonNull(rule);
        mFormat = Objects.requireNonNull(format);
        mRouteFlags = routeFlags;
        mMixType = rule.getTargetMixType();
        mCallbackFlags = callbackFlags;
@@ -269,6 +273,49 @@ public class AudioMix {
        return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        // write mix route flags
        dest.writeInt(mRouteFlags);
        // write callback flags
        dest.writeInt(mCallbackFlags);
        // write device information
        dest.writeInt(mDeviceSystemType);
        dest.writeString8(mDeviceAddress);
        mFormat.writeToParcel(dest, flags);
        mRule.writeToParcel(dest, flags);
    }

    public static final @NonNull Parcelable.Creator<AudioMix> CREATOR = new Parcelable.Creator<>() {
        /**
         * Rebuilds an AudioMix previously stored with writeToParcel().
         *
         * @param p Parcel object to read the AudioMix from
         * @return a new AudioMix created from the data in the parcel
         */
        public AudioMix createFromParcel(Parcel p) {
            final AudioMix.Builder mixBuilder = new AudioMix.Builder();
            // read mix route flags
            mixBuilder.setRouteFlags(p.readInt());
            // read callback flags
            mixBuilder.setCallbackFlags(p.readInt());
            // read device information
            mixBuilder.setDevice(p.readInt(), p.readString8());
            mixBuilder.setFormat(AudioFormat.CREATOR.createFromParcel(p));
            mixBuilder.setMixingRule(AudioMixingRule.CREATOR.createFromParcel(p));
            return mixBuilder.build();
        }

        public AudioMix[] newArray(int size) {
            return new AudioMix[size];
        }
    };

    /** @hide */
    @IntDef(flag = true,
            value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } )
@@ -298,7 +345,7 @@ public class AudioMix {
         * @param rule a non-null {@link AudioMixingRule} instance.
         * @throws IllegalArgumentException
         */
        public Builder(AudioMixingRule rule)
        public Builder(@NonNull AudioMixingRule rule)
                throws IllegalArgumentException {
            if (rule == null) {
                throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
@@ -313,7 +360,7 @@ public class AudioMix {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        Builder setMixingRule(AudioMixingRule rule)
        Builder setMixingRule(@NonNull AudioMixingRule rule)
                throws IllegalArgumentException {
            if (rule == null) {
                throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
@@ -358,7 +405,7 @@ public class AudioMix {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        public Builder setFormat(AudioFormat format)
        public Builder setFormat(@NonNull AudioFormat format)
                throws IllegalArgumentException {
            if (format == null) {
                throw new IllegalArgumentException("Illegal null AudioFormat argument");
+3 −0
Original line number Diff line number Diff line
package android.media.audiopolicy;

parcelable AudioMixingRule;
 No newline at end of file
+112 −46
Original line number Diff line number Diff line
@@ -26,8 +26,11 @@ import android.media.AudioAttributes;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Collection;
@@ -50,7 +53,7 @@ import java.util.Set;
 * </pre>
 */
@SystemApi
public class AudioMixingRule {
public class AudioMixingRule implements Parcelable {

    private AudioMixingRule(int mixType, Collection<AudioMixMatchCriterion> criteria,
                            boolean allowPrivilegedMediaPlaybackCapture,
@@ -130,7 +133,7 @@ public class AudioMixingRule {
            RULE_EXCLUSION_MASK | RULE_MATCH_AUDIO_SESSION_ID;

    /** @hide */
    public static final class AudioMixMatchCriterion {
    public static final class AudioMixMatchCriterion implements Parcelable {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        final AudioAttributes mAttr;
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -139,18 +142,44 @@ public class AudioMixingRule {
        final int mRule;

        /** input parameters must be valid */
        AudioMixMatchCriterion(AudioAttributes attributes, int rule) {
        @VisibleForTesting
        public AudioMixMatchCriterion(AudioAttributes attributes, int rule) {
            mAttr = attributes;
            mIntProp = Integer.MIN_VALUE;
            mRule = rule;
        }
        /** input parameters must be valid */
        AudioMixMatchCriterion(Integer intProp, int rule) {
        @VisibleForTesting
        public AudioMixMatchCriterion(Integer intProp, int rule) {
            mAttr = null;
            mIntProp = intProp.intValue();
            mRule = rule;
        }

        private AudioMixMatchCriterion(@NonNull Parcel in) {
            Objects.requireNonNull(in);
            mRule = in.readInt();
            final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
            switch (match_rule) {
                case RULE_MATCH_ATTRIBUTE_USAGE:
                case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                    mAttr = AudioAttributes.CREATOR.createFromParcel(in);
                    mIntProp = Integer.MIN_VALUE;
                    break;
                case RULE_MATCH_UID:
                case RULE_MATCH_USERID:
                case RULE_MATCH_AUDIO_SESSION_ID:
                    mIntProp = in.readInt();
                    mAttr = null;
                    break;
                default:
                    // assume there was in int value to read as for now they come in pair
                    in.readInt();
                    throw new IllegalArgumentException(
                            "Illegal rule value " + mRule + " in parcel");
            }
        }

        @Override
        public int hashCode() {
            return Objects.hash(mAttr, mIntProp, mRule);
@@ -170,7 +199,13 @@ public class AudioMixingRule {
                    && Objects.equals(mAttr, other.mAttr);
        }

        void writeToParcel(Parcel dest) {
        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            dest.writeInt(mRule);
            final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
            switch (match_rule) {
@@ -190,6 +225,22 @@ public class AudioMixingRule {
            }
        }

        public static final @NonNull Parcelable.Creator<AudioMixMatchCriterion> CREATOR =
                new Parcelable.Creator<>() {
            /**
             * Rebuilds an AudioMixMatchCriterion previously stored with writeToParcel().
             *
             * @param p Parcel object to read the AudioMix from
             * @return a new AudioMixMatchCriterion created from the data in the parcel
             */
            public AudioMixMatchCriterion createFromParcel(Parcel p) {
                return new AudioMixMatchCriterion(p);
            }
            public AudioMixMatchCriterion[] newArray(int size) {
                return new AudioMixMatchCriterion[size];
            }
        };

        public AudioAttributes getAudioAttributes() { return mAttr; }
        public int getIntProp() { return mIntProp; }
        public int getRule() { return mRule; }
@@ -605,13 +656,14 @@ public class AudioMixingRule {
                if (!(property instanceof AudioAttributes)) {
                    throw new IllegalArgumentException("Invalid AudioAttributes argument");
                }
                return addRuleInternal((AudioAttributes) property, null, rule);
                return addRuleInternal(
                        new AudioMixMatchCriterion((AudioAttributes) property, rule));
            } else {
                // implies integer match rule
                if (!(property instanceof Integer)) {
                    throw new IllegalArgumentException("Invalid Integer argument");
                }
                return addRuleInternal(null, (Integer) property, rule);
                return addRuleInternal(new AudioMixMatchCriterion((Integer) property, rule));
            }
        }

@@ -636,12 +688,13 @@ public class AudioMixingRule {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        private Builder addRuleInternal(AudioAttributes attrToMatch, Integer intProp, int rule)
        private Builder addRuleInternal(AudioMixMatchCriterion criterion)
                throws IllegalArgumentException {
            // If mix type is invalid and added rule is valid only for the players / recorders,
            // adjust the mix type accordingly.
            // Otherwise, if the mix type was already deduced or set explicitly, verify the rule
            // is valid for the mix type.
            final int rule = criterion.mRule;
            if (mTargetMixType == AudioMix.MIX_TYPE_INVALID) {
                if (isPlayerRule(rule)) {
                    mTargetMixType = AudioMix.MIX_TYPE_PLAYERS;
@@ -655,51 +708,16 @@ public class AudioMixingRule {
            }
            synchronized (mCriteria) {
                int oppositeRule = rule ^ RULE_EXCLUSION_MASK;
                if (mCriteria.stream().anyMatch(criterion -> criterion.mRule == oppositeRule)) {
                if (mCriteria.stream().anyMatch(
                        otherCriterion -> otherCriterion.mRule == oppositeRule)) {
                    throw new IllegalArgumentException("AudioMixingRule cannot contain RULE_MATCH_*"
                            + " and RULE_EXCLUDE_* for the same dimension.");
                }
                int ruleWithoutExclusion = rule & ~RULE_EXCLUSION_MASK;
                switch (ruleWithoutExclusion) {
                    case RULE_MATCH_ATTRIBUTE_USAGE:
                    case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                        mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule));
                        break;
                    case RULE_MATCH_UID:
                    case RULE_MATCH_USERID:
                    case RULE_MATCH_AUDIO_SESSION_ID:
                        mCriteria.add(new AudioMixMatchCriterion(intProp, rule));
                        break;
                    default:
                        throw new IllegalStateException("Unreachable code in addRuleInternal()");
                }
                mCriteria.add(criterion);
            }
            return this;
        }

        Builder addRuleFromParcel(Parcel in) throws IllegalArgumentException {
            final int rule = in.readInt();
            final int match_rule = rule & ~RULE_EXCLUSION_MASK;
            AudioAttributes attr = null;
            Integer intProp = null;
            switch (match_rule) {
                case RULE_MATCH_ATTRIBUTE_USAGE:
                case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                    attr =  AudioAttributes.CREATOR.createFromParcel(in);
                    break;
                case RULE_MATCH_UID:
                case RULE_MATCH_USERID:
                case RULE_MATCH_AUDIO_SESSION_ID:
                    intProp = new Integer(in.readInt());
                    break;
                default:
                    // assume there was in int value to read as for now they come in pair
                    in.readInt();
                    throw new IllegalArgumentException("Illegal rule value " + rule + " in parcel");
            }
            return addRuleInternal(attr, intProp, rule);
        }

        /**
         * Combines all of the matching and exclusion rules that have been set and return a new
         * {@link AudioMixingRule} object.
@@ -717,4 +735,52 @@ public class AudioMixingRule {
                    mVoiceCommunicationCaptureAllowed);
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        // write opt-out respect
        dest.writeBoolean(mAllowPrivilegedPlaybackCapture);
        // write voice communication capture allowed flag
        dest.writeBoolean(mVoiceCommunicationCaptureAllowed);
        // write specified mix type
        dest.writeInt(mTargetMixType);
        // write mix rules
        dest.writeInt(mCriteria.size());
        for (AudioMixingRule.AudioMixMatchCriterion criterion : mCriteria) {
            criterion.writeToParcel(dest, flags);
        }
    }

    public static final @NonNull Parcelable.Creator<AudioMixingRule> CREATOR =
            new Parcelable.Creator<>() {

        @Override
        public AudioMixingRule createFromParcel(Parcel source) {
            AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
            // read opt-out respect
            ruleBuilder.allowPrivilegedPlaybackCapture(source.readBoolean());
            // read voice capture allowed flag
            ruleBuilder.voiceCommunicationCaptureAllowed(source.readBoolean());
            // read specified mix type
            ruleBuilder.setTargetMixRole(source.readInt());
            // read mix rules
            int nbRules = source.readInt();
            for (int j = 0; j < nbRules; j++) {
                // read the matching rules
                ruleBuilder.addRuleInternal(
                        AudioMixMatchCriterion.CREATOR.createFromParcel(source));
            }
            return ruleBuilder.build();
        }

        @Override
        public AudioMixingRule[] newArray(int size) {
            return new AudioMixingRule[size];
        }
    };
}
Loading