Merge "Support set/clear/get preferred device for capture preset."

This commit is contained in:
Eric Laurent
2020-12-23 08:34:27 +00:00
committed by Gerrit Code Review
14 changed files with 964 additions and 2 deletions

View File

@@ -4146,8 +4146,10 @@ package android.media {
public class AudioManager { public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
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 @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 addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method public void clearAudioServerStateCallback(); 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); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4157,6 +4159,7 @@ package android.media {
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 getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@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 getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); 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); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -4165,6 +4168,7 @@ package android.media {
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 int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); 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 @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);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
@@ -4173,6 +4177,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
@@ -4197,6 +4202,10 @@ package android.media {
method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes); method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
} }
public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener { public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>); method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
} }

View File

@@ -2408,6 +2408,79 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env,
return AUDIO_JAVA_SUCCESS; return AUDIO_JAVA_SUCCESS;
} }
static jint android_media_AudioSystem_setDevicesRoleForCapturePreset(
JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
jobjectArray jDeviceAddresses) {
AudioDeviceTypeAddrVector nDevices;
jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
if (results != NO_ERROR) {
return results;
}
int status = check_AudioSystem_Command(
AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset,
(device_role_t)role, nDevices));
return (jint)status;
}
static jint android_media_AudioSystem_addDevicesRoleForCapturePreset(
JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
jobjectArray jDeviceAddresses) {
AudioDeviceTypeAddrVector nDevices;
jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
if (results != NO_ERROR) {
return results;
}
int status = check_AudioSystem_Command(
AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset,
(device_role_t)role, nDevices));
return (jint)status;
}
static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset(
JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
jobjectArray jDeviceAddresses) {
AudioDeviceTypeAddrVector nDevices;
jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
if (results != NO_ERROR) {
return results;
}
int status = check_AudioSystem_Command(
AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset,
(device_role_t)role, nDevices));
return (jint)status;
}
static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz,
jint capturePreset,
jint role) {
return (jint)check_AudioSystem_Command(
AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset,
(device_role_t)role));
}
static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz,
jint capturePreset,
jint role,
jobject jDevices) {
AudioDeviceTypeAddrVector nDevices;
status_t status = check_AudioSystem_Command(
AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset,
(device_role_t)role, nDevices));
if (status != NO_ERROR) {
return (jint)status;
}
for (const auto &device : nDevices) {
jobject jAudioDeviceAttributes = NULL;
jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
if (jStatus != AUDIO_JAVA_SUCCESS) {
return jStatus;
}
env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
env->DeleteLocalRef(jAudioDeviceAttributes);
}
return AUDIO_JAVA_SUCCESS;
}
static jint static jint
android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz, android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
jobject jaa, jobjectArray jDeviceArray) jobject jaa, jobjectArray jDeviceArray)
@@ -2559,6 +2632,16 @@ static const JNINativeMethod gMethods[] =
(void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
{"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
(void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
{"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
(void *)android_media_AudioSystem_setDevicesRoleForCapturePreset},
{"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
(void *)android_media_AudioSystem_addDevicesRoleForCapturePreset},
{"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
(void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset},
{"clearDevicesRoleForCapturePreset", "(II)I",
(void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset},
{"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
(void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
{"getDevicesForAttributes", {"getDevicesForAttributes",
"(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
(void *)android_media_AudioSystem_getDevicesForAttributes}, (void *)android_media_AudioSystem_getDevicesForAttributes},

View File

@@ -71,6 +71,11 @@ public final class AudioDeviceAttributes implements Parcelable {
*/ */
private final @Role int mRole; private final @Role int mRole;
/**
* The internal audio device type
*/
private final int mNativeType;
/** /**
* @hide * @hide
* Constructor from a valid {@link AudioDeviceInfo} * Constructor from a valid {@link AudioDeviceInfo}
@@ -83,6 +88,7 @@ public final class AudioDeviceAttributes implements Parcelable {
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT; mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType(); mType = deviceInfo.getType();
mAddress = deviceInfo.getAddress(); mAddress = deviceInfo.getAddress();
mNativeType = deviceInfo.getInternalType();
} }
/** /**
@@ -101,9 +107,12 @@ public final class AudioDeviceAttributes implements Parcelable {
} }
if (role == ROLE_OUTPUT) { if (role == ROLE_OUTPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type); AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
} mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
if (role == ROLE_INPUT) { } else if (role == ROLE_INPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type); AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
} else {
mNativeType = AudioSystem.DEVICE_NONE;
} }
mRole = role; mRole = role;
@@ -115,6 +124,7 @@ public final class AudioDeviceAttributes implements Parcelable {
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
mAddress = address; mAddress = address;
mNativeType = nativeType;
} }
/** /**
@@ -147,6 +157,15 @@ public final class AudioDeviceAttributes implements Parcelable {
return mAddress; return mAddress;
} }
/**
* @hide
* Returns the internal device type of a device
* @return the internal device type
*/
public int getInternalType() {
return mNativeType;
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(mRole, mType, mAddress); return Objects.hash(mRole, mType, mAddress);
@@ -189,12 +208,14 @@ public final class AudioDeviceAttributes implements Parcelable {
dest.writeInt(mRole); dest.writeInt(mRole);
dest.writeInt(mType); dest.writeInt(mType);
dest.writeString(mAddress); dest.writeString(mAddress);
dest.writeInt(mNativeType);
} }
private AudioDeviceAttributes(@NonNull Parcel in) { private AudioDeviceAttributes(@NonNull Parcel in) {
mRole = in.readInt(); mRole = in.readInt();
mType = in.readInt(); mType = in.readInt();
mAddress = in.readString(); mAddress = in.readString();
mNativeType = in.readInt();
} }
public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR = public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =

View File

@@ -349,6 +349,14 @@ public final class AudioDeviceInfo {
return mPort; return mPort;
} }
/**
* @hide
* @return the internal device tyoe
*/
public int getInternalType() {
return mPort.type();
}
/** /**
* @return The internal device ID. * @return The internal device ID.
*/ */
@@ -512,10 +520,21 @@ public final class AudioDeviceInfo {
return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN); return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN);
} }
/** @hide */
public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
}
private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING; private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING; private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
/**
* EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device
* type.
*/
private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING;
static { static {
INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray(); INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE);
@@ -600,6 +619,32 @@ public final class AudioDeviceInfo {
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX); EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET); EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER); EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
// privileges mapping to input device
EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
} }
} }

View File

@@ -1947,6 +1947,349 @@ public class AudioManager {
return false; return false;
} }
//====================================================================
// Audio Capture Preset routing
/**
* @hide
* Set the preferred device for a given capture preset, i.e. the audio routing to be used by
* this capture preset. Note that the device may not be available at the time the preferred
* device is set, but it will be used once made available.
* <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
* for this capture preset.</p>
* @param capturePreset the audio capture preset whose routing will be affected
* @param device the audio device to route to when available
* @return true if the operation was successful, false otherwise
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForCapturePreset(int capturePreset,
@NonNull AudioDeviceAttributes device) {
return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
}
/**
* @hide
* Remove all the preferred audio devices previously set
* @param capturePreset the audio capture preset whose routing will be affected
* @return true if the operation was successful, false otherwise (invalid capture preset, or no
* device set for example)
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return false;
}
try {
final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
return status == AudioSystem.SUCCESS;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
* Return the preferred devices for an audio capture preset, previously set with
* {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
* @param capturePreset the capture preset to query
* @return a list that contains preferred devices for that capture preset.
*/
@NonNull
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return new ArrayList<AudioDeviceAttributes>();
}
try {
return getService().getPreferredDevicesForCapturePreset(capturePreset);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private boolean setPreferredDevicesForCapturePreset(
int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
Objects.requireNonNull(devices);
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return false;
}
if (devices.size() != 1) {
throw new IllegalArgumentException(
"Only support setting one preferred devices for capture preset");
}
for (AudioDeviceAttributes device : devices) {
Objects.requireNonNull(device);
}
try {
final int status =
getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
return status == AudioSystem.SUCCESS;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @hide
* Interface to be notified of changes in the preferred audio devices set for a given capture
* preset.
* <p>Note that this listener will only be invoked whenever
* {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
* {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
* preferred device. It will not be invoked directly after registration with
* {@link #addOnPreferredDevicesForCapturePresetChangedListener(
* Executor, OnPreferredDevicesForCapturePresetChangedListener)}
* to indicate which strategies had preferred devices at the time of registration.</p>
* @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
* @see #clearPreferredDevicesForCapturePreset(int)
* @see #getPreferredDevicesForCapturePreset(int)
*/
@SystemApi
public interface OnPreferredDevicesForCapturePresetChangedListener {
/**
* Called on the listener to indicate that the preferred audio devices for the given
* capture preset has changed.
* @param capturePreset the capture preset whose preferred device changed
* @param devices a list of newly set preferred audio devices
*/
void onPreferredDevicesForCapturePresetChanged(
int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
}
/**
* @hide
* Adds a listener for being notified of changes to the capture-preset-preferred audio device.
* @param executor
* @param listener
* @throws SecurityException if the caller doesn't hold the required permission
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void addOnPreferredDevicesForCapturePresetChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
throws SecurityException {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
int status = addOnDevRoleForCapturePresetChangedListener(
executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
if (status == AudioSystem.ERROR) {
// This must not happen
throw new RuntimeException("Unknown error happened");
}
if (status == AudioSystem.BAD_VALUE) {
throw new IllegalArgumentException(
"attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
+ "on a previously registered listener");
}
}
/**
* @hide
* Removes a previously added listener of changes to the capture-preset-preferred audio device.
* @param listener
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void removeOnPreferredDevicesForCapturePresetChangedListener(
@NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
Objects.requireNonNull(listener);
int status = removeOnDevRoleForCapturePresetChangedListener(
listener, AudioSystem.DEVICE_ROLE_PREFERRED);
if (status == AudioSystem.ERROR) {
// This must not happen
throw new RuntimeException("Unknown error happened");
}
if (status == AudioSystem.BAD_VALUE) {
throw new IllegalArgumentException(
"attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
+ "on an unregistered listener");
}
}
private <T> int addOnDevRoleForCapturePresetChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull T listener, int deviceRole) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
DevRoleListeners<T> devRoleListeners =
(DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
if (devRoleListeners == null) {
return AudioSystem.ERROR;
}
synchronized (devRoleListeners.mDevRoleListenersLock) {
if (devRoleListeners.hasDevRoleListener(listener)) {
return AudioSystem.BAD_VALUE;
}
// lazy initialization of the list of device role listener
if (devRoleListeners.mListenerInfos == null) {
devRoleListeners.mListenerInfos = new ArrayList<>();
}
final int oldCbCount = devRoleListeners.mListenerInfos.size();
devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
// register binder for callbacks
synchronized (mDevRoleForCapturePresetListenersLock) {
int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
mDeviceRoleListenersStatus |= (1 << deviceRole);
if (deviceRoleListenerStatus != 0) {
// There are already device role changed listeners active.
return AudioSystem.SUCCESS;
}
if (mDevicesRoleForCapturePresetDispatcherStub == null) {
mDevicesRoleForCapturePresetDispatcherStub =
new CapturePresetDevicesRoleDispatcherStub();
}
try {
getService().registerCapturePresetDevicesRoleDispatcher(
mDevicesRoleForCapturePresetDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
return AudioSystem.SUCCESS;
}
private <T> int removeOnDevRoleForCapturePresetChangedListener(
@NonNull T listener, int deviceRole) {
Objects.requireNonNull(listener);
DevRoleListeners<T> devRoleListeners =
(DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
if (devRoleListeners == null) {
return AudioSystem.ERROR;
}
synchronized (devRoleListeners.mDevRoleListenersLock) {
if (!devRoleListeners.removeDevRoleListener(listener)) {
return AudioSystem.BAD_VALUE;
}
if (devRoleListeners.mListenerInfos.size() == 0) {
// unregister binder for callbacks
synchronized (mDevRoleForCapturePresetListenersLock) {
mDeviceRoleListenersStatus ^= (1 << deviceRole);
if (mDeviceRoleListenersStatus != 0) {
// There are some other device role changed listeners active.
return AudioSystem.SUCCESS;
}
try {
getService().unregisterCapturePresetDevicesRoleDispatcher(
mDevicesRoleForCapturePresetDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
return AudioSystem.SUCCESS;
}
private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{
put(AudioSystem.DEVICE_ROLE_PREFERRED,
new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
}};
private class DevRoleListenerInfo<T> {
final @NonNull Executor mExecutor;
final @NonNull T mListener;
DevRoleListenerInfo(Executor executor, T listener) {
mExecutor = executor;
mListener = listener;
}
}
private class DevRoleListeners<T> {
private final Object mDevRoleListenersLock = new Object();
@GuardedBy("mDevRoleListenersLock")
private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
@GuardedBy("mDevRoleListenersLock")
private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
if (mListenerInfos == null) {
return null;
}
for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
if (listenerInfo.mListener == listener) {
return listenerInfo;
}
}
return null;
}
@GuardedBy("mDevRoleListenersLock")
private boolean hasDevRoleListener(T listener) {
return getDevRoleListenerInfo(listener) != null;
}
@GuardedBy("mDevRoleListenersLock")
private boolean removeDevRoleListener(T listener) {
final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
if (infoToRemove != null) {
mListenerInfos.remove(infoToRemove);
return true;
}
return false;
}
}
private final Object mDevRoleForCapturePresetListenersLock = new Object();
/**
* Record if there is a listener added for device role change. If there is a listener added for
* a specified device role change, the bit at position `1 << device_role` is set.
*/
@GuardedBy("mDevRoleForCapturePresetListenersLock")
private int mDeviceRoleListenersStatus = 0;
@GuardedBy("mDevRoleForCapturePresetListenersLock")
private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
private final class CapturePresetDevicesRoleDispatcherStub
extends ICapturePresetDevicesRoleDispatcher.Stub {
@Override
public void dispatchDevicesRoleChanged(
int capturePreset, int role, List<AudioDeviceAttributes> devices) {
final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
if (listenersObj == null) {
return;
}
switch (role) {
case AudioSystem.DEVICE_ROLE_PREFERRED: {
final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
listeners =
(DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
listenersObj;
final ArrayList<DevRoleListenerInfo<
OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
synchronized (listeners.mDevRoleListenersLock) {
if (listeners.mListenerInfos.isEmpty()) {
return;
}
prefDevListeners = (ArrayList<DevRoleListenerInfo<
OnPreferredDevicesForCapturePresetChangedListener>>)
listeners.mListenerInfos.clone();
}
final long ident = Binder.clearCallingIdentity();
try {
for (DevRoleListenerInfo<
OnPreferredDevicesForCapturePresetChangedListener> info :
prefDevListeners) {
info.mExecutor.execute(() ->
info.mListener.onPreferredDevicesForCapturePresetChanged(
capturePreset, devices));
}
} finally {
Binder.restoreCallingIdentity(ident);
}
} break;
default:
break;
}
}
}
//==================================================================== //====================================================================
// Offload query // Offload query
/** /**

View File

@@ -28,6 +28,7 @@ import android.media.audiopolicy.AudioMix;
import android.os.Build; import android.os.Build;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -1750,6 +1751,134 @@ public class AudioSystem
public static native int getDevicesForRoleAndStrategy( public static native int getDevicesForRoleAndStrategy(
int strategy, int role, @NonNull List<AudioDeviceAttributes> devices); int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
// use case routing by capture preset
private static Pair<int[], String[]> populateInputDevicesTypeAndAddress(
@NonNull List<AudioDeviceAttributes> devices) {
int[] types = new int[devices.size()];
String[] addresses = new String[devices.size()];
for (int i = 0; i < devices.size(); ++i) {
types[i] = devices.get(i).getInternalType();
if (types[i] == AudioSystem.DEVICE_NONE) {
types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
devices.get(i).getType());
}
addresses[i] = devices.get(i).getAddress();
}
return new Pair<int[], String[]>(types, addresses);
}
/**
* @hide
* Set devices as role for capture preset.
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param devices the list of devices to be set as role for the given capture preset
* @return {@link #SUCCESS} if successfully set
*/
public static int setDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
if (devices.isEmpty()) {
return BAD_VALUE;
}
Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
return setDevicesRoleForCapturePreset(
capturePreset, role, typeAddresses.first, typeAddresses.second);
}
/**
* @hide
* Set devices as role for capture preset.
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param types all device types
* @param addresses all device addresses
* @return {@link #SUCCESS} if successfully set
*/
private static native int setDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
/**
* @hide
* Add devices as role for capture preset.
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param devices the list of devices to be added as role for the given capture preset
* @return {@link #SUCCESS} if successfully add
*/
public static int addDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
if (devices.isEmpty()) {
return BAD_VALUE;
}
Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
return addDevicesRoleForCapturePreset(
capturePreset, role, typeAddresses.first, typeAddresses.second);
}
/**
* @hide
* Add devices as role for capture preset.
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param types all device types
* @param addresses all device addresses
* @return {@link #SUCCESS} if successfully set
*/
private static native int addDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
/**
* @hide
* Remove devices as role for the capture preset
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param devices the devices to be removed
* @return {@link #SUCCESS} if successfully removed
*/
public static int removeDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
if (devices.isEmpty()) {
return BAD_VALUE;
}
Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
return removeDevicesRoleForCapturePreset(
capturePreset, role, typeAddresses.first, typeAddresses.second);
}
/**
* @hide
* Remove devices as role for capture preset.
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @param types all device types
* @param addresses all device addresses
* @return {@link #SUCCESS} if successfully set
*/
private static native int removeDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
/**
* @hide
* Remove all devices as role for the capture preset
* @param capturePreset the capture preset to configure
* @param role the role of the devices
* @return {@link #SUCCESS} if successfully removed
*/
public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role);
/**
* @hide
* Query previously set devices as role for a capture preset
* @param capturePreset the capture preset to query for
* @param role the role of the devices
* @param devices a list that will contain the devices of role
* @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
* and written to the array
*/
public static native int getDevicesForRoleAndCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices);
// Items shared with audio service // Items shared with audio service
/** /**

View File

@@ -26,6 +26,7 @@ import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher; import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver; import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher; import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IPlaybackConfigDispatcher; import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher; import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer; import android.media.IRingtonePlayer;
@@ -307,4 +308,16 @@ interface IAudioService {
// code via IAudioManager.h need to be added to the top section. // code via IAudioManager.h need to be added to the top section.
oneway void setMultiAudioFocusEnabled(in boolean enabled); oneway void setMultiAudioFocusEnabled(in boolean enabled);
int setPreferredDevicesForCapturePreset(
in int capturePreset, in List<AudioDeviceAttributes> devices);
int clearPreferredDevicesForCapturePreset(in int capturePreset);
List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset);
void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher);
oneway void unregisterCapturePresetDevicesRoleDispatcher(
ICapturePresetDevicesRoleDispatcher dispatcher);
} }

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2020 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 AudioService to signal devices role for capture preset updates.
*
* {@hide}
*/
oneway interface ICapturePresetDevicesRoleDispatcher {
void dispatchDevicesRoleChanged(
int capturePreset, int role, in List<AudioDeviceAttributes> devices);
}

View File

@@ -404,6 +404,32 @@ public class MediaRecorder implements AudioRouting,
} }
} }
/**
* @hide
* @param source An audio source to test
* @return true if the source is a valid one
*/
public static boolean isValidAudioSource(int source) {
switch(source) {
case AudioSource.MIC:
case AudioSource.VOICE_UPLINK:
case AudioSource.VOICE_DOWNLINK:
case AudioSource.VOICE_CALL:
case AudioSource.CAMCORDER:
case AudioSource.VOICE_RECOGNITION:
case AudioSource.VOICE_COMMUNICATION:
case AudioSource.REMOTE_SUBMIX:
case AudioSource.UNPROCESSED:
case AudioSource.VOICE_PERFORMANCE:
case AudioSource.ECHO_REFERENCE:
case AudioSource.RADIO_TUNER:
case AudioSource.HOTWORD:
return true;
default:
return false;
}
}
/** @hide */ /** @hide */
public static final String toLogFriendlyAudioSource(int source) { public static final String toLogFriendlyAudioSource(int source) {
switch(source) { switch(source) {

View File

@@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioRoutesInfo; import android.media.AudioRoutesInfo;
import android.media.AudioSystem; import android.media.AudioSystem;
import android.media.IAudioRoutesObserver; import android.media.IAudioRoutesObserver;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics; import android.media.MediaMetrics;
import android.os.Binder; import android.os.Binder;
@@ -546,6 +547,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher); mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
} }
/*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset,
@NonNull List<AudioDeviceAttributes> devices) {
return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices);
}
/*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset);
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// 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
@@ -694,6 +714,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy); sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
} }
/*package*/ void postSaveSetPreferredDevicesForCapturePreset(
int capturePreset, List<AudioDeviceAttributes> devices) {
sendILMsgNoDelay(
MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
}
/*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
sendIMsgNoDelay(
MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method // only call from a "handle"* method or "on"* method
@@ -1098,6 +1129,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_CHECK_MUTE_MUSIC: case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0); checkMessagesMuteMusic(0);
break; break;
case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
final int capturePreset = msg.arg1;
final List<AudioDeviceAttributes> devices =
(List<AudioDeviceAttributes>) msg.obj;
mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
capturePreset, devices);
} break;
case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
final int capturePreset = msg.arg1;
mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
} break;
default: default:
Log.wtf(TAG, "Invalid message " + msg.what); Log.wtf(TAG, "Invalid message " + msg.what);
} }
@@ -1174,6 +1216,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_CHECK_MUTE_MUSIC = 36; private static final int MSG_CHECK_MUTE_MUSIC = 36;
private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37; private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
private static boolean isMessageHandledUnderWakelock(int msgId) { private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) { switch(msgId) {

View File

@@ -31,6 +31,7 @@ import android.media.AudioPort;
import android.media.AudioRoutesInfo; import android.media.AudioRoutesInfo;
import android.media.AudioSystem; import android.media.AudioSystem;
import android.media.IAudioRoutesObserver; import android.media.IAudioRoutesObserver;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher; import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics; import android.media.MediaMetrics;
import android.os.Binder; import android.os.Binder;
@@ -140,6 +141,10 @@ public class AudioDeviceInventory {
private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices = private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
new ArrayMap<>(); new ArrayMap<>();
// List of preferred devices of capture preset
private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
new ArrayMap<>();
// the wrapper for AudioSystem static methods, allows us to spy AudioSystem // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
private final @NonNull AudioSystemAdapter mAudioSystem; private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -154,6 +159,10 @@ public class AudioDeviceInventory {
final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers = final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>(); new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
// Monitoring of devices for role and capture preset
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker; mDeviceBroker = broker;
mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
@@ -243,6 +252,9 @@ public class AudioDeviceInventory {
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType) + " (" + AudioSystem.getDeviceName(keyType)
+ ") addr:" + valueAddress); }); + ") addr:" + valueAddress); });
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
pw.println(" " + prefix + "capturePreset:" + capturePreset
+ " devices:" + devices); });
} }
//------------------------------------------------------------ //------------------------------------------------------------
@@ -270,6 +282,9 @@ public class AudioDeviceInventory {
mAudioSystem.setDevicesRoleForStrategy( mAudioSystem.setDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); }); strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
} }
synchronized (mPreferredDevicesForCapturePreset) {
// TODO: call audiosystem to restore
}
} }
// only public for mocking/spying // only public for mocking/spying
@@ -613,6 +628,20 @@ public class AudioDeviceInventory {
dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>()); dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
} }
/*package*/ void onSaveSetPreferredDevicesForCapturePreset(
int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
mPreferredDevicesForCapturePreset.put(capturePreset, devices);
dispatchDevicesRoleForCapturePreset(
capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
}
/*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
mPreferredDevicesForCapturePreset.remove(capturePreset);
dispatchDevicesRoleForCapturePreset(
capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
new ArrayList<AudioDeviceAttributes>());
}
//------------------------------------------------------------ //------------------------------------------------------------
// //
@@ -651,6 +680,41 @@ public class AudioDeviceInventory {
mPrefDevDispatchers.unregister(dispatcher); mPrefDevDispatchers.unregister(dispatcher);
} }
/*package*/ int setPreferredDevicesForCapturePresetSync(
int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
final long identity = Binder.clearCallingIdentity();
final int status = mAudioSystem.setDevicesRoleForCapturePreset(
capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
}
return status;
}
/*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
final long identity = Binder.clearCallingIdentity();
final int status = mAudioSystem.clearDevicesRoleForCapturePreset(
capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
}
return status;
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
mDevRoleCapturePresetDispatchers.register(dispatcher);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
mDevRoleCapturePresetDispatchers.unregister(dispatcher);
}
/** /**
* Implements the communication with AudioSystem to (dis)connect a device in the native layers * Implements the communication with AudioSystem to (dis)connect a device in the native layers
* @param connect true if connection * @param connect true if connection
@@ -1306,6 +1370,19 @@ public class AudioDeviceInventory {
mPrefDevDispatchers.finishBroadcast(); mPrefDevDispatchers.finishBroadcast();
} }
private void dispatchDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; ++i) {
try {
mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
capturePreset, role, devices);
} catch (RemoteException e) {
}
}
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
//---------------------------------------------------------- //----------------------------------------------------------
// For tests only // For tests only

View File

@@ -84,6 +84,7 @@ import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver; import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher; import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService; import android.media.IAudioService;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.IPlaybackConfigDispatcher; import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher; import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer; import android.media.IRingtonePlayer;
@@ -1938,6 +1939,94 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher); mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
} }
/**
* @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
*/
public int setPreferredDevicesForCapturePreset(
int capturePreset, List<AudioDeviceAttributes> devices) {
if (devices == null) {
return AudioSystem.ERROR;
}
enforceModifyAudioRoutingPermission();
final String logString = String.format(
"setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
if (devices.stream().anyMatch(device ->
device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
Log.e(TAG, "Unsupported output routing in " + logString);
return AudioSystem.ERROR;
}
final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
capturePreset, devices);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
return status;
}
/** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
public int clearPreferredDevicesForCapturePreset(int capturePreset) {
enforceModifyAudioRoutingPermission();
final String logString = String.format(
"removePreferredDeviceForCapturePreset source:%d", capturePreset);
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s", status, logString));
}
return status;
}
/**
* @see AudioManager#getPreferredDevicesForCapturePreset(int)
*/
public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
enforceModifyAudioRoutingPermission();
List<AudioDeviceAttributes> devices = new ArrayList<>();
final long identity = Binder.clearCallingIdentity();
final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
status, capturePreset));
return new ArrayList<AudioDeviceAttributes>();
} else {
return devices;
}
}
/**
* @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener(
* Executor, OnPreferredDevicesForCapturePresetChangedListener)
*/
public void registerCapturePresetDevicesRoleDispatcher(
@Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
}
/**
* @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener(
* AudioManager.OnPreferredDevicesForCapturePresetChangedListener)
*/
public void unregisterCapturePresetDevicesRoleDispatcher(
@Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
}
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) { @NonNull AudioAttributes attributes) {

View File

@@ -100,6 +100,40 @@ public class AudioSystemAdapter {
return AudioSystem.removeDevicesRoleForStrategy(strategy, role); return AudioSystem.removeDevicesRoleForStrategy(strategy, role);
} }
/**
* Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List))
* @param capturePreset
* @param role
* @param devices
* @return
*/
public int setDevicesRoleForCapturePreset(int capturePreset, int role,
@NonNull List<AudioDeviceAttributes> devices) {
return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices);
}
/**
* Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)}
* @param capturePreset
* @param role
* @param devicesToRemove
* @return
*/
public int removeDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove);
}
/**
* Same as {@link AudioSystem#}
* @param capturePreset
* @param role
* @return
*/
public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role);
}
/** /**
* Same as {@link AudioSystem#setParameters(String)} * Same as {@link AudioSystem#setParameters(String)}
* @param keyValuePairs * @param keyValuePairs

View File

@@ -78,6 +78,23 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
return AudioSystem.AUDIO_STATUS_OK; return AudioSystem.AUDIO_STATUS_OK;
} }
@Override
public int setDevicesRoleForCapturePreset(int capturePreset, int role,
@NonNull List<AudioDeviceAttributes> devices) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override
public int removeDevicesRoleForCapturePreset(
int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override
public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override @Override
public int setParameters(String keyValuePairs) { public int setParameters(String keyValuePairs) {
return AudioSystem.AUDIO_STATUS_OK; return AudioSystem.AUDIO_STATUS_OK;