AudioService: fix communication device APIs

make sure getAvailableCommunicationDevices() and
getCommunicationDevice() always return null or a valid communication
device,

Bug: 265613261
Bug: 266067856
Test: atest AudioCommunicationDeviceTest

Change-Id: I1fc0bc8a372d49b0fb6c8b25fd51fbcf321bab6f
This commit is contained in:
Eric Laurent
2023-01-25 14:54:00 +01:00
parent 7f5fddd365
commit 1dea0dab31
2 changed files with 81 additions and 50 deletions

View File

@@ -58,6 +58,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.EventLogger; import com.android.server.utils.EventLogger;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@@ -453,6 +454,48 @@ import java.util.concurrent.atomic.AtomicBoolean;
return device; return device;
} }
private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
AudioDeviceInfo.TYPE_WIRED_HEADSET,
AudioDeviceInfo.TYPE_USB_HEADSET,
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
AudioDeviceInfo.TYPE_HEARING_AID,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_USB_DEVICE,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
AudioDeviceInfo.TYPE_LINE_ANALOG,
AudioDeviceInfo.TYPE_HDMI,
AudioDeviceInfo.TYPE_AUX_LINE
};
/*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
if (device.getType() == type) {
return true;
}
}
return false;
}
/* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
AudioDeviceInfo[] allDevices =
AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo device : allDevices) {
if (isValidCommunicationDevice(device)) {
commDevices.add(device);
}
}
return commDevices;
}
private @Nullable AudioDeviceInfo getCommunicationDeviceOfType(int type) {
return getAvailableCommunicationDevices().stream().filter(d -> d.getType() == type)
.findFirst().orElse(null);
}
/** /**
* Returns the device currently requested for communication use case. * Returns the device currently requested for communication use case.
* @return AudioDeviceInfo the requested device for communication. * @return AudioDeviceInfo the requested device for communication.
@@ -460,7 +503,29 @@ import java.util.concurrent.atomic.AtomicBoolean;
/* package */ AudioDeviceInfo getCommunicationDevice() { /* package */ AudioDeviceInfo getCommunicationDevice() {
synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) {
updateActiveCommunicationDevice(); updateActiveCommunicationDevice();
return mActiveCommunicationDevice; AudioDeviceInfo device = mActiveCommunicationDevice;
// make sure we return a valid communication device (i.e. a device that is allowed by
// setCommunicationDevice()) for consistency.
if (device != null) {
// a digital dock is used instead of the speaker in speakerphone mode and should
// be reflected as such
if (device.getType() == AudioDeviceInfo.TYPE_DOCK) {
device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
}
// Try to default to earpiece when current communication device is not valid. This can
// happen for instance if no call is active. If no earpiece device is available take the
// first valid communication device
if (device == null || !AudioDeviceBroker.isValidCommunicationDevice(device)) {
device = getCommunicationDeviceOfType(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
if (device == null) {
List<AudioDeviceInfo> commDevices = getAvailableCommunicationDevices();
if (!commDevices.isEmpty()) {
device = commDevices.get(0);
}
}
}
return device;
} }
} }
@@ -942,8 +1007,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
@GuardedBy("mDeviceStateLock") @GuardedBy("mDeviceStateLock")
private void dispatchCommunicationDevice() { private void dispatchCommunicationDevice() {
int portId = (mActiveCommunicationDevice == null) ? 0 AudioDeviceInfo device = getCommunicationDevice();
: mActiveCommunicationDevice.getId(); int portId = device != null ? device.getId() : 0;
if (portId == mCurCommunicationPortId) { if (portId == mCurCommunicationPortId) {
return; return;
} }
@@ -960,6 +1025,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
mCommDevDispatchers.finishBroadcast(); mCommDevDispatchers.finishBroadcast();
} }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Communication with (to) AudioService // Communication with (to) AudioService
//TODO check whether the AudioService methods are candidates to move here //TODO check whether the AudioService methods are candidates to move here

View File

@@ -6067,45 +6067,12 @@ public class AudioService extends IAudioService.Stub
restoreDeviceVolumeBehavior(); restoreDeviceVolumeBehavior();
} }
private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
AudioDeviceInfo.TYPE_WIRED_HEADSET,
AudioDeviceInfo.TYPE_USB_HEADSET,
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
AudioDeviceInfo.TYPE_HEARING_AID,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_USB_DEVICE,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
AudioDeviceInfo.TYPE_LINE_ANALOG,
AudioDeviceInfo.TYPE_HDMI,
AudioDeviceInfo.TYPE_AUX_LINE
};
private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
if (!device.isSink()) {
return false;
}
for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
if (device.getType() == type) {
return true;
}
}
return false;
}
/** @see AudioManager#getAvailableCommunicationDevices(int) */ /** @see AudioManager#getAvailableCommunicationDevices(int) */
public int[] getAvailableCommunicationDeviceIds() { public int[] getAvailableCommunicationDeviceIds() {
ArrayList<Integer> deviceIds = new ArrayList<>(); List<AudioDeviceInfo> commDevices = AudioDeviceBroker.getAvailableCommunicationDevices();
AudioDeviceInfo[] devices = AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS); return commDevices.stream().mapToInt(AudioDeviceInfo::getId).toArray();
for (AudioDeviceInfo device : devices) {
if (isValidCommunicationDevice(device)) {
deviceIds.add(device.getId());
}
}
return deviceIds.stream().mapToInt(Integer::intValue).toArray();
} }
/** /**
* @see AudioManager#setCommunicationDevice(int) * @see AudioManager#setCommunicationDevice(int)
* @see AudioManager#clearCommunicationDevice() * @see AudioManager#clearCommunicationDevice()
@@ -6120,7 +6087,7 @@ public class AudioService extends IAudioService.Stub
if (device == null) { if (device == null) {
throw new IllegalArgumentException("invalid portID " + portId); throw new IllegalArgumentException("invalid portID " + portId);
} }
if (!isValidCommunicationDevice(device)) { if (!AudioDeviceBroker.isValidCommunicationDevice(device)) {
if (!device.isSink()) { if (!device.isSink()) {
throw new IllegalArgumentException("device must have sink role"); throw new IllegalArgumentException("device must have sink role");
} else { } else {
@@ -6168,17 +6135,15 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getCommunicationDevice() */ /** @see AudioManager#getCommunicationDevice() */
public int getCommunicationDevice() { public int getCommunicationDevice() {
AudioDeviceInfo device = null; int deviceId = 0;
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
device = mDeviceBroker.getCommunicationDevice(); AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
deviceId = device != null ? device.getId() : 0;
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
if (device == null) { return deviceId;
return 0;
}
return device.getId();
} }
/** @see AudioManager#addOnCommunicationDeviceChangedListener( /** @see AudioManager#addOnCommunicationDeviceChangedListener(