/* * Copyright (C) 2019 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.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; /** * @hide * Class to represent the attributes of an audio device: its type (speaker, headset...), address * (if known) and role (input, output). *

Unlike {@link AudioDeviceInfo}, the device * doesn't need to be connected to be uniquely identified, it can * for instance represent a specific A2DP headset even after a * disconnection, whereas the corresponding AudioDeviceInfo * would then be invalid. *

While creating / obtaining an instance is not protected by a * permission, APIs using one rely on MODIFY_AUDIO_ROUTING. */ @SystemApi public final class AudioDeviceAttributes implements Parcelable { /** * A role identifying input devices, such as microphones. */ public static final int ROLE_INPUT = AudioPort.ROLE_SOURCE; /** * A role identifying output devices, such as speakers or headphones. */ public static final int ROLE_OUTPUT = AudioPort.ROLE_SINK; /** @hide */ @IntDef(flag = false, prefix = "ROLE_", value = { ROLE_INPUT, ROLE_OUTPUT } ) @Retention(RetentionPolicy.SOURCE) public @interface Role {} /** * The audio device type, as defined in {@link AudioDeviceInfo} */ private final @AudioDeviceInfo.AudioDeviceType int mType; /** * The unique address of the device. Some devices don't have addresses, only an empty string. */ private @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 * Constructor from a valid {@link AudioDeviceInfo} * @param deviceInfo the connected audio device from which to obtain the device-identifying * type and address. */ @SystemApi public AudioDeviceAttributes(@NonNull AudioDeviceInfo deviceInfo) { Objects.requireNonNull(deviceInfo); 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(); } /** * @hide * Constructor from role, device type and address * @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 */ @SystemApi public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, @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); } if (role == ROLE_OUTPUT) { AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type); mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type); } else if (role == ROLE_INPUT) { AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type); mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type, address); } else { mNativeType = AudioSystem.DEVICE_NONE; } mRole = role; mType = type; mAddress = address; mName = name; mAudioProfiles = profiles; mAudioDescriptors = descriptors; } /** * @hide * 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) { 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 = AudioSystem.isInputDevice(nativeType) ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; mName = name; mNativeType = nativeType; mAudioProfiles = new ArrayList<>(); mAudioDescriptors = new ArrayList<>(); } /** * @hide * Copy Constructor. * @param ada the copied AudioDeviceAttributes */ public AudioDeviceAttributes(AudioDeviceAttributes ada) { mRole = ada.getRole(); mType = ada.getType(); mAddress = ada.getAddress(); mName = ada.getName(); mNativeType = ada.getInternalType(); mAudioProfiles = ada.getAudioProfiles(); mAudioDescriptors = ada.getAudioDescriptors(); } /** * @hide * Returns the role of a device * @return the role */ @SystemApi public @Role int getRole() { return mRole; } /** * @hide * Returns the audio device type of a device * @return the type, as defined in {@link AudioDeviceInfo} */ @SystemApi public @AudioDeviceInfo.AudioDeviceType int getType() { return mType; } /** * @hide * Returns the address of the audio device, or an empty string for devices without one * @return the device address */ @SystemApi public @NonNull String getAddress() { return mAddress; } /** * @hide * Sets the device address. Only used by audio service. */ public void setAddress(@NonNull String address) { Objects.requireNonNull(address); mAddress = address; } /** * @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 * @return the internal device type */ public int getInternalType() { 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, mName, mAudioProfiles, mAudioDescriptors); } @Override public boolean equals(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) && 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) && mAddress.equals(that.mAddress)); } /** @hide */ public static String roleToString(@Role int role) { return (role == ROLE_OUTPUT ? "output" : "input"); } @Override public String toString() { return new String("AudioDeviceAttributes:" + " role:" + roleToString(mRole) + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType) : AudioSystem.getInputDeviceName(mNativeType)) + " addr:" + Utils.anonymizeBluetoothAddress(mNativeType, mAddress) + " name:" + mName + " profiles:" + mAudioProfiles.toString() + " descriptors:" + mAudioDescriptors.toString()); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { 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 = new Parcelable.Creator() { /** * Rebuilds an AudioDeviceAttributes previously stored with writeToParcel(). * @param p Parcel object to read the AudioDeviceAttributes from * @return a new AudioDeviceAttributes created from the data in the parcel */ public AudioDeviceAttributes createFromParcel(Parcel p) { return new AudioDeviceAttributes(p); } public AudioDeviceAttributes[] newArray(int size) { return new AudioDeviceAttributes[size]; } }; }