From 4e27963e6055019b92ee8076214c7ec059e397e9 Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Wed, 22 Sep 2021 13:59:10 +0200 Subject: [PATCH 1/3] Add fields to AudioDeviceAttributes class Additionally, expose AudioProfile and AudioDescriptor constructors. Bug: 199846689 Test: atest AudioServiceHostTest && atest AudioProfileTest && atest AudioDescriptorTest Change-Id: I7fb347c14a81d1d5f3872c8c0d2ae35eb65903f2 --- core/api/current.txt | 10 +- core/api/system-current.txt | 13 ++ media/java/android/media/AudioDescriptor.java | 79 +++++++++++- .../android/media/AudioDeviceAttributes.java | 120 +++++++++++++++++- media/java/android/media/AudioDeviceInfo.java | 2 +- media/java/android/media/AudioProfile.java | 89 ++++++++++++- 6 files changed, 297 insertions(+), 16 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 385899a078a51..92b0822a16ff6 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20787,10 +20787,13 @@ package android.media { method public android.media.AudioAttributes.Builder setUsage(int); } - public class AudioDescriptor { + public class AudioDescriptor implements android.os.Parcelable { + method public int describeContents(); method @NonNull public byte[] getDescriptor(); method public int getEncapsulationType(); method public int getStandard(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; field public static final int STANDARD_EDID = 1; // 0x1 field public static final int STANDARD_NONE = 0; // 0x0 } @@ -21304,14 +21307,17 @@ package android.media { method @NonNull public android.media.AudioPresentation.Builder setProgramId(int); } - public class AudioProfile { + public class AudioProfile implements android.os.Parcelable { + method public int describeContents(); method @NonNull public int[] getChannelIndexMasks(); method @NonNull public int[] getChannelMasks(); method public int getEncapsulationType(); method public int getFormat(); method @NonNull public int[] getSampleRates(); + method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1; // 0x1 field public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator CREATOR; } public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 17ea0858b7c06..9fc0b651aa99a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5671,11 +5671,20 @@ package android.media { method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int); } + public class AudioDescriptor implements android.os.Parcelable { + ctor public AudioDescriptor(int, int, @NonNull byte[]); + } + public final class AudioDeviceAttributes implements android.os.Parcelable { ctor public AudioDeviceAttributes(@NonNull android.media.AudioDeviceInfo); ctor public AudioDeviceAttributes(int, int, @NonNull String); + ctor public AudioDeviceAttributes(int, int, @NonNull String, @NonNull String, @NonNull java.util.List, @NonNull java.util.List); method public int describeContents(); + method public boolean equalTypeAddress(@Nullable Object); method @NonNull public String getAddress(); + method @NonNull public java.util.List getAudioDescriptors(); + method @NonNull public java.util.List getAudioProfiles(); + method @NonNull public String getName(); method public int getRole(); method public int getType(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -5826,6 +5835,10 @@ package android.media { field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff } + public class AudioProfile implements android.os.Parcelable { + ctor public AudioProfile(int, @NonNull int[], @NonNull int[], @NonNull int[], int); + } + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException; method public static long getMaxSharedAudioHistoryMillis(); diff --git a/media/java/android/media/AudioDescriptor.java b/media/java/android/media/AudioDescriptor.java index 11371b11e9059..df648be4c157b 100644 --- a/media/java/android/media/AudioDescriptor.java +++ b/media/java/android/media/AudioDescriptor.java @@ -18,16 +18,21 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; /** * The AudioDescriptor contains the information to describe the audio playback/capture * capabilities. The capabilities are described by a byte array, which is defined by a * particular standard. This is used when the format is unrecognized to the platform. */ -public class AudioDescriptor { +public class AudioDescriptor implements Parcelable { /** * The audio standard is not specified. */ @@ -49,7 +54,15 @@ public class AudioDescriptor { private final byte[] mDescriptor; private final int mEncapsulationType; - AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) { + /** + * @hide + * Constructor from standard, encapsulation type and descriptor + * @param standard the standard of the audio descriptor + * @param encapsulationType the encapsulation type of the audio descriptor + * @param descriptor the audio descriptor + */ + @SystemApi + public AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) { mStandard = standard; mEncapsulationType = encapsulationType; mDescriptor = descriptor; @@ -87,4 +100,66 @@ public class AudioDescriptor { public @AudioProfile.EncapsulationType int getEncapsulationType() { return mEncapsulationType; } + + @Override + public int hashCode() { + return Objects.hash(mStandard, mEncapsulationType, Arrays.hashCode(mDescriptor)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioDescriptor that = (AudioDescriptor) o; + return ((mStandard == that.mStandard) + && (mEncapsulationType == that.mEncapsulationType) + && (Arrays.equals(mDescriptor, that.mDescriptor))); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + sb.append("standard=" + mStandard); + sb.append(", encapsulation type=" + mEncapsulationType); + if (mDescriptor != null && mDescriptor.length > 0) { + sb.append(", descriptor=").append(Arrays.toString(mDescriptor)); + } + sb.append("}"); + return sb.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStandard); + dest.writeInt(mEncapsulationType); + dest.writeByteArray(mDescriptor); + } + + private AudioDescriptor(@NonNull Parcel in) { + mStandard = in.readInt(); + mEncapsulationType = in.readInt(); + mDescriptor = in.createByteArray(); + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + /** + * Rebuilds an AudioDescriptor previously stored with writeToParcel(). + * @param p Parcel object to read the AudioDescriptor from + * @return a new AudioDescriptor created from the data in the parcel + */ + public AudioDescriptor createFromParcel(Parcel p) { + return new AudioDescriptor(p); + } + + public AudioDescriptor[] newArray(int size) { + return new AudioDescriptor[size]; + } + }; } diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java index 1448c49105b25..4ce0440944a8b 100644 --- a/media/java/android/media/AudioDeviceAttributes.java +++ b/media/java/android/media/AudioDeviceAttributes.java @@ -18,12 +18,16 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -65,16 +69,27 @@ public final class AudioDeviceAttributes implements Parcelable { * The unique address of the device. Some devices don't have addresses, only an empty string. */ private final @NonNull String mAddress; - + /** + * The non-unique name of the device. Some devices don't have names, only an empty string. + * Should not be used as a unique identifier for a device. + */ + private final @NonNull String mName; /** * Is input or output device */ private final @Role int mRole; - /** * The internal audio device type */ private final int mNativeType; + /** + * List of AudioProfiles supported by the device + */ + private final @NonNull List mAudioProfiles; + /** + * List of AudioDescriptors supported by the device + */ + private final @NonNull List mAudioDescriptors; /** * @hide @@ -88,7 +103,10 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT; mType = deviceInfo.getType(); mAddress = deviceInfo.getAddress(); + mName = String.valueOf(deviceInfo.getProductName()); mNativeType = deviceInfo.getInternalType(); + mAudioProfiles = deviceInfo.getAudioProfiles(); + mAudioDescriptors = deviceInfo.getAudioDescriptors(); } /** @@ -100,7 +118,24 @@ public final class AudioDeviceAttributes implements Parcelable { */ @SystemApi public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, - @NonNull String address) { + @NonNull String address) { + this(role, type, address, "", new ArrayList<>(), new ArrayList<>()); + } + + /** + * @hide + * Constructor with specification of all attributes + * @param role indicates input or output role + * @param type the device type, as defined in {@link AudioDeviceInfo} + * @param address the address of the device, or an empty string for devices without one + * @param name the name of the device, or an empty string for devices without one + * @param profiles the list of AudioProfiles supported by the device + * @param descriptors the list of AudioDescriptors supported by the device + */ + @SystemApi + public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, + @NonNull String address, @NonNull String name, @NonNull List profiles, + @NonNull List descriptors) { Objects.requireNonNull(address); if (role != ROLE_OUTPUT && role != ROLE_INPUT) { throw new IllegalArgumentException("Invalid role " + role); @@ -118,19 +153,26 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = role; mType = type; mAddress = address; + mName = name; + mAudioProfiles = profiles; + mAudioDescriptors = descriptors; } /** * @hide - * Constructor from internal device type and address - * @param type the internal device type, as defined in {@link AudioSystem} + * Constructor called from AudioSystem JNI when creating an AudioDeviceAttributes from a native + * AudioDeviceTypeAddr instance. + * @param nativeType the internal device type, as defined in {@link AudioSystem} * @param address the address of the device, or an empty string for devices without one */ public AudioDeviceAttributes(int nativeType, @NonNull String address) { mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; + mName = ""; mNativeType = nativeType; + mAudioProfiles = new ArrayList<>(); + mAudioDescriptors = new ArrayList<>(); } /** @@ -163,6 +205,16 @@ public final class AudioDeviceAttributes implements Parcelable { return mAddress; } + /** + * @hide + * Returns the name of the audio device, or an empty string for devices without one + * @return the device name + */ + @SystemApi + public @NonNull String getName() { + return mName; + } + /** * @hide * Returns the internal device type of a device @@ -172,9 +224,29 @@ public final class AudioDeviceAttributes implements Parcelable { return mNativeType; } + /** + * @hide + * Returns the list of AudioProfiles supported by the device + * @return the list of AudioProfiles + */ + @SystemApi + public @NonNull List getAudioProfiles() { + return mAudioProfiles; + } + + /** + * @hide + * Returns the list of AudioDescriptors supported by the device + * @return the list of AudioDescriptors + */ + @SystemApi + public @NonNull List getAudioDescriptors() { + return mAudioDescriptors; + } + @Override public int hashCode() { - return Objects.hash(mRole, mType, mAddress); + return Objects.hash(mRole, mType, mAddress, mName, mAudioProfiles, mAudioDescriptors); } @Override @@ -182,6 +254,25 @@ public final class AudioDeviceAttributes implements Parcelable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + AudioDeviceAttributes that = (AudioDeviceAttributes) o; + return ((mRole == that.mRole) + && (mType == that.mType) + && mAddress.equals(that.mAddress) + && mName.equals(that.mName) + && mAudioProfiles.equals(that.mAudioProfiles) + && mAudioDescriptors.equals(that.mAudioDescriptors)); + } + + /** + * Returns true if the role, type and address are equal. Called to compare with an + * AudioDeviceAttributes that was created from a native AudioDeviceTypeAddr instance. + * @param o object to compare with + * @return whether role, type and address are equal + */ + public boolean equalTypeAddress(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AudioDeviceAttributes that = (AudioDeviceAttributes) o; return ((mRole == that.mRole) && (mType == that.mType) @@ -199,7 +290,10 @@ public final class AudioDeviceAttributes implements Parcelable { + " role:" + roleToString(mRole) + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType) : AudioSystem.getInputDeviceName(mNativeType)) - + " addr:" + mAddress); + + " addr:" + mAddress + + " name:" + mName + + " profiles:" + mAudioProfiles.toString() + + " descriptors:" + mAudioDescriptors.toString()); } @Override @@ -212,14 +306,26 @@ public final class AudioDeviceAttributes implements Parcelable { dest.writeInt(mRole); dest.writeInt(mType); dest.writeString(mAddress); + dest.writeString(mName); dest.writeInt(mNativeType); + dest.writeParcelableArray( + mAudioProfiles.toArray(new AudioProfile[mAudioProfiles.size()]), flags); + dest.writeParcelableArray( + mAudioDescriptors.toArray(new AudioDescriptor[mAudioDescriptors.size()]), flags); } private AudioDeviceAttributes(@NonNull Parcel in) { mRole = in.readInt(); mType = in.readInt(); mAddress = in.readString(); + mName = in.readString(); mNativeType = in.readInt(); + AudioProfile[] audioProfilesArray = + in.readParcelableArray(AudioProfile.class.getClassLoader(), AudioProfile.class); + mAudioProfiles = new ArrayList(Arrays.asList(audioProfilesArray)); + AudioDescriptor[] audioDescriptorsArray = in.readParcelableArray( + AudioDescriptor.class.getClassLoader(), AudioDescriptor.class); + mAudioDescriptors = new ArrayList(Arrays.asList(audioDescriptorsArray)); } public static final @NonNull Parcelable.Creator CREATOR = diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 211a50ee93afc..59e109f1d36b7 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -413,7 +413,7 @@ public final class AudioDeviceInfo { */ public CharSequence getProductName() { String portName = mPort.name(); - return portName.length() != 0 ? portName : android.os.Build.MODEL; + return (portName != null && portName.length() != 0) ? portName : android.os.Build.MODEL; } /** diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java index ae8d0a5ad4ab1..5c5f837dd07a2 100644 --- a/media/java/android/media/AudioProfile.java +++ b/media/java/android/media/AudioProfile.java @@ -18,10 +18,14 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -33,7 +37,7 @@ import java.util.stream.Collectors; * be reported in different audio profiles. The application can choose any of the encapsulation * types. */ -public class AudioProfile { +public class AudioProfile implements Parcelable { /** * No encapsulation type is specified. */ @@ -57,9 +61,19 @@ public class AudioProfile { private final int[] mChannelIndexMasks; private final int mEncapsulationType; - AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, - @NonNull int[] channelIndexMasks, - int encapsulationType) { + /** + * @hide + * Constructor from format, sampling rates, channel masks, channel index masks and + * encapsulation type. + * @param format the audio format + * @param samplingRates the supported sampling rates + * @param channelMasks the supported channel masks + * @param channelIndexMasks the supported channel index masks + * @param encapsulationType the encapsulation type of the encoding format + */ + @SystemApi + public AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, + @NonNull int[] channelIndexMasks, int encapsulationType) { mFormat = format; mSamplingRates = samplingRates; mChannelMasks = channelMasks; @@ -113,6 +127,26 @@ public class AudioProfile { return mEncapsulationType; } + @Override + public int hashCode() { + return Objects.hash(mFormat, Arrays.hashCode(mSamplingRates), + Arrays.hashCode(mChannelMasks), Arrays.hashCode(mChannelIndexMasks), + mEncapsulationType); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioProfile that = (AudioProfile) o; + return ((mFormat == that.mFormat) + && (hasIdenticalElements(mSamplingRates, that.mSamplingRates)) + && (hasIdenticalElements(mChannelMasks, that.mChannelMasks)) + && (hasIdenticalElements(mChannelIndexMasks, that.mChannelIndexMasks)) + && (mEncapsulationType == that.mEncapsulationType)); + } + @Override public String toString() { StringBuilder sb = new StringBuilder("{"); @@ -126,6 +160,7 @@ public class AudioProfile { if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) { sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks)); } + sb.append(", encapsulation type=" + mEncapsulationType); sb.append("}"); return sb.toString(); } @@ -137,4 +172,50 @@ public class AudioProfile { return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X", anInt)) .collect(Collectors.joining(", ")); } + + private static boolean hasIdenticalElements(int[] array1, int[] array2) { + int[] sortedArray1 = Arrays.copyOf(array1, array1.length); + Arrays.sort(sortedArray1); + int[] sortedArray2 = Arrays.copyOf(array2, array2.length); + Arrays.sort(sortedArray2); + return Arrays.equals(sortedArray1, sortedArray2); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mFormat); + dest.writeIntArray(mSamplingRates); + dest.writeIntArray(mChannelMasks); + dest.writeIntArray(mChannelIndexMasks); + dest.writeInt(mEncapsulationType); + } + + private AudioProfile(@NonNull Parcel in) { + mFormat = in.readInt(); + mSamplingRates = in.createIntArray(); + mChannelMasks = in.createIntArray(); + mChannelIndexMasks = in.createIntArray(); + mEncapsulationType = in.readInt(); + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + /** + * Rebuilds an AudioProfile previously stored with writeToParcel(). + * @param p Parcel object to read the AudioProfile from + * @return a new AudioProfile created from the data in the parcel + */ + public AudioProfile createFromParcel(Parcel p) { + return new AudioProfile(p); + } + + public AudioProfile[] newArray(int size) { + return new AudioProfile[size]; + } + }; } From 517a1327ca71b5f01c7705b0a90499c20e4b8a23 Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Fri, 15 Oct 2021 14:22:41 +0200 Subject: [PATCH 2/3] Add AudioDeviceAttributes as argument to AudioManager API NoNonSdkCheck: This CL changes the arguments of an existing API that was annotated already. Bug: 199846845 Test: atest AidlConversionUnitTests Change-Id: I90a24c0428d9c77ccd45dff65bbdad198aa81fb2 --- core/jni/android_media_AudioSystem.cpp | 51 +-- .../android/media/AudioDeviceAttributes.java | 13 +- media/java/android/media/AudioManager.java | 25 +- media/java/android/media/AudioSystem.java | 23 +- media/java/android/media/IAudioService.aidl | 3 +- .../media/audio/common/AidlConversion.java | 350 ++++++++++++++++++ .../media/AidlConversionUnitTests.java | 107 +++++- .../server/audio/AudioDeviceBroker.java | 13 +- .../server/audio/AudioDeviceInventory.java | 130 +++---- .../android/server/audio/AudioService.java | 14 +- .../server/audio/AudioServiceEvents.java | 7 +- .../server/audio/AudioSystemAdapter.java | 13 +- .../com/android/server/audio/BtHelper.java | 15 +- .../server/audio/AudioDeviceBrokerTest.java | 12 +- .../server/audio/NoOpAudioSystemAdapter.java | 9 +- .../com/android/server/usb/UsbAlsaDevice.java | 16 +- 16 files changed, 657 insertions(+), 144 deletions(-) diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 3e2b25853a6a2..e439e49acf71e 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -18,25 +18,25 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioSystem-JNI" -#include - -#include -#include -#include -#include -#include "core_jni_helpers.h" - #include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include +#include + +#include +#include + #include "android_media_AudioAttributes.h" #include "android_media_AudioDescriptor.h" #include "android_media_AudioDeviceAttributes.h" @@ -46,6 +46,7 @@ #include "android_media_AudioProfile.h" #include "android_media_MicrophoneInfo.h" #include "android_util_Binder.h" +#include "core_jni_helpers.h" // ---------------------------------------------------------------------------- @@ -578,18 +579,26 @@ android_media_AudioSystem_routing_callback() env->DeleteLocalRef(clazz); } -static jint -android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name, - jint codec) -{ - const char *c_address = env->GetStringUTFChars(device_address, NULL); - const char *c_name = env->GetStringUTFChars(device_name, NULL); - int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast (device), - static_cast (state), - c_address, c_name, - static_cast (codec))); - env->ReleaseStringUTFChars(device_address, c_address); - env->ReleaseStringUTFChars(device_name, c_name); +static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, + jint state, jobject jParcel, + jint codec) { + int status; + if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) { + android::media::audio::common::AudioPort port{}; + if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) { + status = check_AudioSystem_Command( + AudioSystem::setDeviceConnectionState(static_cast( + state), + port, + static_cast(codec))); + } else { + ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str()); + status = kAudioStatusError; + } + } else { + ALOGE("Failed to retrieve the native parcel from Java parcel"); + status = kAudioStatusError; + } return (jint) status; } @@ -2830,7 +2839,7 @@ static const JNINativeMethod gMethods[] = {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId}, {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId}, - {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I", + {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java index 4ce0440944a8b..af3c295b8d6cf 100644 --- a/media/java/android/media/AudioDeviceAttributes.java +++ b/media/java/android/media/AudioDeviceAttributes.java @@ -166,10 +166,21 @@ public final class AudioDeviceAttributes implements Parcelable { * @param address the address of the device, or an empty string for devices without one */ public AudioDeviceAttributes(int nativeType, @NonNull String address) { + this(nativeType, address, ""); + } + + /** + * @hide + * Constructor called from BtHelper to connect or disconnect a Bluetooth device. + * @param nativeType the internal device type, as defined in {@link AudioSystem} + * @param address the address of the device, or an empty string for devices without one + * @param name the name of the device, or an empty string for devices without one + */ + public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull String name) { mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; - mName = ""; + mName = name; mNativeType = nativeType; mAudioProfiles = new ArrayList<>(); mAudioDescriptors = new ArrayList<>(); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 68e5d94f75595..bae0455627675 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5851,7 +5851,7 @@ public class AudioManager { return false; } - /** + /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) * @param state new connection state: 1 connected, 0 disconnected @@ -5860,10 +5860,29 @@ public class AudioManager { */ @UnsupportedAppUsage @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public void setWiredDeviceConnectionState(int type, int state, String address, String name) { + public void setWiredDeviceConnectionState(int device, int state, String address, + String name) { + final IAudioService service = getService(); + int role = isOutputDevice(device) + ? AudioDeviceAttributes.ROLE_OUTPUT : AudioDeviceAttributes.ROLE_INPUT; + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + role, AudioDeviceInfo.convertInternalDeviceToDeviceType(device), address, + name, new ArrayList<>()/*mAudioProfiles*/, new ArrayList<>()/*mAudioDescriptors*/); + setWiredDeviceConnectionState(attributes, state); + } + + /** + * Indicate wired accessory connection state change and attributes. + * @param state new connection state: 1 connected, 0 disconnected + * @param attributes attributes of the connected device + * {@hide} + */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) { final IAudioService service = getService(); try { - service.setWiredDeviceConnectionState(type, state, address, name, + service.setWiredDeviceConnectionState(attributes, state, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index af5a3da5f3e28..31ccba5b4d028 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -26,10 +26,12 @@ import android.bluetooth.BluetoothLeAudioCodecConfig; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; +import android.media.audio.common.AidlConversion; import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.os.Build; import android.os.IBinder; +import android.os.Parcel; import android.os.Vibrator; import android.telephony.TelephonyManager; import android.util.Log; @@ -1542,9 +1544,24 @@ public class AudioSystem * {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED} */ @UnsupportedAppUsage - public static native int setDeviceConnectionState(int device, int state, - String device_address, String device_name, - int codecFormat); + public static int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + android.media.audio.common.AudioPort port = + AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + Parcel parcel = Parcel.obtain(); + port.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + try { + return setDeviceConnectionState(state, parcel, codecFormat); + } finally { + parcel.recycle(); + } + } + /** + * @hide + */ + @UnsupportedAppUsage + public static native int setDeviceConnectionState(int state, Parcel parcel, int codecFormat); /** @hide */ @UnsupportedAppUsage public static native int getDeviceConnectionState(int device, String device_address); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 96199a9887043..a43f90a1a503c 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -215,8 +215,7 @@ interface IAudioService { IRingtonePlayer getRingtonePlayer(); int getUiSoundsStreamType(); - void setWiredDeviceConnectionState(int type, int state, String address, String name, - String caller); + void setWiredDeviceConnectionState(in AudioDeviceAttributes aa, int state, String caller); @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java index 1053fb717fda9..f17189dedcba4 100644 --- a/media/java/android/media/audio/common/AidlConversion.java +++ b/media/java/android/media/audio/common/AidlConversion.java @@ -17,12 +17,17 @@ package android.media.audio.common; import android.annotation.NonNull; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; import android.media.AudioFormat; +import android.media.AudioSystem; import android.media.MediaFormat; import android.os.Parcel; import com.android.internal.annotations.VisibleForTesting; +import java.util.stream.Collectors; + /** * This class provides utility functions for converting between * the AIDL types defined in 'android.media.audio.common' and: @@ -525,6 +530,351 @@ public class AidlConversion { } } + /** + * Convert from SDK AudioDeviceAttributes to AIDL AudioPort. + */ + public static AudioPort api2aidl_AudioDeviceAttributes_AudioPort( + @NonNull AudioDeviceAttributes attributes) { + AudioPort port = new AudioPort(); + port.name = attributes.getName(); + // TO DO: b/211611504 Convert attributes.getAudioProfiles() to AIDL as well. + port.profiles = new AudioProfile[]{}; + port.extraAudioDescriptors = attributes.getAudioDescriptors().stream() + .map(descriptor -> api2aidl_AudioDescriptor_ExtraAudioDescriptor(descriptor)) + .collect(Collectors.toList()).toArray(ExtraAudioDescriptor[]::new); + port.flags = new AudioIoFlags(); + port.gains = new AudioGain[]{}; + AudioPortDeviceExt deviceExt = new AudioPortDeviceExt(); + deviceExt.device = new AudioDevice(); + deviceExt.encodedFormats = new AudioFormatDescription[]{}; + deviceExt.device.type = + api2aidl_NativeType_AudioDeviceDescription(attributes.getInternalType()); + deviceExt.device.address = AudioDeviceAddress.id(attributes.getAddress()); + port.ext = AudioPortExt.device(deviceExt); + return port; + } + + /** + * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor. + */ + public static ExtraAudioDescriptor api2aidl_AudioDescriptor_ExtraAudioDescriptor( + @NonNull AudioDescriptor descriptor) { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = + api2aidl_AudioDescriptorStandard_AudioStandard(descriptor.getStandard()); + extraDescriptor.audioDescriptor = descriptor.getDescriptor(); + extraDescriptor.encapsulationType = + api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType( + descriptor.getEncapsulationType()); + return extraDescriptor; + } + + /** + * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor. + */ + public static @NonNull AudioDescriptor aidl2api_ExtraAudioDescriptor_AudioDescriptor( + @NonNull ExtraAudioDescriptor extraDescriptor) { + AudioDescriptor descriptor = new AudioDescriptor( + aidl2api_AudioStandard_AudioDescriptorStandard(extraDescriptor.standard), + aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType( + extraDescriptor.encapsulationType), + extraDescriptor.audioDescriptor); + return descriptor; + } + + /** + * Convert from SDK AudioDescriptor#mStandard to AIDL AudioStandard + */ + @AudioStandard + public static int api2aidl_AudioDescriptorStandard_AudioStandard( + @AudioDescriptor.AudioDescriptorStandard int standard) { + switch (standard) { + case AudioDescriptor.STANDARD_EDID: + return AudioStandard.EDID; + case AudioDescriptor.STANDARD_NONE: + default: + return AudioStandard.NONE; + } + } + + /** + * Convert from AIDL AudioStandard to SDK AudioDescriptor#mStandard + */ + @AudioDescriptor.AudioDescriptorStandard + public static int aidl2api_AudioStandard_AudioDescriptorStandard(@AudioStandard int standard) { + switch (standard) { + case AudioStandard.EDID: + return AudioDescriptor.STANDARD_EDID; + case AudioStandard.NONE: + default: + return AudioDescriptor.STANDARD_NONE; + } + } + + /** + * Convert from SDK AudioProfile.EncapsulationType to AIDL AudioEncapsulationType + */ + @AudioEncapsulationType + public static int api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType( + @android.media.AudioProfile.EncapsulationType int type) { + switch (type) { + case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937: + return AudioEncapsulationType.IEC61937; + case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE: + default: + return AudioEncapsulationType.NONE; + } + } + + /** + * Convert from AIDL AudioEncapsulationType to SDK AudioProfile.EncapsulationType + */ + @android.media.AudioProfile.EncapsulationType + public static int aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType( + @AudioEncapsulationType int type) { + switch (type) { + case AudioEncapsulationType.IEC61937: + return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937; + case AudioEncapsulationType.NONE: + default: + return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE; + } + } + + /** + * Convert from SDK native type to AIDL AudioDeviceDescription + */ + public static AudioDeviceDescription api2aidl_NativeType_AudioDeviceDescription( + int nativeType) { + AudioDeviceDescription aidl = new AudioDeviceDescription(); + aidl.connection = ""; + switch (nativeType) { + case AudioSystem.DEVICE_OUT_EARPIECE: + aidl.type = AudioDeviceType.OUT_SPEAKER_EARPIECE; + break; + case AudioSystem.DEVICE_OUT_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + break; + case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE: + aidl.type = AudioDeviceType.OUT_HEADPHONE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + aidl.type = AudioDeviceType.OUT_CARKIT; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + aidl.type = AudioDeviceType.OUT_HEADPHONE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_TELEPHONY_TX: + aidl.type = AudioDeviceType.OUT_TELEPHONY_TX; + break; + case AudioSystem.DEVICE_OUT_AUX_LINE: + aidl.type = AudioDeviceType.OUT_LINE_AUX; + break; + case AudioSystem.DEVICE_OUT_SPEAKER_SAFE: + aidl.type = AudioDeviceType.OUT_SPEAKER_SAFE; + break; + case AudioSystem.DEVICE_OUT_HEARING_AID: + aidl.type = AudioDeviceType.OUT_HEARING_AID; + aidl.connection = AudioDeviceDescription.CONNECTION_WIRELESS; + break; + case AudioSystem.DEVICE_OUT_ECHO_CANCELLER: + aidl.type = AudioDeviceType.OUT_ECHO_CANCELLER; + break; + case AudioSystem.DEVICE_OUT_BLE_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_BUILTIN_MIC: + aidl.type = AudioDeviceType.IN_MICROPHONE; + break; + case AudioSystem.DEVICE_IN_BACK_MIC: + aidl.type = AudioDeviceType.IN_MICROPHONE_BACK; + break; + case AudioSystem.DEVICE_IN_TELEPHONY_RX: + aidl.type = AudioDeviceType.IN_TELEPHONY_RX; + break; + case AudioSystem.DEVICE_IN_TV_TUNER: + aidl.type = AudioDeviceType.IN_TV_TUNER; + break; + case AudioSystem.DEVICE_IN_LOOPBACK: + aidl.type = AudioDeviceType.IN_LOOPBACK; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_BLE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_ECHO_REFERENCE: + aidl.type = AudioDeviceType.IN_ECHO_REFERENCE; + break; + case AudioSystem.DEVICE_IN_WIRED_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_WIRED_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_IN_HDMI: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI; + break; + case AudioSystem.DEVICE_OUT_HDMI: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI; + break; + case AudioSystem.DEVICE_IN_REMOTE_SUBMIX: + aidl.type = AudioDeviceType.IN_SUBMIX; + break; + case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX: + aidl.type = AudioDeviceType.OUT_SUBMIX; + break; + case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET: + aidl.type = AudioDeviceType.IN_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET: + aidl.type = AudioDeviceType.OUT_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET: + aidl.type = AudioDeviceType.IN_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET: + aidl.type = AudioDeviceType.OUT_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_USB_ACCESSORY: + aidl.type = AudioDeviceType.IN_ACCESSORY; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_ACCESSORY: + aidl.type = AudioDeviceType.OUT_ACCESSORY; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_USB_DEVICE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_DEVICE: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_FM_TUNER: + aidl.type = AudioDeviceType.IN_FM_TUNER; + break; + case AudioSystem.DEVICE_OUT_FM: + aidl.type = AudioDeviceType.OUT_FM; + break; + case AudioSystem.DEVICE_IN_LINE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_LINE: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_SPDIF: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF; + break; + case AudioSystem.DEVICE_OUT_SPDIF: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_A2DP: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_IN_IP: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4; + break; + case AudioSystem.DEVICE_OUT_IP: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4; + break; + case AudioSystem.DEVICE_IN_BUS: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BUS; + break; + case AudioSystem.DEVICE_OUT_BUS: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BUS; + break; + case AudioSystem.DEVICE_IN_PROXY: + aidl.type = AudioDeviceType.IN_AFE_PROXY; + break; + case AudioSystem.DEVICE_OUT_PROXY: + aidl.type = AudioDeviceType.OUT_AFE_PROXY; + break; + case AudioSystem.DEVICE_IN_USB_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_HDMI_ARC: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC; + break; + case AudioSystem.DEVICE_OUT_HDMI_ARC: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC; + break; + case AudioSystem.DEVICE_IN_HDMI_EARC: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC; + break; + case AudioSystem.DEVICE_OUT_HDMI_EARC: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC; + break; + case AudioSystem.DEVICE_IN_BLE_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_OUT_BLE_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_DEFAULT: + aidl.type = AudioDeviceType.IN_DEFAULT; + break; + case AudioSystem.DEVICE_OUT_DEFAULT: + aidl.type = AudioDeviceType.OUT_DEFAULT; + break; + default: + aidl.type = AudioDeviceType.NONE; + } + return aidl; + } + private static native int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t( Parcel aidl, boolean isInput); private static native Parcel legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel( diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java index 414de89f82185..09573909c288d 100644 --- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java +++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java @@ -16,8 +16,18 @@ package android.media.audio.common; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + import android.media.AudioAttributes; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; +import android.media.AudioProfile; import android.media.AudioSystem; import android.media.AudioTrack; import android.media.MediaFormat; @@ -25,11 +35,12 @@ import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; -import static org.junit.Assert.*; - import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; + /** * Unit tests for AidlConversion utilities. * @@ -417,10 +428,102 @@ public final class AidlConversionUnitTests { () -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue)); } + @Test + public void testAudioDescriptorConversion_Default() { + ExtraAudioDescriptor aidl = createDefaultDescriptor(); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_NONE, audioDescriptor.getStandard()); + assertEquals( + AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDescriptorConversion() { + ExtraAudioDescriptor aidl = createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A}); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_EDID, audioDescriptor.getStandard()); + assertEquals(AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937, + audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDeviceAttributesConversion_Default() { + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_DEFAULT, "myAddress"); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + assertEquals("", port.name); + assertEquals(0, port.extraAudioDescriptors.length); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals("", port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEFAULT, port.ext.getDevice().device.type.type); + } + + @Test + public void testAudioDeviceAttributesConversion() { + AudioDescriptor audioDescriptor1 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A})); + + AudioDescriptor audioDescriptor2 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createDefaultDescriptor()); + + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HDMI_ARC, "myAddress", "myName", new ArrayList<>(), + new ArrayList<>(Arrays.asList(audioDescriptor1, audioDescriptor2))); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort( + attributes); + assertEquals("myName", port.name); + assertEquals(2, port.extraAudioDescriptors.length); + assertEquals(AudioStandard.EDID, port.extraAudioDescriptors[0].standard); + assertEquals(AudioEncapsulationType.IEC61937, + port.extraAudioDescriptors[0].encapsulationType); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, + port.extraAudioDescriptors[0].audioDescriptor)); + assertEquals(AudioStandard.NONE, port.extraAudioDescriptors[1].standard); + assertEquals(AudioEncapsulationType.NONE, + port.extraAudioDescriptors[1].encapsulationType); + assertTrue(Arrays.equals(new byte[]{}, + port.extraAudioDescriptors[1].audioDescriptor)); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals(AudioDeviceDescription.CONNECTION_HDMI_ARC, + port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type); + } + private static AudioFormatDescription createPcm16FormatAidl() { final AudioFormatDescription aidl = new AudioFormatDescription(); aidl.type = AudioFormatType.PCM; aidl.pcm = PcmType.INT_16_BIT; return aidl; } + + private static ExtraAudioDescriptor createDefaultDescriptor() { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.NONE; + extraDescriptor.encapsulationType = AudioEncapsulationType.NONE; + extraDescriptor.audioDescriptor = new byte[]{}; + return extraDescriptor; + } + + private static ExtraAudioDescriptor createEncapsulationDescriptor(byte[] audioDescriptor) { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.EDID; + extraDescriptor.encapsulationType = AudioEncapsulationType.IEC61937; + extraDescriptor.audioDescriptor = audioDescriptor; + return extraDescriptor; + } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 2fcdd6145a646..b855763299ae4 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -499,12 +499,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void setWiredDeviceConnectionState(int type, - @AudioService.ConnectionState int state, String address, String name, - String caller) { + /*package*/ void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { //TODO move logging here just like in setBluetooth* methods synchronized (mDeviceStateLock) { - mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceInventory.setWiredDeviceConnectionState(attributes, state, caller); } } @@ -1012,11 +1011,9 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) { synchronized (mDeviceStateLock) { - return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName, - false /*for test*/); + return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 0961fcb31ace1..43860161bb1c4 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -219,19 +219,15 @@ public class AudioDeviceInventory { * A class just for packaging up a set of connection parameters. */ /*package*/ class WiredDeviceConnectionState { - public final int mType; + public final AudioDeviceAttributes mAttributes; public final @AudioService.ConnectionState int mState; - public final String mAddress; - public final String mName; public final String mCaller; public boolean mForTest = false; - /*package*/ WiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { - mType = type; + /*package*/ WiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { + mAttributes = attributes; mState = state; - mAddress = address; - mName = name; mCaller = caller; } } @@ -269,11 +265,10 @@ public class AudioDeviceInventory { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { - mAudioSystem.setDeviceConnectionState( - di.mDeviceType, - AudioSystem.DEVICE_STATE_AVAILABLE, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, - di.mDeviceName, + di.mDeviceName), + AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } } @@ -508,41 +503,45 @@ public class AudioDeviceInventory { /*package*/ void onSetWiredDeviceConnectionState( AudioDeviceInventory.WiredDeviceConnectionState wdcs) { + int type = wdcs.mAttributes.getInternalType(); + AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onSetWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType)) + .set(MediaMetrics.Property.ADDRESS, wdcs.mAttributes.getAddress()) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(type)) .set(MediaMetrics.Property.STATE, wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) - && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state DISCONNECTED"); } - if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, - wdcs.mType, wdcs.mAddress, wdcs.mName, wdcs.mForTest)) { + if (!handleDeviceConnection(wdcs.mAttributes, + wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) { // change of connection state failed, bailout mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") .record(); return; } if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { - if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state not DISCONNECTED"); } - mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller); + mDeviceBroker.checkMusicActive(type, wdcs.mCaller); } - if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) { + if (type == AudioSystem.DEVICE_OUT_HDMI) { mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller); } - sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName); - updateAudioRoutes(wdcs.mType, wdcs.mState); + sendDeviceConnectionIntent(type, wdcs.mState, + wdcs.mAttributes.getAddress(), wdcs.mAttributes.getName()); + updateAudioRoutes(type, wdcs.mState); } mmi.record(); } @@ -561,12 +560,12 @@ public class AudioDeviceInventory { return; } // Toggle HDMI to retrigger broadcast with proper formats. - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "", - "android"); // disconnect - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_AVAILABLE, "", "", - "android"); // reconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect } mmi.record(); } @@ -696,16 +695,17 @@ public class AudioDeviceInventory { /** * Implements the communication with AudioSystem to (dis)connect a device in the native layers + * @param attributes the attributes of the device * @param connect true if connection - * @param device the device type - * @param address the address of the device - * @param deviceName human-readable name of device * @param isForTesting if true, not calling AudioSystem for the connection as this is * just for testing * @return false if an error was reported by AudioSystem */ - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName, boolean isForTesting) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect, + boolean isForTesting) { + int device = attributes.getInternalType(); + String address = attributes.getAddress(); + String deviceName = attributes.getName(); if (AudioService.DEBUG_DEVICES) { Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device) + " address:" + address @@ -732,9 +732,8 @@ public class AudioDeviceInventory { if (isForTesting) { res = AudioSystem.AUDIO_STATUS_OK; } else { - res = mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + res = mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); } if (res != AudioSystem.AUDIO_STATUS_OK) { final String reason = "not connecting device 0x" + Integer.toHexString(device) @@ -751,9 +750,8 @@ public class AudioDeviceInventory { mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } else if (!connect && isConnected) { - mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); @@ -930,13 +928,13 @@ public class AudioDeviceInventory { return delay; } - /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { + /*package*/ int setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { synchronized (mDevicesLock) { - int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE); + int delay = checkSendBecomingNoisyIntentInt( + attributes.getInternalType(), state, AudioSystem.DEVICE_NONE); mDeviceBroker.postSetWiredDeviceConnectionState( - new WiredDeviceConnectionState(type, state, address, name, caller), - delay); + new WiredDeviceConnectionState(attributes, state, caller), delay); return delay; } } @@ -944,8 +942,7 @@ public class AudioDeviceInventory { /*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device, @AudioService.ConnectionState int state) { final WiredDeviceConnectionState connection = new WiredDeviceConnectionState( - device.getInternalType(), state, device.getAddress(), - "test device", "com.android.server.audio"); + device, state, "com.android.server.audio"); connection.mForTest = true; onSetWiredDeviceConnectionState(connection); } @@ -961,8 +958,9 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec); // TODO: log in MediaMetrics once distinction between connection failure and // double connection is made. @@ -1019,8 +1017,9 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.clearAvrcpAbsoluteVolumeSupported(); - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( @@ -1058,8 +1057,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), @@ -1069,8 +1069,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); @@ -1083,8 +1084,9 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), @@ -1106,8 +1108,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); @@ -1124,14 +1127,14 @@ public class AudioDeviceInventory { private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device, String eventSource) { if (device != AudioSystem.DEVICE_NONE) { - /* Audio Policy sees Le Audio similar to A2DP. Let's make sure * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set */ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, - address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); @@ -1152,8 +1155,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeLeAudioDeviceUnavailable(String address, int device) { if (device != AudioSystem.DEVICE_NONE) { - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, - address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 346ae0fed2e12..c51e967bd8594 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6377,23 +6377,23 @@ public class AudioService extends IAudioService.Stub /** * see AudioManager.setWiredDeviceConnectionState() */ - public void setWiredDeviceConnectionState(int type, - @ConnectionState int state, String address, String name, - String caller) { + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @ConnectionState int state, String caller) { enforceModifyAudioRoutingPermission(); if (state != CONNECTION_STATE_CONNECTED && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); } new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ADDRESS, attributes.getAddress()) .set(MediaMetrics.Property.CLIENT_NAME, caller) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type)) - .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(attributes.getInternalType())) + .set(MediaMetrics.Property.NAME, attributes.getName()) .set(MediaMetrics.Property.STATE, state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected") .record(); - mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceBroker.setWiredDeviceConnectionState(attributes, state, caller); } /** @see AudioManager#setTestDeviceConnectionState(AudioDeviceAttributes, boolean) */ diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 3137fa5784d2a..3225274a8a9bf 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -116,10 +116,11 @@ public class AudioServiceEvents { @Override public String eventToString() { return new StringBuilder("setWiredDeviceConnectionState(") - .append(" type:").append(Integer.toHexString(mState.mType)) + .append(" type:").append( + Integer.toHexString(mState.mAttributes.getInternalType())) .append(" state:").append(AudioSystem.deviceStateToString(mState.mState)) - .append(" addr:").append(mState.mAddress) - .append(" name:").append(mState.mName) + .append(" addr:").append(mState.mAttributes.getAddress()) + .append(" name:").append(mState.mAttributes.getName()) .append(") from ").append(mState.mCaller).toString(); } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index a2ba76b6fd6a1..f572261c09e57 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -258,19 +258,16 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { } /** - * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} - * @param device + * Same as {@link AudioSystem#setDeviceConnectionState(AudioDeviceAttributes, int, int)} + * @param attributes * @param state - * @param deviceAddress - * @param deviceName * @param codecFormat * @return */ - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { invalidateRoutingCache(); - return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, - codecFormat); + return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat); } /** diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 6ec9836206981..30b5c3c16069a 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -586,8 +586,9 @@ public class BtHelper { String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(), - audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName), + isActive); } else { int[] outDeviceTypes = { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, @@ -595,13 +596,15 @@ public class BtHelper { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; for (int outDeviceType : outDeviceTypes) { - result |= mDeviceBroker.handleDeviceConnection( - isActive, outDeviceType, audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + outDeviceType, audioDevice.getAddress(), btDeviceName), + isActive); } } // handleDeviceConnection() && result to make sure the method get executed - result = mDeviceBroker.handleDeviceConnection( - isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result; + result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + inDevice, audioDevice.getAddress(), btDeviceName), + isActive) && result; return result; } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 9e1445cf589d3..2294a60ba4bc2 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; +import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; import android.media.BtProfileConnectionInfo; @@ -186,8 +187,9 @@ public class AudioDeviceBrokerTest { doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup( any(Intent.class)); - mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioService.CONNECTION_STATE_CONNECTED, address, name, caller); + mSpyDevInventory.setWiredDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_WIRED_HEADSET, address, name), + AudioService.CONNECTION_STATE_CONNECTED, caller); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // Verify that the sticky intent is broadcasted @@ -246,11 +248,11 @@ public class AudioDeviceBrokerTest { */ private void checkSingleSystemConnection(BluetoothDevice btDevice) throws Exception { final String expectedName = btDevice.getName() == null ? "" : btDevice.getName(); + AudioDeviceAttributes expected = new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress(), expectedName); verify(mSpyAudioSystem, times(1)).setDeviceConnectionState( - ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), + ArgumentMatchers.argThat(x -> x.equalTypeAddress(expected)), ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE), - ArgumentMatchers.eq(btDevice.getAddress()), - ArgumentMatchers.eq(expectedName), anyInt() /*codec*/); } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 8d706cb960e98..1f355b096335f 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -48,11 +48,10 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { //----------------------------------------------------------------- // Overrides of AudioSystemAdapter @Override - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { - Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, %s, %s, 0x%s", - Integer.toHexString(device), state, deviceAddress, deviceName, - Integer.toHexString(codecFormat))); + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s", + attributes.toString(), state, Integer.toHexString(codecFormat))); return AudioSystem.AUDIO_STATUS_OK; } diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java index 9d4db003a2978..7d8d240cd8e07 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.annotation.NonNull; +import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.media.IAudioService; import android.os.RemoteException; @@ -203,24 +204,25 @@ public final class UsbAlsaDevice { int outputState = (enable && connected) ? 1 : 0; if (outputState != mOutputState) { mOutputState = outputState; - mAudioService.setWiredDeviceConnectionState(device, outputState, - alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG); } } // Input Device if (mHasInput) { - int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET + int device = mIsInputHeadset + ? AudioSystem.DEVICE_IN_USB_HEADSET : AudioSystem.DEVICE_IN_USB_DEVICE; boolean connected = isInputJackConnected(); Slog.i(TAG, "INPUT JACK connected: " + connected); int inputState = (enable && connected) ? 1 : 0; if (inputState != mInputState) { mInputState = inputState; - mAudioService.setWiredDeviceConnectionState( - device, inputState, alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG); } } } catch (RemoteException e) { From 4173335baffc84a496d69e2ce4219771bc8bd08f Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Mon, 29 Nov 2021 16:51:49 +0100 Subject: [PATCH 3/3] Inform AudioManager about ARC SADs When an ARC connection is established, the SAD query is triggered and the result is sent to AudioManager, together with the ARC connection state. Bug: 199846933 Test: atest HdmiCecLocalDeviceTvTest Change-Id: Ic598136625da6958aee725b57ecada26ea0217d7 --- .../server/hdmi/HdmiCecLocalDeviceTv.java | 41 ++++++++++++++----- .../server/hdmi/HdmiCecLocalDeviceTvTest.java | 24 +++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index c2ed24a1136d5..1f411cda4bd3b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -38,7 +38,10 @@ import android.hardware.hdmi.HdmiRecordSources; import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; -import android.media.AudioSystem; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; +import android.media.AudioProfile; import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager.TvInputCallback; import android.util.Slog; @@ -55,6 +58,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; /** * Represent a logical device of type TV residing in Android system. @@ -813,12 +817,23 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled); boolean oldStatus = mArcEstablished; - // 1. Enable/disable ARC circuit. - enableAudioReturnChannel(enabled); - // 2. Notify arc status to audio service. - notifyArcStatusToAudioService(enabled); - // 3. Update arc status; - mArcEstablished = enabled; + if (enabled) { + RequestSadAction action = new RequestSadAction( + this, Constants.ADDR_AUDIO_SYSTEM, + new RequestSadAction.RequestSadCallback() { + @Override + public void onRequestSadDone(List supportedSads) { + enableAudioReturnChannel(enabled); + notifyArcStatusToAudioService(enabled, supportedSads); + mArcEstablished = enabled; + } + }); + addAndStartAction(action); + } else { + enableAudioReturnChannel(enabled); + notifyArcStatusToAudioService(enabled, new ArrayList<>()); + mArcEstablished = enabled; + } return oldStatus; } @@ -840,11 +855,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return mService.isConnected(portId); } - private void notifyArcStatusToAudioService(boolean enabled) { + private void notifyArcStatusToAudioService(boolean enabled, List supportedSads) { // Note that we don't set any name to ARC. - mService.getAudioManager().setWiredDeviceConnectionState( - AudioSystem.DEVICE_OUT_HDMI_ARC, - enabled ? 1 : 0, "", ""); + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_ARC, "", "", + new ArrayList(), supportedSads.stream() + .map(sad -> new AudioDescriptor(AudioDescriptor.STANDARD_EDID, + AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, sad)) + .collect(Collectors.toList())); + mService.getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0); } /** diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index a260a6d349207..0ab5cb54b5d72 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -62,6 +62,23 @@ import java.util.concurrent.TimeUnit; public class HdmiCecLocalDeviceTvTest { private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; + private static final String[] SADS_NOT_TO_QUERY = new String[]{ + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX}; + private static final HdmiCecMessage SAD_QUERY = + HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(ADDR_TV, ADDR_AUDIO_SYSTEM, + new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD, + Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2}); + private HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; @@ -137,6 +154,10 @@ public class HdmiCecLocalDeviceTvTest { mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress); mTestLooper.dispatchAll(); mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(); + for (String sad : SADS_NOT_TO_QUERY) { + mHdmiControlService.getHdmiCecConfig().setIntValue( + sad, HdmiControlManager.QUERY_SAD_DISABLED); + } mNativeWrapper.clearResultMessages(); } @@ -431,6 +452,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY); } @Test @@ -452,6 +474,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY); } @Test @@ -474,6 +497,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY); } @Test