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

Commit 9efb10a9 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioMix: add option for silence injection on injection starvation

New option in AudioMix's Builder to inject silence for audio
injection mixes when the injector is starving the mix (not
providing enough audio data in time).
Also annotate other Builder methods with @NonNull when returning
the Builder itself.

Bug: 416965224
Test: atest CtsMediaAudioTestCases:AudioMixTest
Flag: android.media.audio.dap_injection_starve_management
Change-Id: I7838702e5a21346954c5f1a18dbaa6b3ae0a8861
parent cf3ce3b9
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -8146,6 +8146,7 @@ package android.media.audiopolicy {
  public class AudioMix implements android.os.Parcelable {
    method public int describeContents();
    method public int getMixState();
    method @FlaggedApi("android.media.audio.dap_injection_starve_management") public boolean isInjectingSilenceOnStarvation();
    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
@@ -8158,9 +8159,10 @@ package android.media.audiopolicy {
  public static class AudioMix.Builder {
    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(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
    method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException;
    method @NonNull public android.media.audiopolicy.AudioMix.Builder setDevice(@NonNull android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException;
    method @NonNull public android.media.audiopolicy.AudioMix.Builder setFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
    method @FlaggedApi("android.media.audio.dap_injection_starve_management") @NonNull public android.media.audiopolicy.AudioMix.Builder setInjectSilenceOnStarvation(boolean);
    method @NonNull public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException;
  }
  public class AudioMixingRule implements android.os.Parcelable {
+81 −11
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package android.media.audiopolicy;

import static android.media.audio.Flags.FLAG_DAP_INJECTION_STARVE_MANAGEMENT;
import static android.media.audio.Flags.dapInjectionStarveManagement;
import static android.media.AudioSystem.getDeviceName;
import static android.media.AudioSystem.isRemoteSubmixDevice;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -74,6 +77,11 @@ public class AudioMix implements Parcelable {
    // audio routing for this device ID.
    private int mVirtualDeviceId;

    // When this mix is used for injecting audio into recordings, indicates whether silence
    // should be injected when the injection is failing to provide enough audio data in time
    // False by default or when not applicable
    private boolean mInjectSilenceOnStarve = false;

    /**
     * All parameters are guaranteed valid through the Builder.
     */
@@ -90,6 +98,23 @@ public class AudioMix implements Parcelable {
        mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress;
        mToken = token;
        mVirtualDeviceId = virtualDeviceId;
        mInjectSilenceOnStarve = false;
    }

    private AudioMix(@NonNull AudioMixingRule rule, @NonNull AudioFormat format,
            int routeFlags, int callbackFlags,
            int deviceType, @Nullable String deviceAddress, IBinder token,
            int virtualDeviceId, boolean injectSilenceOnStarve) {
        mRule = Objects.requireNonNull(rule);
        mFormat = Objects.requireNonNull(format);
        mRouteFlags = routeFlags;
        mMixType = rule.getTargetMixType();
        mCallbackFlags = callbackFlags;
        mDeviceSystemType = deviceType;
        mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress;
        mToken = token;
        mVirtualDeviceId = virtualDeviceId;
        mInjectSilenceOnStarve = injectSilenceOnStarve;
    }

    // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined
@@ -272,6 +297,20 @@ public class AudioMix implements Parcelable {
        return null;
    }

    /**
     * Returns whether this mix was configured to inject silence audio into recordings
     * when the injection is failing to provide enough audio data in time.
     * @return <code>false</code> if the mix is not an injection mix, or how it was configured
     *         {@link Builder#setInjectSilenceOnStarvation(boolean)}.
     */
    @FlaggedApi(FLAG_DAP_INJECTION_STARVE_MANAGEMENT)
    public boolean isInjectingSilenceOnStarvation() {
        if (!dapInjectionStarveManagement()) {
            return false;
        }
        return mInjectSilenceOnStarve;
    }

    /** @hide */
    public boolean isForCallRedirection() {
        return mRule.isForCallRedirection();
@@ -321,6 +360,9 @@ public class AudioMix implements Parcelable {
        mRule.writeToParcel(dest, flags);
        dest.writeStrongBinder(mToken);
        dest.writeInt(mVirtualDeviceId);
        if (dapInjectionStarveManagement()) {
            dest.writeBoolean(mInjectSilenceOnStarve);
        }
    }

    public static final @NonNull Parcelable.Creator<AudioMix> CREATOR = new Parcelable.Creator<>() {
@@ -342,6 +384,9 @@ public class AudioMix implements Parcelable {
            mixBuilder.setMixingRule(AudioMixingRule.CREATOR.createFromParcel(p));
            mixBuilder.setToken(p.readStrongBinder());
            mixBuilder.setVirtualDeviceId(p.readInt());
            if (dapInjectionStarveManagement()) {
                mixBuilder.setInjectSilenceOnStarvation(p.readBoolean());
            }
            return mixBuilder.build();
        }

@@ -378,6 +423,7 @@ public class AudioMix implements Parcelable {
        // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_*
        private int mDeviceSystemType = AudioSystem.DEVICE_NONE;
        private String mDeviceAddress = null;
        private boolean mInjectSilenceOnStarve = false;

        /**
         * @hide
@@ -405,7 +451,7 @@ public class AudioMix implements Parcelable {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        Builder setMixingRule(@NonNull AudioMixingRule rule)
        @NonNull Builder setMixingRule(@NonNull AudioMixingRule rule)
                throws IllegalArgumentException {
            if (rule == null) {
                throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
@@ -418,7 +464,7 @@ public class AudioMix implements Parcelable {
         * @hide
         * Only used by AudioMix internally.
         */
        Builder setToken(IBinder token) {
        @NonNull Builder setToken(IBinder token) {
            mToken = token;
            return this;
        }
@@ -427,7 +473,7 @@ public class AudioMix implements Parcelable {
         * @hide
         * Only used by AudioMix internally.
         */
        Builder setVirtualDeviceId(int virtualDeviceId) {
        @NonNull Builder setVirtualDeviceId(int virtualDeviceId) {
            mVirtualDeviceId = virtualDeviceId;
            return this;
        }
@@ -435,11 +481,11 @@ public class AudioMix implements Parcelable {
        /**
         * @hide
         * Only used by AudioPolicyConfig, not a public API.
         * @param callbackFlags which callbacks are called from native
         * @param flags which callbacks are called from native
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        Builder setCallbackFlags(int flags) throws IllegalArgumentException {
        @NonNull Builder setCallbackFlags(int flags) throws IllegalArgumentException {
            if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
                throw new IllegalArgumentException("Illegal callback flags 0x"
                        + Integer.toHexString(flags).toUpperCase());
@@ -456,7 +502,7 @@ public class AudioMix implements Parcelable {
         * @return the same Builder instance.
         */
        @VisibleForTesting
        public Builder setDevice(int deviceType, String address) {
        public @NonNull Builder setDevice(int deviceType, String address) {
            mDeviceSystemType = deviceType;
            mDeviceAddress = address;
            return this;
@@ -468,7 +514,7 @@ public class AudioMix implements Parcelable {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        public Builder setFormat(@NonNull AudioFormat format)
        public @NonNull Builder setFormat(@NonNull AudioFormat format)
                throws IllegalArgumentException {
            if (format == null) {
                throw new IllegalArgumentException("Illegal null AudioFormat argument");
@@ -485,7 +531,7 @@ public class AudioMix implements Parcelable {
         * @return the same Builder instance.
         * @throws IllegalArgumentException
         */
        public Builder setRouteFlags(@RouteFlags int routeFlags)
        public @NonNull Builder setRouteFlags(@RouteFlags int routeFlags)
                throws IllegalArgumentException {
            if (routeFlags == 0) {
                throw new IllegalArgumentException("Illegal empty route flags");
@@ -511,7 +557,8 @@ public class AudioMix implements Parcelable {
         * @return the same Builder instance
         * @throws IllegalArgumentException
         */
        public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException {
        public @NonNull Builder setDevice(@NonNull AudioDeviceInfo device)
                throws IllegalArgumentException {
            if (device == null) {
                throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument");
            }
@@ -523,10 +570,24 @@ public class AudioMix implements Parcelable {
            return this;
        }

        /**
         * When this mix is used for injecting audio into recordings, configure whether silence
         * should be injected when the injection is failing to provide enough audio data in time
         * @param injectSilence indicates if silence is injected into recordings when
         * @return the same Builder instance
         */
        @FlaggedApi(FLAG_DAP_INJECTION_STARVE_MANAGEMENT)
        public @NonNull Builder setInjectSilenceOnStarvation(boolean injectSilence) {
            mInjectSilenceOnStarve = injectSilence;
            return this;
        }

        /**
         * Combines all of the settings and return a new {@link AudioMix} object.
         * @return a new {@link AudioMix} object
         * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
         * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set
         *     or if {@link #setInjectSilenceOnStarvation(boolean)} was set to <code>true</code> on
         *     a mix not used for injection
         */
        public AudioMix build() throws IllegalArgumentException {
            if (mRule == null) {
@@ -588,6 +649,15 @@ public class AudioMix implements Parcelable {
                }
            }

            if (dapInjectionStarveManagement()
                    && ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK)
                    && (mRule.getTargetMixType() != MIX_TYPE_RECORDERS)
                    && (mInjectSilenceOnStarve)) {
                throw new IllegalArgumentException(
                        "AudioMix.Builder.setInjectSilenceOnStarvation(true) called "
                        + "on a non-injection AudioMix");
            }

            if (mRule.allowPrivilegedMediaPlaybackCapture()) {
                String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat);
                if (error != null) {
@@ -600,7 +670,7 @@ public class AudioMix implements Parcelable {
            }

            return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
                    mDeviceAddress, mToken, mVirtualDeviceId);
                    mDeviceAddress, mToken, mVirtualDeviceId, mInjectSilenceOnStarve);
        }

        private int getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule) {
+3 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audio.Flags.concurrentAudioRecordBypassPermission;
import static android.media.audio.Flags.dapInjectionStarveManagement;
import static android.media.audio.Flags.deviceVolumeApis;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
@@ -5442,6 +5443,8 @@ public class AudioService extends IAudioService.Stub
        pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:"
                + autoPublicVolumeApiHardening());
        pw.println("\tandroid.media.audio.automaticBtDeviceType - EOL");
        pw.println("\tandroid.media.audio.dapInjectionStarveManagement:"
                + dapInjectionStarveManagement());
        pw.println("\tandroid.media.audio.featureSpatialAudioHeadtrackingLowLatency:"
                + featureSpatialAudioHeadtrackingLowLatency());
        pw.println("\tandroid.media.audio.focusFreezeTestApi:"