Merge "Support set/clear/get preferred device for capture preset."
This commit is contained in:
@@ -4146,8 +4146,10 @@ package android.media {
|
||||
public class AudioManager {
|
||||
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 @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 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 @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();
|
||||
@@ -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 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 @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 int[] getSupportedSystemUsages();
|
||||
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 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 @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 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;
|
||||
@@ -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 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 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 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[]);
|
||||
@@ -4197,6 +4202,10 @@ package android.media {
|
||||
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 {
|
||||
method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
|
||||
}
|
||||
|
||||
@@ -2408,6 +2408,79 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env,
|
||||
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
|
||||
android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
|
||||
jobject jaa, jobjectArray jDeviceArray)
|
||||
@@ -2559,6 +2632,16 @@ static const JNINativeMethod gMethods[] =
|
||||
(void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
|
||||
{"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
|
||||
(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",
|
||||
"(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
|
||||
(void *)android_media_AudioSystem_getDevicesForAttributes},
|
||||
|
||||
@@ -71,6 +71,11 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
*/
|
||||
private final @Role int mRole;
|
||||
|
||||
/**
|
||||
* The internal audio device type
|
||||
*/
|
||||
private final int mNativeType;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constructor from a valid {@link AudioDeviceInfo}
|
||||
@@ -83,6 +88,7 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
|
||||
mType = deviceInfo.getType();
|
||||
mAddress = deviceInfo.getAddress();
|
||||
mNativeType = deviceInfo.getInternalType();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,9 +107,12 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
}
|
||||
if (role == ROLE_OUTPUT) {
|
||||
AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
|
||||
}
|
||||
if (role == ROLE_INPUT) {
|
||||
mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
|
||||
} else if (role == ROLE_INPUT) {
|
||||
AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
|
||||
mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
|
||||
} else {
|
||||
mNativeType = AudioSystem.DEVICE_NONE;
|
||||
}
|
||||
|
||||
mRole = role;
|
||||
@@ -115,6 +124,7 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
|
||||
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
|
||||
mAddress = address;
|
||||
mNativeType = nativeType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +157,15 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
return mAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Returns the internal device type of a device
|
||||
* @return the internal device type
|
||||
*/
|
||||
public int getInternalType() {
|
||||
return mNativeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mRole, mType, mAddress);
|
||||
@@ -189,12 +208,14 @@ public final class AudioDeviceAttributes implements Parcelable {
|
||||
dest.writeInt(mRole);
|
||||
dest.writeInt(mType);
|
||||
dest.writeString(mAddress);
|
||||
dest.writeInt(mNativeType);
|
||||
}
|
||||
|
||||
private AudioDeviceAttributes(@NonNull Parcel in) {
|
||||
mRole = in.readInt();
|
||||
mType = in.readInt();
|
||||
mAddress = in.readString();
|
||||
mNativeType = in.readInt();
|
||||
}
|
||||
|
||||
public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
|
||||
|
||||
@@ -349,6 +349,14 @@ public final class AudioDeviceInfo {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @return the internal device tyoe
|
||||
*/
|
||||
public int getInternalType() {
|
||||
return mPort.type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The internal device ID.
|
||||
*/
|
||||
@@ -512,10 +520,21 @@ public final class AudioDeviceInfo {
|
||||
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 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 {
|
||||
INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
|
||||
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_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1947,6 +1947,349 @@ public class AudioManager {
|
||||
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
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.media.audiopolicy.AudioMix;
|
||||
import android.os.Build;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -1750,6 +1751,134 @@ public class AudioSystem
|
||||
public static native int getDevicesForRoleAndStrategy(
|
||||
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
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.media.AudioRoutesInfo;
|
||||
import android.media.IAudioFocusDispatcher;
|
||||
import android.media.IAudioRoutesObserver;
|
||||
import android.media.IAudioServerStateDispatcher;
|
||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||
import android.media.IPlaybackConfigDispatcher;
|
||||
import android.media.IRecordingConfigDispatcher;
|
||||
import android.media.IRingtonePlayer;
|
||||
@@ -307,4 +308,16 @@ interface IAudioService {
|
||||
// code via IAudioManager.h need to be added to the top section.
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
public static final String toLogFriendlyAudioSource(int source) {
|
||||
switch(source) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioRoutesInfo;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.IAudioRoutesObserver;
|
||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||
import android.media.MediaMetrics;
|
||||
import android.os.Binder;
|
||||
@@ -546,6 +547,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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
|
||||
//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);
|
||||
}
|
||||
|
||||
/*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)
|
||||
// only call from a "handle"* method or "on"* method
|
||||
@@ -1098,6 +1129,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
case MSG_CHECK_MUTE_MUSIC:
|
||||
checkMessagesMuteMusic(0);
|
||||
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:
|
||||
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_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) {
|
||||
switch(msgId) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.media.AudioPort;
|
||||
import android.media.AudioRoutesInfo;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.IAudioRoutesObserver;
|
||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||
import android.media.MediaMetrics;
|
||||
import android.os.Binder;
|
||||
@@ -140,6 +141,10 @@ public class AudioDeviceInventory {
|
||||
private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
|
||||
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
|
||||
private final @NonNull AudioSystemAdapter mAudioSystem;
|
||||
|
||||
@@ -154,6 +159,10 @@ public class AudioDeviceInventory {
|
||||
final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
|
||||
new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
|
||||
|
||||
// Monitoring of devices for role and capture preset
|
||||
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
|
||||
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
|
||||
|
||||
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
|
||||
mDeviceBroker = broker;
|
||||
mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
|
||||
@@ -243,6 +252,9 @@ public class AudioDeviceInventory {
|
||||
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
|
||||
+ " (" + AudioSystem.getDeviceName(keyType)
|
||||
+ ") addr:" + valueAddress); });
|
||||
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
|
||||
pw.println(" " + prefix + "capturePreset:" + capturePreset
|
||||
+ " devices:" + devices); });
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
@@ -270,6 +282,9 @@ public class AudioDeviceInventory {
|
||||
mAudioSystem.setDevicesRoleForStrategy(
|
||||
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
|
||||
}
|
||||
synchronized (mPreferredDevicesForCapturePreset) {
|
||||
// TODO: call audiosystem to restore
|
||||
}
|
||||
}
|
||||
|
||||
// only public for mocking/spying
|
||||
@@ -613,6 +628,20 @@ public class AudioDeviceInventory {
|
||||
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);
|
||||
}
|
||||
|
||||
/*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
|
||||
* @param connect true if connection
|
||||
@@ -1306,6 +1370,19 @@ public class AudioDeviceInventory {
|
||||
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
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ import android.media.IAudioFocusDispatcher;
|
||||
import android.media.IAudioRoutesObserver;
|
||||
import android.media.IAudioServerStateDispatcher;
|
||||
import android.media.IAudioService;
|
||||
import android.media.ICapturePresetDevicesRoleDispatcher;
|
||||
import android.media.IPlaybackConfigDispatcher;
|
||||
import android.media.IRecordingConfigDispatcher;
|
||||
import android.media.IRingtonePlayer;
|
||||
@@ -1938,6 +1939,94 @@ public class AudioService extends IAudioService.Stub
|
||||
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) */
|
||||
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
|
||||
@NonNull AudioAttributes attributes) {
|
||||
|
||||
@@ -100,6 +100,40 @@ public class AudioSystemAdapter {
|
||||
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)}
|
||||
* @param keyValuePairs
|
||||
|
||||
@@ -78,6 +78,23 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
|
||||
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
|
||||
public int setParameters(String keyValuePairs) {
|
||||
return AudioSystem.AUDIO_STATUS_OK;
|
||||
|
||||
Reference in New Issue
Block a user