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

Commit 715d08a2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "muteAwaitConnection"

* changes:
  AudioManager: test API for device connection
  AudioManager: add API for muting playback until device connects
parents 02751aec 4da775dd
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -5403,6 +5403,7 @@ package android.media {
    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void cancelMuteAwaitConnection(@NonNull android.media.AudioDeviceAttributes) throws java.lang.IllegalStateException;
    method public void clearAudioServerStateCallback();
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
@@ -5416,6 +5417,7 @@ package android.media {
    method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
    method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
    method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getMutingExpectedDevice();
    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
@@ -5424,7 +5426,9 @@ package android.media {
    method public boolean isAudioServerRunning();
    method public boolean isHdmiSystemAudioSupported();
    method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
    method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
@@ -5444,6 +5448,7 @@ package android.media {
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
    method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
    field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
    field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
@@ -5463,6 +5468,15 @@ package android.media {
    method public void onAudioServerUp();
  }
  public abstract static class AudioManager.MuteAwaitConnectionCallback {
    ctor public AudioManager.MuteAwaitConnectionCallback();
    method public void onMutedUntilConnection(@NonNull android.media.AudioDeviceAttributes, @NonNull int[]);
    method public void onUnmutedEvent(int, @NonNull android.media.AudioDeviceAttributes, @NonNull int[]);
    field public static final int EVENT_CANCEL = 3; // 0x3
    field public static final int EVENT_CONNECTION = 1; // 0x1
    field public static final int EVENT_TIMEOUT = 2; // 0x2
  }
  @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
    method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
  }
+1 −0
Original line number Diff line number Diff line
@@ -1458,6 +1458,7 @@ package android.media {
    method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
    method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
    method public void setRampingRingerEnabled(boolean);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
  }

  public static final class AudioRecord.MetricsConstants {
+243 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * AudioManager provides access to volume and ringer mode control.
@@ -5774,6 +5775,23 @@ public class AudioManager {
        }
    }

    /**
     * Indicate wired accessory connection state change.
     * @param device {@link AudioDeviceAttributes} of the device to "fake-connect"
     * @param connected true for connected, false for disconnected
     * {@hide}
     */
    @TestApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
            boolean connected) {
        try {
            getService().setTestDeviceConnectionState(device, connected);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Indicate Bluetooth profile connection state change.
     * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and
@@ -7967,6 +7985,231 @@ public class AudioManager {
        }
    }

    //---------------------------------------------------------
    // audio device connection-dependent muting
    /**
     * @hide
     * Mute a set of playback use cases until a given audio device is connected.
     * Automatically unmute upon connection of the device, or after the given timeout, whichever
     * happens first.
     * @param usagesToMute non-empty array of {@link AudioAttributes} usages (for example
     *                     {@link AudioAttributes#USAGE_MEDIA}) to mute until the
     *                     device connects
     * @param device the audio device expected to connect within the timeout duration
     * @param timeout the maximum amount of time to wait for the device connection
     * @param timeUnit the unit for the timeout
     * @throws IllegalStateException when trying to issue the command while another is already in
     *         progress and hasn't been cancelled by
     *         {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}. See
     *         {@link #getMutingExpectedDevice()} to check if a muting command is active.
     * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void muteAwaitConnection(@NonNull int[] usagesToMute,
            @NonNull AudioDeviceAttributes device,
            long timeout, @NonNull TimeUnit timeUnit) throws IllegalStateException {
        if (timeout <= 0) {
            throw new IllegalArgumentException("Timeout must be greater than 0");
        }
        Objects.requireNonNull(usagesToMute);
        if (usagesToMute.length == 0) {
            throw new IllegalArgumentException("Array of usages to mute cannot be empty");
        }
        Objects.requireNonNull(device);
        Objects.requireNonNull(timeUnit);
        try {
            getService().muteAwaitConnection(usagesToMute, device, timeUnit.toMillis(timeout));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Query which audio device, if any, is causing some playback use cases to be muted until it
     * connects.
     * @return the audio device used in
     *        {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}, or null
     *        if there is no active muting command (either because the muting command was not issued
     *        or because it timed out)
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
        try {
            return getService().getMutingExpectedDevice();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Cancel a {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
     * command.
     * @param device the device whose connection was expected when the {@code muteAwaitConnection}
     *               command was issued.
     * @throws IllegalStateException when trying to issue the command for a device whose connection
     *         is not anticipated by a previous call to
     *         {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device)
            throws IllegalStateException {
        Objects.requireNonNull(device);
        try {
            getService().cancelMuteAwaitConnection(device);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * A callback class to receive events about the muting and unmuting of playback use cases
     * conditional on the upcoming connection of an audio device.
     * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
     */
    @SystemApi
    public abstract static class MuteAwaitConnectionCallback {

        /**
         * An event where the expected audio device connected
         * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
         */
        public static final int EVENT_CONNECTION = 1;
        /**
         * An event where the expected audio device failed connect before the timeout happened
         * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
         */
        public static final int EVENT_TIMEOUT    = 2;
        /**
         * An event where the {@code muteAwaitConnection()} command
         * was cancelled with {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}
         * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
         */
        public static final int EVENT_CANCEL     = 3;

        /** @hide */
        @IntDef(flag = false, prefix = "EVENT_", value = {
                EVENT_CONNECTION,
                EVENT_TIMEOUT,
                EVENT_CANCEL }
        )
        @Retention(RetentionPolicy.SOURCE)
        public @interface UnmuteEvent {}

        /**
         * Called when a number of playback use cases are muted in response to a call to
         * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}.
         * @param device the audio device whose connection is expected. Playback use cases are
         *               unmuted when that device connects
         * @param mutedUsages an array of {@link AudioAttributes} usages that describe the affected
         *                    playback use cases.
         */
        public void onMutedUntilConnection(
                @NonNull AudioDeviceAttributes device,
                @NonNull int[] mutedUsages) {}

        /**
         * Called when an event occurred that caused playback uses cases to be unmuted
         * @param unmuteEvent the nature of the event
         * @param device the device that was expected to connect
         * @param mutedUsages the array of {@link AudioAttributes} usages that were muted until
         *                    the event occurred
         */
        public void onUnmutedEvent(
                @UnmuteEvent int unmuteEvent,
                @NonNull AudioDeviceAttributes device, @NonNull int[] mutedUsages) {}
    }


    /**
     * @hide
     * Register a callback to receive updates on the playback muting conditional on a specific
     * audio device connection.
     * @param executor the {@link Executor} handling the callback
     * @param callback the callback to register
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void registerMuteAwaitConnectionCallback(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull MuteAwaitConnectionCallback callback) {
        synchronized (mMuteAwaitConnectionListenerLock) {
            final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
                    MuteAwaitConnectionDispatcherStub> res =
                    CallbackUtil.addListener("registerMuteAwaitConnectionCallback",
                            executor, callback, mMuteAwaitConnectionListeners,
                            mMuteAwaitConnDispatcherStub,
                            () -> new MuteAwaitConnectionDispatcherStub(),
                            stub -> stub.register(true));
            mMuteAwaitConnectionListeners = res.first;
            mMuteAwaitConnDispatcherStub = res.second;
        }
    }

    /**
     * @hide
     * Unregister a previously registered callback for playback muting conditional on device
     * connection.
     * @param callback the callback to unregister
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
    public void unregisterMuteAwaitConnectionCallback(
            @NonNull MuteAwaitConnectionCallback callback) {
        synchronized (mMuteAwaitConnectionListenerLock) {
            final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
                    MuteAwaitConnectionDispatcherStub> res =
                    CallbackUtil.removeListener("unregisterMuteAwaitConnectionCallback",
                            callback, mMuteAwaitConnectionListeners, mMuteAwaitConnDispatcherStub,
                            stub -> stub.register(false));
            mMuteAwaitConnectionListeners = res.first;
            mMuteAwaitConnDispatcherStub = res.second;
        }
    }

    private final Object mMuteAwaitConnectionListenerLock = new Object();

    @GuardedBy("mMuteAwaitConnectionListenerLock")
    private @Nullable ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>
            mMuteAwaitConnectionListeners;

    @GuardedBy("mMuteAwaitConnectionListenerLock")
    private MuteAwaitConnectionDispatcherStub mMuteAwaitConnDispatcherStub;

    private final class MuteAwaitConnectionDispatcherStub
            extends IMuteAwaitConnectionCallback.Stub {
        public void register(boolean register) {
            try {
                getService().registerMuteAwaitConnectionDispatcher(this, register);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        @Override
        @SuppressLint("GuardedBy") // lock applied inside callListeners method
        public void dispatchOnMutedUntilConnection(AudioDeviceAttributes device,
                int[] mutedUsages) {
            CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
                    mMuteAwaitConnectionListenerLock,
                    (listener) -> listener.onMutedUntilConnection(device, mutedUsages));
        }

        @Override
        @SuppressLint("GuardedBy") // lock applied inside callListeners method
        public void dispatchOnUnmutedEvent(int event, AudioDeviceAttributes device,
                int[] mutedUsages) {
            CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
                    mMuteAwaitConnectionListenerLock,
                    (listener) -> listener.onUnmutedEvent(event, device, mutedUsages));
        }
    }

    //---------------------------------------------------------
    // Inner classes
    //--------------------
+13 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
@@ -447,4 +448,16 @@ interface IAudioService {
    boolean isVolumeFixed();

    boolean isPstnCallAudioInterceptable();

    oneway void muteAwaitConnection(in int[] usagesToMute, in AudioDeviceAttributes dev,
            long timeOutMs);

    oneway void cancelMuteAwaitConnection(in AudioDeviceAttributes dev);

    AudioDeviceAttributes getMutingExpectedDevice();

    void registerMuteAwaitConnectionDispatcher(in IMuteAwaitConnectionCallback cb,
            boolean register);

    void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
}
+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.media.AudioDeviceAttributes;

/**
 * AIDL for the AudioService to signal mute events tied to audio device connections.
 *
 * {@hide}
 */
oneway interface IMuteAwaitConnectionCallback {

    void dispatchOnMutedUntilConnection(in AudioDeviceAttributes device, in int[] mutedUsages);

    void dispatchOnUnmutedEvent(int event, in AudioDeviceAttributes device, in int[] mutedUsages);
}
Loading