AudioManager: address API review comments

Address API council comments for new AudioManager
API setDeviceForCommunication.

Bug: 179709789
Test: ates AudioCommunicationTest
Change-Id: I1c656578f614c735e8ad839b814ddeb175213400
This commit is contained in:
Eric Laurent
2021-02-11 15:10:31 +01:00
parent 1d57fc25ae
commit 7412f57550
5 changed files with 135 additions and 64 deletions

View File

@@ -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);

View File

@@ -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();
}
@@ -7175,7 +7191,7 @@ public class AudioManager {
* 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 deviceType The device device for which an <code>AudioDeviceInfo</code>
* object is queried.
* object is queried.
* @return An AudioDeviceInfo object or null if no device with the requested type is connected.
* @throws IllegalArgumentException If an invalid device type is specified.
*/
@@ -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) {
return device;
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(

View File

@@ -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);

View File

@@ -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) {
return null;
AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
AudioSystem.STREAM_VOICE_CALL);
List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
if (devices.isEmpty()) {
return null;
}
device = devices.get(0);
}
return AudioManager.getDeviceInfoFromType(device.getType());
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();

View File

@@ -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;