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

Commit 7412f575 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioManager: address API review comments

Address API council comments for new AudioManager
API setDeviceForCommunication.

Bug: 179709789
Test: ates AudioCommunicationTest
Change-Id: I1c656578f614c735e8ad839b814ddeb175213400
parent 1d57fc25
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -20088,14 +20088,15 @@ package android.media {
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
    method public void clearDeviceForCommunication();
    method public void clearCommunicationDevice();
    method public void dispatchMediaKeyEvent(android.view.KeyEvent);
    method public int generateAudioSessionId();
    method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
    method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
    method public int getAllowedCapturePolicy();
    method public int getAudioHwSyncForSession(int);
    method @Nullable public android.media.AudioDeviceInfo getDeviceForCommunication();
    method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
    method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
    method public android.media.AudioDeviceInfo[] getDevices(int);
    method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
    method public int getMode();
@@ -20137,7 +20138,7 @@ package android.media {
    method public void setAllowedCapturePolicy(int);
    method @Deprecated public void setBluetoothA2dpOn(boolean);
    method public void setBluetoothScoOn(boolean);
    method public boolean setDeviceForCommunication(@NonNull android.media.AudioDeviceInfo);
    method public boolean setCommunicationDevice(@NonNull android.media.AudioDeviceInfo);
    method public void setMicrophoneMute(boolean);
    method public void setMode(int);
    method public void setParameters(String);
+77 −32
Original line number Diff line number Diff line
@@ -7062,20 +7062,12 @@ public class AudioManager {
     * Selects the audio device that should be used for communication use cases, for instance voice
     * or video calls. This method can be used by voice or video chat applications to select a
     * different audio device than the one selected by default by the platform.
     * <p>The device selection is expressed as an {@link AudioDeviceInfo}, of role sink
     * ({@link AudioDeviceInfo#isSink()} is <code>true</code>) and of one of the following types:
     * <ul>
     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_EARPIECE}
     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}
     *   <li> {@link AudioDeviceInfo#TYPE_WIRED_HEADSET}
     *   <li> {@link AudioDeviceInfo#TYPE_BLUETOOTH_SCO}
     *   <li> {@link AudioDeviceInfo#TYPE_USB_HEADSET}
     *   <li> {@link AudioDeviceInfo#TYPE_BLE_HEADSET}
     * </ul>
     * The selection is active as long as the requesting application lives, until
     * {@link #clearDeviceForCommunication} is called or until the device is disconnected.
     * <p>The device selection is expressed as an {@link AudioDeviceInfo} among devices returned by
     * {@link #getAvailableCommunicationDevices()}.
     * The selection is active as long as the requesting application process lives, until
     * {@link #clearCommunicationDevice} is called or until the device is disconnected.
     * It is therefore important for applications to clear the request when a call ends or the
     * application is paused.
     * the requesting activity or service is stopped or destroyed.
     * <p>In case of simultaneous requests by multiple applications the priority is given to the
     * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
     * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
@@ -7097,7 +7089,7 @@ public class AudioManager {
     * AudioManager audioManager = Context.getSystemService(AudioManager.class);
     * try {
     *     AudioDeviceInfo speakerDevice = null;
     *     AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_OUTPUTS);
     *     List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
     *     for (AudioDeviceInfo device : devices) {
     *         if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
     *             speakerDevice = device;
@@ -7106,12 +7098,12 @@ public class AudioManager {
     *     }
     *     if (speakerDevice != null) {
     *         // Turn speakerphone ON.
     *         boolean result = audioManager.setDeviceForCommunication(speakerDevice);
     *         boolean result = audioManager.setCommunicationDevice(speakerDevice);
     *         if (!result) {
     *             // Handle error.
     *         }
     *         // Turn speakerphone OFF.
     *         audioManager.clearDeviceForCommunication();
     *         audioManager.clearCommunicationDevice();
     *     }
     * } catch (IllegalArgumentException e) {
     *     // Handle exception.
@@ -7121,13 +7113,13 @@ public class AudioManager {
     * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
     * @throws IllegalArgumentException If an invalid device is specified.
     */
    public boolean setDeviceForCommunication(@NonNull AudioDeviceInfo device) {
    public boolean setCommunicationDevice(@NonNull AudioDeviceInfo device) {
        Objects.requireNonNull(device);
        try {
            if (device.getId() == 0) {
                throw new IllegalArgumentException("In valid device: " + device);
            }
            return getService().setDeviceForCommunication(mICallBack, device.getId());
            return getService().setCommunicationDevice(mICallBack, device.getId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -7135,11 +7127,11 @@ public class AudioManager {

    /**
     * Cancels previous communication device selection made with
     * {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * {@link #setCommunicationDevice(AudioDeviceInfo)}.
     */
    public void clearDeviceForCommunication() {
    public void clearCommunicationDevice() {
        try {
            getService().setDeviceForCommunication(mICallBack, 0);
            getService().setCommunicationDevice(mICallBack, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -7153,14 +7145,38 @@ public class AudioManager {
     *   <li> {@link #isSpeakerphoneOn()}
     * </ul>
     * @return an {@link AudioDeviceInfo} indicating which audio device is
     * currently selected or communication use cases or null if default selection
     * currently selected for communication use cases. Can be null on platforms
     * not supporting {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
     * is used.
     */
    @Nullable
    public AudioDeviceInfo getDeviceForCommunication() {
    public AudioDeviceInfo getCommunicationDevice() {
        try {
            return getDeviceForPortId(
                    getService().getDeviceForCommunication(), GET_DEVICES_OUTPUTS);
                    getService().getCommunicationDevice(), GET_DEVICES_OUTPUTS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns a list of audio devices that can be selected for communication use cases via
     * {@link #setCommunicationDevice(AudioDeviceInfo)}.
     * @return a list of {@link AudioDeviceInfo} suitable for use with setCommunicationDevice().
     */
    @NonNull
    public List<AudioDeviceInfo> getAvailableCommunicationDevices() {
        try {
            ArrayList<AudioDeviceInfo> devices = new ArrayList<>();
            int[] portIds = getService().getAvailableCommunicationDeviceIds();
            for (int portId : portIds) {
                AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
                if (device == null) {
                    continue;
                }
                devices.add(device);
            }
            return devices;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -7183,30 +7199,59 @@ public class AudioManager {
    @Nullable
    public static AudioDeviceInfo getDeviceInfoFromType(
            @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
        return getDeviceInfoFromTypeAndAddress(deviceType, null);
    }

        /**
     * @hide
     * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type and
     * address provided.
     * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
     * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
     * If a null address is provided, the matching will happen on the type only.
     * The method will return null if no device of the provided type and address is connected.
     * If more than one device of the provided type is connected, an object corresponding to the
     * first device encountered in the enumeration list will be returned.
     * @param type The device device for which an <code>AudioDeviceInfo</code>
     *             object is queried.
     * @param address The device address for which an <code>AudioDeviceInfo</code>
     *                object is queried or null if requesting match on type only.
     * @return An AudioDeviceInfo object or null if no matching device is connected.
     * @throws IllegalArgumentException If an invalid device type is specified.
     */
    @Nullable
    public static AudioDeviceInfo getDeviceInfoFromTypeAndAddress(
            @AudioDeviceInfo.AudioDeviceTypeOut int type, @Nullable String address) {
        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
        AudioDeviceInfo deviceForType = null;
        for (AudioDeviceInfo device : devices) {
            if (device.getType() == deviceType) {
            if (device.getType() == type) {
                deviceForType = device;
                if (address == null || address.equals(device.getAddress())) {
                    return device;
                }
            }
        return null;
        }
        return deviceForType;
    }

    /**
     * Listener registered by client to be notified upon communication audio device change.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
     */
    public interface OnCommunicationDeviceChangedListener {
        /**
         * Callback method called upon communication audio device change.
         * @param device the audio device selected for communication use cases
         * @param device the audio device requested for communication use cases.
         *               Can be null on platforms not supporting
         *               {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
         */
        void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
    }

    /**
     * Adds a listener for being notified of changes to the communication audio device.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
     * @param executor
     * @param listener
     */
@@ -7243,7 +7288,7 @@ public class AudioManager {

    /**
     * Removes a previously added listener of changes to the communication audio device.
     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
     * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
     * @param listener
     */
    public void removeOnCommunicationDeviceChangedListener(
+4 −2
Original line number Diff line number Diff line
@@ -342,9 +342,11 @@ interface IAudioService {

    int getDevicesForStream(in int streamType);

    boolean setDeviceForCommunication(IBinder cb, int portId);
    int[] getAvailableCommunicationDeviceIds();

    int getDeviceForCommunication();
    boolean setCommunicationDevice(IBinder cb, int portId);

    int getCommunicationDevice();

    void registerCommunicationDeviceDispatcher(ICommunicationDeviceDispatcher dispatcher);

+30 −18
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
@@ -80,7 +81,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
    private final @NonNull Context mContext;

    /** Forced device usage for communications sent to AudioSystem */
    private AudioDeviceAttributes mPreferredDeviceforComm;
    private AudioDeviceAttributes mPreferredCommunicationDevice;
    private int mCommunicationStrategyId = -1;

    // Manages all connected devices, only ever accessed on the message loop
@@ -152,7 +153,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
    private void init() {
        setupMessaging(mContext);

        mPreferredDeviceforComm = null;
        mPreferredCommunicationDevice = null;
        initCommunicationStrategyId();

        mSystemServer.registerUserStartedReceiver(mContext);
@@ -260,11 +261,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
     * @param device Device selected or null to unselect.
     * @param eventSource for logging purposes
     */
    /*package*/ boolean setDeviceForCommunication(
    /*package*/ boolean setCommunicationDevice(
            IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {

        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "setDeviceForCommunication, device: " + device + ", pid: " + pid);
            Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid);
        }

        synchronized (mSetModeLock) {
@@ -385,13 +386,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
     * Returns the device currently requested for communication use case.
     * @return AudioDeviceInfo the requested device for communication.
     */
    AudioDeviceInfo getDeviceForCommunication() {
    AudioDeviceInfo getCommunicationDevice() {
        synchronized (mDeviceStateLock) {
            AudioDeviceAttributes device = requestedCommunicationDevice();
            if (device == null) {
                AudioAttributes attr =
                        AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
                                AudioSystem.STREAM_VOICE_CALL);
                List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
                if (devices.isEmpty()) {
                    return null;
                }
            return AudioManager.getDeviceInfoFromType(device.getType());
                device = devices.get(0);
            }
            return AudioManager.getDeviceInfoFromTypeAndAddress(
                    device.getType(), device.getAddress());
        }
    }

@@ -736,8 +745,8 @@ import java.util.concurrent.atomic.AtomicBoolean;

    @GuardedBy("mDeviceStateLock")
    private void dispatchCommunicationDevice() {
        AudioDeviceInfo device = getDeviceForCommunication();
        int portId = (getDeviceForCommunication() == null) ? 0 : device.getId();
        AudioDeviceInfo device = getCommunicationDevice();
        int portId = (getCommunicationDevice() == null) ? 0 : device.getId();
        if (portId == mCurCommunicationPortId) {
            return;
        }
@@ -1010,8 +1019,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
            pw.println("  " + prefix + "pid: " + cl.getPid() + " device: "
                        + cl.getDevice() + " cb: " + cl.getBinder()); });

        pw.println("\n" + prefix + "mPreferredDeviceforComm: "
                +  mPreferredDeviceforComm);
        pw.println("\n" + prefix + "mPreferredCommunicationDevice: "
                +  mPreferredCommunicationDevice);
        pw.println(prefix + "Selected Communication Device: "
                +  ((getCommunicationDevice() == null) ? "None"
                        : new AudioDeviceAttributes(getCommunicationDevice())));
        pw.println(prefix + "mCommunicationStrategyId: "
                +  mCommunicationStrategyId);

@@ -1692,24 +1704,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
    // @GuardedBy("mSetModeLock")
    @GuardedBy("mDeviceStateLock")
    private void onUpdateCommunicationRoute(String eventSource) {
        mPreferredDeviceforComm = getPreferredDeviceForComm();
        mPreferredCommunicationDevice = getPreferredDeviceForComm();
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "onUpdateCommunicationRoute, mPreferredDeviceforComm: "
                    + mPreferredDeviceforComm + " eventSource: " + eventSource);
            Log.v(TAG, "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
                    + mPreferredCommunicationDevice + " eventSource: " + eventSource);
        }

        if (mPreferredDeviceforComm == null
        if (mPreferredCommunicationDevice == null
                || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
                        mPreferredDeviceforComm.getInternalType())) {
                        mPreferredCommunicationDevice.getInternalType())) {
            AudioSystem.setParameters("BT_SCO=off");
        } else {
            AudioSystem.setParameters("BT_SCO=on");
        }
        if (mPreferredDeviceforComm == null) {
        if (mPreferredCommunicationDevice == null) {
            postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
        } else {
            postSetPreferredDevicesForStrategy(
                    mCommunicationStrategyId, Arrays.asList(mPreferredDeviceforComm));
                    mCommunicationStrategyId, Arrays.asList(mPreferredCommunicationDevice));
        }
        mAudioService.postUpdateRingerModeServiceInt();
        dispatchCommunicationDevice();
+20 −9
Original line number Diff line number Diff line
@@ -4761,8 +4761,19 @@ public class AudioService extends IAudioService.Stub
        return false;
    }

    /** @see AudioManager#setDeviceForCommunication(int) */
    public boolean setDeviceForCommunication(IBinder cb, int portId) {
    /** @see AudioManager#getAvailableCommunicationDevices(int) */
    public int[] getAvailableCommunicationDeviceIds() {
        ArrayList<Integer> deviceIds = new ArrayList<>();
        AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
        for (AudioDeviceInfo device : devices) {
            if (isValidCommunicationDevice(device)) {
                deviceIds.add(device.getId());
            }
        }
        return deviceIds.stream().mapToInt(Integer::intValue).toArray();
    }
        /** @see AudioManager#setCommunicationDevice(int) */
    public boolean setCommunicationDevice(IBinder cb, int portId) {
        final int uid = Binder.getCallingUid();
        final int pid = Binder.getCallingPid();

@@ -4776,7 +4787,7 @@ public class AudioService extends IAudioService.Stub
                throw new IllegalArgumentException("invalid device type " + device.getType());
            }
        }
        final String eventSource = new StringBuilder("setDeviceForCommunication(")
        final String eventSource = new StringBuilder("setCommunicationDevice(")
                .append(") from u/pid:").append(uid).append("/")
                .append(pid).toString();

@@ -4786,7 +4797,7 @@ public class AudioService extends IAudioService.Stub
            deviceType = device.getPort().type();
            deviceAddress = device.getAddress();
        } else {
            AudioDeviceInfo curDevice = mDeviceBroker.getDeviceForCommunication();
            AudioDeviceInfo curDevice = mDeviceBroker.getCommunicationDevice();
            if (curDevice != null) {
                deviceType = curDevice.getPort().type();
                deviceAddress = curDevice.getAddress();
@@ -4796,7 +4807,7 @@ public class AudioService extends IAudioService.Stub
        // was selected
        if (deviceType != AudioSystem.DEVICE_OUT_DEFAULT) {
            new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
                    + MediaMetrics.SEPARATOR + "setDeviceForCommunication")
                    + MediaMetrics.SEPARATOR + "setCommunicationDevice")
                    .set(MediaMetrics.Property.DEVICE,
                            AudioSystem.getDeviceName(deviceType))
                    .set(MediaMetrics.Property.ADDRESS, deviceAddress)
@@ -4807,15 +4818,15 @@ public class AudioService extends IAudioService.Stub

        final long ident = Binder.clearCallingIdentity();
        boolean status =
                mDeviceBroker.setDeviceForCommunication(cb, pid, device, eventSource);
                mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
        Binder.restoreCallingIdentity(ident);
        return status;
    }

    /** @see AudioManager#getDeviceForCommunication() */
    public int getDeviceForCommunication() {
    /** @see AudioManager#getCommunicationDevice() */
    public int getCommunicationDevice() {
        final long ident = Binder.clearCallingIdentity();
        AudioDeviceInfo device = mDeviceBroker.getDeviceForCommunication();
        AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
        Binder.restoreCallingIdentity(ident);
        if (device == null) {
            return 0;