Use device set for getDevicesForStream

Implemented through getDevicesForAttributes.
This change avoids device bit-mask aliasing.

Use a TreeSet for device set instead of
HashSet to unify with native handling.

Test: boot and check audio
Test: atest audiopolicy_tests
Test: atest AudioHostTest
Test: atest AudioServiceHostTest
Test: atest AudioManagerTest
Bug: 184944421
Bug: 220928984
Change-Id: Id382172f44f604de659de0bff52c0a28603d1e79
This commit is contained in:
Andy Hung
2021-04-13 19:31:00 -07:00
parent 515b6b3a49
commit 1162c55ed4
7 changed files with 224 additions and 176 deletions

View File

@@ -828,13 +828,6 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz)
return balance;
}
static jint
android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
{
return (jint)deviceTypesToBitMask(
AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(stream)));
}
static jint
android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz)
{
@@ -2959,7 +2952,6 @@ static const JNINativeMethod gMethods[] =
{"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono},
{"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance},
{"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance},
{"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
{"getPrimaryOutputSamplingRate", "()I",
(void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
{"getPrimaryOutputFrameCount", "()I",

View File

@@ -5625,27 +5625,33 @@ public class AudioManager {
* Note that the information may be imprecise when the implementation
* cannot distinguish whether a particular device is enabled.
*
* {@hide}
* @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
* will have multi-bit device types.
* Prefer to use {@link #getDevicesForAttributes()} instead,
* noting that getDevicesForStream() has a few small discrepancies
* for better volume handling.
* @hide
*/
@UnsupportedAppUsage
@Deprecated
public int getDevicesForStream(int streamType) {
switch (streamType) {
case STREAM_VOICE_CALL:
case STREAM_SYSTEM:
case STREAM_RING:
case STREAM_MUSIC:
case STREAM_ALARM:
case STREAM_NOTIFICATION:
case STREAM_DTMF:
case STREAM_ACCESSIBILITY:
final IAudioService service = getService();
try {
return service.getDevicesForStream(streamType);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
default:
return 0;
case STREAM_VOICE_CALL:
case STREAM_SYSTEM:
case STREAM_RING:
case STREAM_MUSIC:
case STREAM_ALARM:
case STREAM_NOTIFICATION:
case STREAM_DTMF:
case STREAM_ACCESSIBILITY:
final IAudioService service = getService();
try {
return service.getDeviceMaskForStream(streamType);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
default:
return 0;
}
}

View File

@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
import android.media.audio.common.AidlConversion;
import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioProductStrategy;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
@@ -47,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
/* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
* TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -1655,9 +1657,63 @@ public class AudioSystem
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static native boolean getMasterMute();
/** @hide */
/** @hide
* Only used (unsupported) for legacy apps.
* @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
* will have multi-bit device types.
* Use {@link AudioManager#getDevicesForAttributes(AudioAttributes)} instead.
*/
@UnsupportedAppUsage
public static native int getDevicesForStream(int stream);
@Deprecated
public static int getDevicesForStream(int stream) {
final AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
return getDeviceMaskFromSet(generateAudioDeviceTypesSet(
getDevicesForAttributes(attr, true /* forVolume */)));
}
/** @hide
* Conversion from a device set to a bit mask.
*
* Legacy compatibility method (use a device list instead of a bit mask).
* Conversion to bit mask skips multi-bit (S and later) internal device types
* (e.g. AudioSystem.DEVICE_OUT* or AudioManager.DEVICE_OUT*) for legacy
* compatibility reasons. Legacy apps will not understand these new device types
* and it will raise false matches with old device types.
*/
public static int getDeviceMaskFromSet(@NonNull Set<Integer> deviceSet) {
int deviceMask = DEVICE_NONE; // zero.
int deviceInChecksum = DEVICE_BIT_IN;
for (Integer device : deviceSet) {
if ((device & (device - 1) & ~DEVICE_BIT_IN) != 0) {
Log.v(TAG, "getDeviceMaskFromSet skipping multi-bit device value " + device);
continue;
}
deviceMask |= device;
deviceInChecksum &= device;
}
// Validate that deviceSet is either ALL input devices or ALL output devices.
// We check that the "OR" of all the DEVICE_BIT_INs == the "AND" of all DEVICE_BIT_INs.
if (!deviceSet.isEmpty() && deviceInChecksum != (deviceMask & DEVICE_BIT_IN)) {
Log.e(TAG, "getDeviceMaskFromSet: Invalid set: " + deviceSetToString(deviceSet));
}
return deviceMask;
}
/** @hide */
@NonNull
public static String deviceSetToString(@NonNull Set<Integer> devices) {
int n = 0;
StringBuilder sb = new StringBuilder();
for (Integer device : devices) {
if (n++ > 0) {
sb.append(", ");
}
sb.append(AudioSystem.getDeviceName(device));
sb.append("(" + Integer.toHexString(device) + ")");
}
return sb.toString();
}
/**
* @hide
@@ -2252,18 +2308,15 @@ public class AudioSystem
/**
* @hide
* Return a set of audio device types from a bit mask audio device type, which may
* Return a set of audio device types from a list of audio device attributes, which may
* represent multiple audio device types.
* FIXME: Remove this when getting ride of bit mask usage of audio device types.
*/
public static Set<Integer> generateAudioDeviceTypesSet(int types) {
Set<Integer> deviceTypes = new HashSet<>();
Set<Integer> allDeviceTypes =
(types & DEVICE_BIT_IN) == 0 ? DEVICE_OUT_ALL_SET : DEVICE_IN_ALL_SET;
for (int deviceType : allDeviceTypes) {
if ((types & deviceType) == deviceType) {
deviceTypes.add(deviceType);
}
@NonNull
public static Set<Integer> generateAudioDeviceTypesSet(
@NonNull List<AudioDeviceAttributes> deviceList) {
Set<Integer> deviceTypes = new TreeSet<>();
for (AudioDeviceAttributes device : deviceList) {
deviceTypes.add(device.getInternalType());
}
return deviceTypes;
}
@@ -2274,7 +2327,7 @@ public class AudioSystem
*/
public static Set<Integer> intersectionAudioDeviceTypes(
@NonNull Set<Integer> a, @NonNull Set<Integer> b) {
Set<Integer> intersection = new HashSet<>(a);
Set<Integer> intersection = new TreeSet<>(a);
intersection.retainAll(b);
return intersection;
}

View File

@@ -360,7 +360,7 @@ interface IAudioService {
boolean isMusicActive(in boolean remotely);
int getDevicesForStream(in int streamType);
int getDeviceMaskForStream(in int streamType);
int[] getAvailableCommunicationDeviceIds();

View File

@@ -1565,6 +1565,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
private AtomicBoolean mMusicMuted = new AtomicBoolean(false);
private static <T> boolean hasIntersection(Set<T> a, Set<T> b) {
for (T e : a) {
if (b.contains(e)) return true;
}
return false;
}
boolean messageMutesMusic(int message) {
if (message == 0) {
return false;
@@ -1574,8 +1581,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|| message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT
|| message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE)
&& AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
&& mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(
mAudioService.getDevicesForStream(AudioSystem.STREAM_MUSIC))) {
&& hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
return false;
}
return true;

View File

@@ -196,6 +196,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -1823,6 +1824,7 @@ public class AudioService extends IAudioService.Stub
}
// Check if device to be updated is routed for the given audio stream
// This may include devices such as SPEAKER_SAFE.
List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt(
new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build(),
true /* forVolume */);
@@ -3694,8 +3696,7 @@ public class AudioService extends IAudioService.Stub
int streamType = getBluetoothContextualVolumeStream(newMode);
final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
mAudioSystem.getDevicesForStream(streamType));
final Set<Integer> deviceTypes = getDeviceSetForStreamDirect(streamType);
final Set<Integer> absVolumeMultiModeCaseDevices = AudioSystem.intersectionAudioDeviceTypes(
mAbsVolumeMultiModeCaseDevices, deviceTypes);
if (absVolumeMultiModeCaseDevices.isEmpty()) {
@@ -6259,55 +6260,104 @@ public class AudioService extends IAudioService.Stub
}
}
/** only public for mocking/spying, do not call outside of AudioService */
/**
* Returns device associated with the stream volume.
*
* Only public for mocking/spying, do not call outside of AudioService.
* Device volume aliasing means DEVICE_OUT_SPEAKER may be returned for
* DEVICE_OUT_SPEAKER_SAFE.
*/
@VisibleForTesting
public int getDeviceForStream(int stream) {
int device = getDevicesForStreamInt(stream);
if ((device & (device - 1)) != 0) {
return selectOneAudioDevice(getDeviceSetForStream(stream));
}
/*
* Must match native apm_extract_one_audio_device() used in getDeviceForVolume()
* or the wrong device volume may be adjusted.
*/
private int selectOneAudioDevice(Set<Integer> deviceSet) {
if (deviceSet.isEmpty()) {
return AudioSystem.DEVICE_NONE;
} else if (deviceSet.size() == 1) {
return deviceSet.iterator().next();
} else {
// Multiple device selection is either:
// - speaker + one other device: give priority to speaker in this case.
// - one A2DP device + another device: happens with duplicated output. In this case
// retain the device on the A2DP output as the other must not correspond to an active
// selection if not the speaker.
// - HDMI-CEC system audio mode only output: give priority to available item in order.
// FIXME: Haven't applied audio device type refactor to this API
// as it is going to be deprecated.
if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
device = AudioSystem.DEVICE_OUT_SPEAKER;
} else if ((device & AudioSystem.DEVICE_OUT_HDMI_ARC) != 0) {
// FIXME(b/184944421): DEVICE_OUT_HDMI_EARC has two bits set,
// so it must be handled correctly as it aliases
// with DEVICE_OUT_HDMI_ARC | DEVICE_OUT_EARPIECE.
device = AudioSystem.DEVICE_OUT_HDMI_ARC;
} else if ((device & AudioSystem.DEVICE_OUT_SPDIF) != 0) {
device = AudioSystem.DEVICE_OUT_SPDIF;
} else if ((device & AudioSystem.DEVICE_OUT_AUX_LINE) != 0) {
device = AudioSystem.DEVICE_OUT_AUX_LINE;
if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
return AudioSystem.DEVICE_OUT_SPEAKER;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER_SAFE)) {
// Note: DEVICE_OUT_SPEAKER_SAFE not present in getDeviceSetForStreamDirect
return AudioSystem.DEVICE_OUT_SPEAKER_SAFE;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_ARC)) {
return AudioSystem.DEVICE_OUT_HDMI_ARC;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_HDMI_EARC)) {
return AudioSystem.DEVICE_OUT_HDMI_EARC;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPDIF)) {
return AudioSystem.DEVICE_OUT_SPDIF;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_AUX_LINE)) {
return AudioSystem.DEVICE_OUT_AUX_LINE;
} else {
for (int deviceType : AudioSystem.DEVICE_OUT_ALL_A2DP_SET) {
if ((deviceType & device) == deviceType) {
if (deviceSet.contains(deviceType)) {
return deviceType;
}
}
}
}
return device;
Log.w(TAG, "selectOneAudioDevice returning DEVICE_NONE from invalid device combination "
+ AudioSystem.deviceSetToString(deviceSet));
return AudioSystem.DEVICE_NONE;
}
/**
* @see AudioManager#getDevicesForStream(int)
* @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
* will have multi-bit device types since S.
* Use {@link #getDevicesForAttributes()} instead.
*/
public int getDevicesForStream(int streamType) {
@Override
@Deprecated
public int getDeviceMaskForStream(int streamType) {
ensureValidStreamType(streamType);
// no permission required
final long token = Binder.clearCallingIdentity();
try {
return mAudioSystem.getDevicesForStream(streamType);
return AudioSystem.getDeviceMaskFromSet(
getDeviceSetForStreamDirect(streamType));
} finally {
Binder.restoreCallingIdentity(token);
}
}
private int getDevicesForStreamInt(int stream) {
/**
* Returns the devices associated with a stream type.
*
* SPEAKER_SAFE will alias to SPEAKER.
*/
@NonNull
private Set<Integer> getDeviceSetForStreamDirect(int stream) {
final AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
Set<Integer> deviceSet =
AudioSystem.generateAudioDeviceTypesSet(
getDevicesForAttributesInt(attr, true /* forVolume */));
return deviceSet;
}
/**
* Returns a reference to the list of devices for the stream, do not modify.
*
* The device returned may be aliased to the actual device whose volume curve
* will be used. For example DEVICE_OUT_SPEAKER_SAFE aliases to DEVICE_OUT_SPEAKER.
*/
@NonNull
public Set<Integer> getDeviceSetForStream(int stream) {
ensureValidStreamType(stream);
synchronized (VolumeStreamState.class) {
return mStreamStates[stream].observeDevicesForStream_syncVSS(true);
@@ -6319,11 +6369,10 @@ public class AudioService extends IAudioService.Stub
synchronized (VolumeStreamState.class) {
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (stream != skipStream) {
int devices = mStreamStates[stream].observeDevicesForStream_syncVSS(
false /*checkOthers*/);
Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices);
for (Integer device : devicesSet) {
Set<Integer> deviceSet =
mStreamStates[stream].observeDevicesForStream_syncVSS(
false /*checkOthers*/);
for (Integer device : deviceSet) {
// Update volume states for devices routed for the stream
updateVolumeStates(device, stream,
"AudioService#onObserveDevicesForAllStreams");
@@ -6644,7 +6693,7 @@ public class AudioService extends IAudioService.Stub
&& DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
&& (newDevice & mAudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
&& getDeviceSetForStreamDirect(AudioSystem.STREAM_MUSIC).contains(newDevice)) {
if (DEBUG_VOL) {
Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
@@ -7032,7 +7081,7 @@ public class AudioService extends IAudioService.Stub
private boolean mIsMuted;
private boolean mIsMutedInternally;
private String mVolumeIndexSettingName;
private int mObservedDevices;
@NonNull private Set<Integer> mObservedDeviceSet = new TreeSet<>();
private final SparseIntArray mIndexMap = new SparseIntArray(8) {
@Override
@@ -7099,17 +7148,30 @@ public class AudioService extends IAudioService.Stub
}
}
/**
* Returns a list of devices associated with the stream type.
*
* This is a reference to the local list, do not modify.
*/
@GuardedBy("VolumeStreamState.class")
public int observeDevicesForStream_syncVSS(boolean checkOthers) {
@NonNull
public Set<Integer> observeDevicesForStream_syncVSS(
boolean checkOthers) {
if (!mSystemServer.isPrivileged()) {
return AudioSystem.DEVICE_NONE;
return new TreeSet<Integer>();
}
final int devices = mAudioSystem.getDevicesForStream(mStreamType);
if (devices == mObservedDevices) {
return devices;
final Set<Integer> deviceSet =
getDeviceSetForStreamDirect(mStreamType);
if (deviceSet.equals(mObservedDeviceSet)) {
return mObservedDeviceSet;
}
final int prevDevices = mObservedDevices;
mObservedDevices = devices;
// Use legacy bit masks for message signalling.
// TODO(b/185386781): message needs update since it uses devices bit-mask.
final int devices = AudioSystem.getDeviceMaskFromSet(deviceSet);
final int prevDevices = AudioSystem.getDeviceMaskFromSet(mObservedDeviceSet);
mObservedDeviceSet = deviceSet;
if (checkOthers) {
// one stream's devices have changed, check the others
postObserveDevicesForAllStreams(mStreamType);
@@ -7125,7 +7187,7 @@ public class AudioService extends IAudioService.Stub
SENDMSG_QUEUE, prevDevices /*arg1*/, devices /*arg2*/,
// ok to send reference to this object, it is final
mStreamDevicesChanged /*obj*/, 0 /*delay*/);
return devices;
return mObservedDeviceSet;
}
public @Nullable String getSettingNameForDevice(int device) {
@@ -7556,19 +7618,7 @@ public class AudioService extends IAudioService.Stub
}
pw.println();
pw.print(" Devices: ");
final int devices = getDevicesForStreamInt(mStreamType);
int device, i = 0, n = 0;
// iterate all devices from 1 to DEVICE_OUT_DEFAULT exclusive
// (the default device is not returned by getDevicesForStreamInt)
while ((device = 1 << i) != AudioSystem.DEVICE_OUT_DEFAULT) {
if ((devices & device) != 0) {
if (n++ > 0) {
pw.print(", ");
}
pw.print(AudioSystem.getOutputDeviceName(device));
}
i++;
}
pw.print(AudioSystem.deviceSetToString(getDeviceSetForStream(mStreamType)));
}
}
@@ -9325,7 +9375,9 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.setForceUse_Async(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config,
"setHdmiSystemAudioSupported");
}
device = getDevicesForStreamInt(AudioSystem.STREAM_MUSIC);
// TODO(b/185386781): Update AudioManager API to use device list.
// So far, this value appears to be advisory for debug log.
device = getDeviceMaskForStream(AudioSystem.STREAM_MUSIC);
}
}
return device;

View File

@@ -52,15 +52,13 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
* in measured methods
*/
private static final boolean ENABLE_GETDEVICES_STATS = false;
private static final int NB_MEASUREMENTS = 2;
private static final int METHOD_GETDEVICESFORSTREAM = 0;
private static final int METHOD_GETDEVICESFORATTRIBUTES = 1;
private static final int NB_MEASUREMENTS = 1;
private static final int METHOD_GETDEVICESFORATTRIBUTES = 0;
private long[] mMethodTimeNs;
private int[] mMethodCallCounter;
private String[] mMethodNames = {"getDevicesForStream", "getDevicesForAttributes"};
private String[] mMethodNames = {"getDevicesForAttributes"};
private static final boolean USE_CACHE_FOR_GETDEVICES = true;
private ConcurrentHashMap<Integer, Integer> mDevicesForStreamCache;
private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
@@ -113,8 +111,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
sSingletonDefaultAdapter = new AudioSystemAdapter();
AudioSystem.setRoutingCallback(sSingletonDefaultAdapter);
if (USE_CACHE_FOR_GETDEVICES) {
sSingletonDefaultAdapter.mDevicesForStreamCache =
new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
sSingletonDefaultAdapter.mDevicesForAttrCache =
new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes());
sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS];
@@ -131,11 +127,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
if (DEBUG_CACHE) {
Log.d(TAG, "---- clearing cache ----------");
}
if (mDevicesForStreamCache != null) {
synchronized (mDevicesForStreamCache) {
mDevicesForStreamCache.clear();
}
}
if (mDevicesForAttrCache != null) {
synchronized (mDevicesForAttrCache) {
mDevicesForAttrCache.clear();
@@ -143,60 +134,6 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
}
}
/**
* Same as {@link AudioSystem#getDevicesForStream(int)}
* @param stream a valid stream type
* @return a mask of device types
*/
public int getDevicesForStream(int stream) {
if (!ENABLE_GETDEVICES_STATS) {
return getDevicesForStreamImpl(stream);
}
mMethodCallCounter[METHOD_GETDEVICESFORSTREAM]++;
final long startTime = SystemClock.uptimeNanos();
final int res = getDevicesForStreamImpl(stream);
mMethodTimeNs[METHOD_GETDEVICESFORSTREAM] += SystemClock.uptimeNanos() - startTime;
return res;
}
private int getDevicesForStreamImpl(int stream) {
if (USE_CACHE_FOR_GETDEVICES) {
Integer res;
synchronized (mDevicesForStreamCache) {
res = mDevicesForStreamCache.get(stream);
if (res == null) {
res = AudioSystem.getDevicesForStream(stream);
mDevicesForStreamCache.put(stream, res);
if (DEBUG_CACHE) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
+ streamDeviceToDebugString(stream, res));
}
return res;
}
// cache hit
mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]++;
if (DEBUG_CACHE) {
final int real = AudioSystem.getDevicesForStream(stream);
if (res == real) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
+ streamDeviceToDebugString(stream, res) + " CACHE");
} else {
Log.e(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM]
+ streamDeviceToDebugString(stream, res)
+ " CACHE ERROR real dev=0x" + Integer.toHexString(real));
}
}
}
return res;
}
// not using cache
return AudioSystem.getDevicesForStream(stream);
}
private static String streamDeviceToDebugString(int stream, int dev) {
return " stream=" + stream + " dev=0x" + Integer.toHexString(dev);
}
/**
* Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)}
* @param attributes the attributes for which the routing is queried
@@ -253,12 +190,9 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
}
private static String attrDeviceToDebugString(@NonNull AudioAttributes attr,
@NonNull ArrayList<AudioDeviceAttributes> devices) {
String ds = " attrUsage=" + attr.getSystemUsage();
for (AudioDeviceAttributes ada : devices) {
ds = ds.concat(" dev=0x" + Integer.toHexString(ada.getInternalType()));
}
return ds;
@NonNull List<AudioDeviceAttributes> devices) {
return " attrUsage=" + attr.getSystemUsage() + " "
+ AudioSystem.deviceSetToString(AudioSystem.generateAudioDeviceTypesSet(devices));
}
/**
@@ -518,20 +452,23 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
*/
public void dump(PrintWriter pw) {
pw.println("\nAudioSystemAdapter:");
pw.println(" mDevicesForStreamCache:");
if (mDevicesForStreamCache != null) {
for (Integer stream : mDevicesForStreamCache.keySet()) {
pw.println("\t stream:" + stream + " device:"
+ AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream)));
}
}
pw.println(" mDevicesForAttrCache:");
if (mDevicesForAttrCache != null) {
for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
entry : mDevicesForAttrCache.entrySet()) {
pw.println("\t" + entry.getKey().first + " forVolume: " + entry.getKey().second);
for (AudioDeviceAttributes devAttr : entry.getValue()) {
pw.println("\t\t" + devAttr);
final AudioAttributes attributes = entry.getKey().first;
try {
final int stream = attributes.getVolumeControlStream();
pw.println("\t" + attributes + " forVolume: " + entry.getKey().second
+ " stream: "
+ AudioSystem.STREAM_NAMES[stream] + "(" + stream + ")");
for (AudioDeviceAttributes devAttr : entry.getValue()) {
pw.println("\t\t" + devAttr);
}
} catch (IllegalArgumentException e) {
// dump could fail if attributes do not map to a stream.
pw.println("\t dump failed for attributes: " + attributes);
Log.e(TAG, "dump failed", e);
}
}
}
@@ -545,7 +482,8 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
+ ": counter=" + mMethodCallCounter[i]
+ " time(ms)=" + (mMethodTimeNs[i] / 1E6)
+ (USE_CACHE_FOR_GETDEVICES
? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]) : ""));
? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES])
: ""));
}
pw.println("\n");
}