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

Commit ba63ef90 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "AudioService: add test APIs for audio focus and ducking" into main

parents 677d75a1 a95a78f9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1877,6 +1877,8 @@ package android.media {

  public class AudioManager {
    method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
    method @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
    method @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceComputeCsdOnAllDevices(boolean);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceUseFrameworkMel(boolean);
    method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -1884,6 +1886,9 @@ package android.media {
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getCsd();
    method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
    method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
    method @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
    method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
    method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
    method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
    method public static final int[] getPublicStreamTypes();
    method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+91 −0
Original line number Diff line number Diff line
@@ -4737,6 +4737,97 @@ public class AudioManager {
        }
    }

    /**
     * @hide
     * Test method to return the list of UIDs currently marked as ducked because of their
     * audio focus status
     * @return the list of UIDs, can be empty when no app is being ducked.
     */
    @TestApi
    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
    public @NonNull List<Integer> getFocusDuckedUidsForTest() {
        try {
            return getService().getFocusDuckedUidsForTest();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Test method to return the duration of the fade out applied on the players of a focus loser
     * @return the fade out duration in ms
     */
    @TestApi
    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
    public long getFocusFadeOutDurationForTest() {
        try {
            return getService().getFocusFadeOutDurationForTest();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Test method to return the length of time after a fade-out before the focus loser is unmuted
     * (and is faded back in).
     * @return the time gap after a fade-out completion on focus loss, and fade-in start in ms.
     */
    @TestApi
    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
    public long getFocusUnmuteDelayAfterFadeOutForTest() {
        try {
            return getService().getFocusUnmuteDelayAfterFadeOutForTest();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Test method to start preventing applications from requesting audio focus during a test,
     * which could interfere with the functionality/behavior under test.
     * Calling this method needs to be paired with a call to {@link #exitAudioFocusFreezeForTest}
     * when the testing is done. If this is not the case (e.g. in case of a test crash),
     * a death observer mechanism will ensure the system is not left in a bad state, but this should
     * not be relied on when implementing tests.
     * @param exemptedUids a list of UIDs that are exempt from the freeze. This would for instance
     *     be those of the test runner and other players used in the test, or the "fake" UIDs used
     *     for testing with {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)}.
     * @return true if the focus freeze mode is successfully entered, false if there was an issue,
     *     such as another freeze in place at the time of invocation.
     *     A false result should result in a test failure as this would indicate the system is not
     *     in a proper state with a predictable behavior for audio focus management.
     */
    @TestApi
    @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    public boolean enterAudioFocusFreezeForTest(@NonNull List<Integer> exemptedUids) {
        Objects.requireNonNull(exemptedUids);
        try {
            final int[] uids = exemptedUids.stream().mapToInt(Integer::intValue).toArray();
            return getService().enterAudioFocusFreezeForTest(mICallBack, uids);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Test method to end preventing applications from requesting audio focus during a test.
     * @return true if the focus freeze mode is successfully exited, false if there was an issue,
     *     such as the freeze already having ended, or not started.
     */
    @TestApi
    @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    public boolean exitAudioFocusFreezeForTest() {
        try {
            return getService().exitAudioFocusFreezeForTest(mICallBack);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Request or lock audio focus.
+17 −0
Original line number Diff line number Diff line
@@ -529,6 +529,23 @@ interface IAudioService {

    long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa);

    @EnforcePermission("QUERY_AUDIO_STATE")
    /* Returns a List<Integer> */
    @SuppressWarnings(value = {"untyped-collection"})
    List getFocusDuckedUidsForTest();

    @EnforcePermission("QUERY_AUDIO_STATE")
    long getFocusFadeOutDurationForTest();

    @EnforcePermission("QUERY_AUDIO_STATE")
    long getFocusUnmuteDelayAfterFadeOutForTest();

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    boolean enterAudioFocusFreezeForTest(IBinder cb, in int[] uids);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    boolean exitAudioFocusFreezeForTest(IBinder cb);

    void registerModeDispatcher(IAudioModeDispatcher dispatcher);

    oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher);
+72 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static com.android.server.utils.EventLogger.Event.ALOGI;
import static com.android.server.utils.EventLogger.Event.ALOGW;
import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -10005,6 +10006,14 @@ public class AudioService extends IAudioService.Stub
        return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
    }
    /** see {@link AudioManager#getFocusDuckedUidsForTest()} */
    @Override
    @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
    public @NonNull List<Integer> getFocusDuckedUidsForTest() {
        super.getFocusDuckedUidsForTest_enforcePermission();
        return mPlaybackMonitor.getFocusDuckedUids();
    }
    public void unregisterAudioFocusClient(String clientId) {
        new MediaMetrics.Item(mMetricsId + "focus")
                .set(MediaMetrics.Property.CLIENT_NAME, clientId)
@@ -10021,6 +10030,68 @@ public class AudioService extends IAudioService.Stub
        return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr);
    }
    /**
     * Test method to return the duration of the fade out applied on the players of a focus loser
     * @see AudioManager#getFocusFadeOutDurationForTest()
     * @return the fade out duration, in ms
     */
    @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
    public long getFocusFadeOutDurationForTest() {
        super.getFocusFadeOutDurationForTest_enforcePermission();
        return mMediaFocusControl.getFocusFadeOutDurationForTest();
    }
    /**
     * Test method to return the length of time after a fade out before the focus loser is unmuted
     * (and is faded back in).
     * @see AudioManager#getFocusUnmuteDelayAfterFadeOutForTest()
     * @return the time gap after a fade out completion on focus loss, and fade in start, in ms
     */
    @Override
    @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
    public long getFocusUnmuteDelayAfterFadeOutForTest() {
        super.getFocusUnmuteDelayAfterFadeOutForTest_enforcePermission();
        return mMediaFocusControl.getFocusUnmuteDelayAfterFadeOutForTest();
    }
    /**
     * Test method to start preventing applications from requesting audio focus during a test,
     * which could interfere with the testing of the functionality/behavior under test.
     * Calling this method needs to be paired with a call to {@link #exitAudioFocusFreezeForTest}
     * when the testing is done. If this is not the case (e.g. in case of a test crash),
     * a death observer mechanism will ensure the system is not left in a bad state, but this should
     * not be relied on when implementing tests.
     * @see AudioManager#enterAudioFocusFreezeForTest(List)
     * @param cb IBinder to track the death of the client of this method
     * @param exemptedUids a list of UIDs that are exempt from the freeze. This would for instance
     *                     be those of the test runner and other players used in the test
     * @return true if the focus freeze mode is successfully entered, false if there was an issue,
     *     such as another freeze currently used.
     */
    @Override
    @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    public boolean enterAudioFocusFreezeForTest(IBinder cb, int[] exemptedUids) {
        super.enterAudioFocusFreezeForTest_enforcePermission();
        Objects.requireNonNull(exemptedUids);
        Objects.requireNonNull(cb);
        return mMediaFocusControl.enterAudioFocusFreezeForTest(cb, exemptedUids);
    }
    /**
     * Test method to end preventing applications from requesting audio focus during a test.
     * @see AudioManager#exitAudioFocusFreezeForTest()
     * @param cb IBinder identifying the client of this method
     * @return true if the focus freeze mode is successfully exited, false if there was an issue,
     *     such as the freeze already having ended, or not started.
     */
    @Override
    @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    public boolean exitAudioFocusFreezeForTest(IBinder cb) {
        super.exitAudioFocusFreezeForTest_enforcePermission();
        Objects.requireNonNull(cb);
        return mMediaFocusControl.exitAudioFocusFreezeForTest(cb);
    }
    /** only public for mocking/spying, do not call outside of AudioService */
    @VisibleForTesting
    public boolean hasAudioFocusUsers() {
@@ -10028,6 +10099,7 @@ public class AudioService extends IAudioService.Stub
    }
    /** see {@link AudioManager#getFadeOutDurationOnFocusLossMillis(AudioAttributes)} */
    @Override
    public long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
        if (!enforceQueryAudioStateForTest("fade out duration")) {
            return 0;
+10 −1
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ import java.io.PrintWriter;
public class FocusRequester {

    // on purpose not using this classe's name, as it will only be used from MediaFocusControl
    private static final String TAG = "MediaFocusControl";
    private static final String TAG = "FocusRequester";
    private static final boolean DEBUG = false;

    private AudioFocusDeathHandler mDeathHandler; // may be null
@@ -340,6 +340,9 @@ public class FocusRequester {
    @GuardedBy("MediaFocusControl.mAudioFocusLock")
    boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)
    {
        if (DEBUG) {
            Log.i(TAG, "handleFocusLossFromGain for " + mClientId + " gain:" + focusGain);
        }
        final int focusLoss = focusLossForGainRequest(focusGain);
        handleFocusLoss(focusLoss, frWinner, forceDuck);
        return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
@@ -378,6 +381,9 @@ public class FocusRequester {
    @GuardedBy("MediaFocusControl.mAudioFocusLock")
    void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
    {
        if (DEBUG) {
            Log.i(TAG, "handleFocusLoss for " + mClientId + " loss:" + focusLoss);
        }
        try {
            if (focusLoss != mFocusLossReceived) {
                mFocusLossReceived = focusLoss;
@@ -427,6 +433,9 @@ public class FocusRequester {
                            toAudioFocusInfo(), true /* wasDispatched */);
                    mFocusLossWasNotified = true;
                    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
                } else if (DEBUG) {
                    Log.i(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
                            + " to " + mClientId + " no IAudioFocusDispatcher");
                }
            }
        } catch (android.os.RemoteException e) {
Loading