1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.List; 31 import java.util.Objects; 32 33 /** 34 * @hide 35 * Class to represent the attributes of an audio device: its type (speaker, headset...), address 36 * (if known) and role (input, output). 37 * <p>Unlike {@link AudioDeviceInfo}, the device 38 * doesn't need to be connected to be uniquely identified, it can 39 * for instance represent a specific A2DP headset even after a 40 * disconnection, whereas the corresponding <code>AudioDeviceInfo</code> 41 * would then be invalid. 42 * <p>While creating / obtaining an instance is not protected by a 43 * permission, APIs using one rely on MODIFY_AUDIO_ROUTING. 44 */ 45 @SystemApi 46 public final class AudioDeviceAttributes implements Parcelable { 47 48 /** 49 * A role identifying input devices, such as microphones. 50 */ 51 public static final int ROLE_INPUT = AudioPort.ROLE_SOURCE; 52 /** 53 * A role identifying output devices, such as speakers or headphones. 54 */ 55 public static final int ROLE_OUTPUT = AudioPort.ROLE_SINK; 56 57 /** @hide */ 58 @IntDef(flag = false, prefix = "ROLE_", value = { 59 ROLE_INPUT, ROLE_OUTPUT } 60 ) 61 @Retention(RetentionPolicy.SOURCE) 62 public @interface Role {} 63 64 /** 65 * The audio device type, as defined in {@link AudioDeviceInfo} 66 */ 67 private final @AudioDeviceInfo.AudioDeviceType int mType; 68 /** 69 * The unique address of the device. Some devices don't have addresses, only an empty string. 70 */ 71 private @NonNull String mAddress; 72 /** 73 * The non-unique name of the device. Some devices don't have names, only an empty string. 74 * Should not be used as a unique identifier for a device. 75 */ 76 private final @NonNull String mName; 77 /** 78 * Is input or output device 79 */ 80 private final @Role int mRole; 81 /** 82 * The internal audio device type 83 */ 84 private final int mNativeType; 85 /** 86 * List of AudioProfiles supported by the device 87 */ 88 private final @NonNull List<AudioProfile> mAudioProfiles; 89 /** 90 * List of AudioDescriptors supported by the device 91 */ 92 private final @NonNull List<AudioDescriptor> mAudioDescriptors; 93 94 /** 95 * @hide 96 * Constructor from a valid {@link AudioDeviceInfo} 97 * @param deviceInfo the connected audio device from which to obtain the device-identifying 98 * type and address. 99 */ 100 @SystemApi AudioDeviceAttributes(@onNull AudioDeviceInfo deviceInfo)101 public AudioDeviceAttributes(@NonNull AudioDeviceInfo deviceInfo) { 102 Objects.requireNonNull(deviceInfo); 103 mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT; 104 mType = deviceInfo.getType(); 105 mAddress = deviceInfo.getAddress(); 106 mName = String.valueOf(deviceInfo.getProductName()); 107 mNativeType = deviceInfo.getInternalType(); 108 mAudioProfiles = deviceInfo.getAudioProfiles(); 109 mAudioDescriptors = deviceInfo.getAudioDescriptors(); 110 } 111 112 /** 113 * @hide 114 * Constructor from role, device type and address 115 * @param role indicates input or output role 116 * @param type the device type, as defined in {@link AudioDeviceInfo} 117 * @param address the address of the device, or an empty string for devices without one 118 */ 119 @SystemApi AudioDeviceAttributes(@ole int role, @AudioDeviceInfo.AudioDeviceType int type, @NonNull String address)120 public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, 121 @NonNull String address) { 122 this(role, type, address, "", new ArrayList<>(), new ArrayList<>()); 123 } 124 125 /** 126 * @hide 127 * Constructor with specification of all attributes 128 * @param role indicates input or output role 129 * @param type the device type, as defined in {@link AudioDeviceInfo} 130 * @param address the address of the device, or an empty string for devices without one 131 * @param name the name of the device, or an empty string for devices without one 132 * @param profiles the list of AudioProfiles supported by the device 133 * @param descriptors the list of AudioDescriptors supported by the device 134 */ 135 @SystemApi AudioDeviceAttributes(@ole int role, @AudioDeviceInfo.AudioDeviceType int type, @NonNull String address, @NonNull String name, @NonNull List<AudioProfile> profiles, @NonNull List<AudioDescriptor> descriptors)136 public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, 137 @NonNull String address, @NonNull String name, @NonNull List<AudioProfile> profiles, 138 @NonNull List<AudioDescriptor> descriptors) { 139 Objects.requireNonNull(address); 140 if (role != ROLE_OUTPUT && role != ROLE_INPUT) { 141 throw new IllegalArgumentException("Invalid role " + role); 142 } 143 if (role == ROLE_OUTPUT) { 144 AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type); 145 mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type); 146 } else if (role == ROLE_INPUT) { 147 AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type); 148 mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type, address); 149 } else { 150 mNativeType = AudioSystem.DEVICE_NONE; 151 } 152 153 mRole = role; 154 mType = type; 155 mAddress = address; 156 mName = name; 157 mAudioProfiles = profiles; 158 mAudioDescriptors = descriptors; 159 } 160 161 /** 162 * @hide 163 * Constructor called from AudioSystem JNI when creating an AudioDeviceAttributes from a native 164 * AudioDeviceTypeAddr instance. 165 * @param nativeType the internal device type, as defined in {@link AudioSystem} 166 * @param address the address of the device, or an empty string for devices without one 167 */ AudioDeviceAttributes(int nativeType, @NonNull String address)168 public AudioDeviceAttributes(int nativeType, @NonNull String address) { 169 this(nativeType, address, ""); 170 } 171 172 /** 173 * @hide 174 * Constructor called from BtHelper to connect or disconnect a Bluetooth device. 175 * @param nativeType the internal device type, as defined in {@link AudioSystem} 176 * @param address the address of the device, or an empty string for devices without one 177 * @param name the name of the device, or an empty string for devices without one 178 */ AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull String name)179 public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull String name) { 180 mRole = AudioSystem.isInputDevice(nativeType) ? ROLE_INPUT : ROLE_OUTPUT; 181 mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); 182 mAddress = address; 183 mName = name; 184 mNativeType = nativeType; 185 mAudioProfiles = new ArrayList<>(); 186 mAudioDescriptors = new ArrayList<>(); 187 } 188 189 /** 190 * @hide 191 * Copy Constructor. 192 * @param ada the copied AudioDeviceAttributes 193 */ AudioDeviceAttributes(AudioDeviceAttributes ada)194 public AudioDeviceAttributes(AudioDeviceAttributes ada) { 195 mRole = ada.getRole(); 196 mType = ada.getType(); 197 mAddress = ada.getAddress(); 198 mName = ada.getName(); 199 mNativeType = ada.getInternalType(); 200 mAudioProfiles = ada.getAudioProfiles(); 201 mAudioDescriptors = ada.getAudioDescriptors(); 202 } 203 204 /** 205 * @hide 206 * Returns the role of a device 207 * @return the role 208 */ 209 @SystemApi getRole()210 public @Role int getRole() { 211 return mRole; 212 } 213 214 /** 215 * @hide 216 * Returns the audio device type of a device 217 * @return the type, as defined in {@link AudioDeviceInfo} 218 */ 219 @SystemApi getType()220 public @AudioDeviceInfo.AudioDeviceType int getType() { 221 return mType; 222 } 223 224 /** 225 * @hide 226 * Returns the address of the audio device, or an empty string for devices without one 227 * @return the device address 228 */ 229 @SystemApi getAddress()230 public @NonNull String getAddress() { 231 return mAddress; 232 } 233 234 /** 235 * @hide 236 * Sets the device address. Only used by audio service. 237 */ setAddress(@onNull String address)238 public void setAddress(@NonNull String address) { 239 Objects.requireNonNull(address); 240 mAddress = address; 241 } 242 243 /** 244 * @hide 245 * Returns the name of the audio device, or an empty string for devices without one 246 * @return the device name 247 */ 248 @SystemApi getName()249 public @NonNull String getName() { 250 return mName; 251 } 252 253 /** 254 * @hide 255 * Returns the internal device type of a device 256 * @return the internal device type 257 */ getInternalType()258 public int getInternalType() { 259 return mNativeType; 260 } 261 262 /** 263 * @hide 264 * Returns the list of AudioProfiles supported by the device 265 * @return the list of AudioProfiles 266 */ 267 @SystemApi getAudioProfiles()268 public @NonNull List<AudioProfile> getAudioProfiles() { 269 return mAudioProfiles; 270 } 271 272 /** 273 * @hide 274 * Returns the list of AudioDescriptors supported by the device 275 * @return the list of AudioDescriptors 276 */ 277 @SystemApi getAudioDescriptors()278 public @NonNull List<AudioDescriptor> getAudioDescriptors() { 279 return mAudioDescriptors; 280 } 281 282 @Override hashCode()283 public int hashCode() { 284 return Objects.hash(mRole, mType, mAddress, mName, mAudioProfiles, mAudioDescriptors); 285 } 286 287 @Override equals(Object o)288 public boolean equals(Object o) { 289 if (this == o) return true; 290 if (o == null || getClass() != o.getClass()) return false; 291 292 AudioDeviceAttributes that = (AudioDeviceAttributes) o; 293 return ((mRole == that.mRole) 294 && (mType == that.mType) 295 && mAddress.equals(that.mAddress) 296 && mName.equals(that.mName) 297 && mAudioProfiles.equals(that.mAudioProfiles) 298 && mAudioDescriptors.equals(that.mAudioDescriptors)); 299 } 300 301 /** 302 * Returns true if the role, type and address are equal. Called to compare with an 303 * AudioDeviceAttributes that was created from a native AudioDeviceTypeAddr instance. 304 * @param o object to compare with 305 * @return whether role, type and address are equal 306 */ equalTypeAddress(@ullable Object o)307 public boolean equalTypeAddress(@Nullable Object o) { 308 if (this == o) return true; 309 if (o == null || getClass() != o.getClass()) return false; 310 311 AudioDeviceAttributes that = (AudioDeviceAttributes) o; 312 return ((mRole == that.mRole) 313 && (mType == that.mType) 314 && mAddress.equals(that.mAddress)); 315 } 316 317 /** @hide */ roleToString(@ole int role)318 public static String roleToString(@Role int role) { 319 return (role == ROLE_OUTPUT ? "output" : "input"); 320 } 321 322 @Override toString()323 public String toString() { 324 return new String("AudioDeviceAttributes:" 325 + " role:" + roleToString(mRole) 326 + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType) 327 : AudioSystem.getInputDeviceName(mNativeType)) 328 + " addr:" + Utils.anonymizeBluetoothAddress(mNativeType, mAddress) 329 + " name:" + mName 330 + " profiles:" + mAudioProfiles.toString() 331 + " descriptors:" + mAudioDescriptors.toString()); 332 } 333 334 @Override describeContents()335 public int describeContents() { 336 return 0; 337 } 338 339 @Override writeToParcel(@onNull Parcel dest, int flags)340 public void writeToParcel(@NonNull Parcel dest, int flags) { 341 dest.writeInt(mRole); 342 dest.writeInt(mType); 343 dest.writeString(mAddress); 344 dest.writeString(mName); 345 dest.writeInt(mNativeType); 346 dest.writeParcelableArray( 347 mAudioProfiles.toArray(new AudioProfile[mAudioProfiles.size()]), flags); 348 dest.writeParcelableArray( 349 mAudioDescriptors.toArray(new AudioDescriptor[mAudioDescriptors.size()]), flags); 350 } 351 AudioDeviceAttributes(@onNull Parcel in)352 private AudioDeviceAttributes(@NonNull Parcel in) { 353 mRole = in.readInt(); 354 mType = in.readInt(); 355 mAddress = in.readString(); 356 mName = in.readString(); 357 mNativeType = in.readInt(); 358 AudioProfile[] audioProfilesArray = 359 in.readParcelableArray(AudioProfile.class.getClassLoader(), AudioProfile.class); 360 mAudioProfiles = new ArrayList<AudioProfile>(Arrays.asList(audioProfilesArray)); 361 AudioDescriptor[] audioDescriptorsArray = in.readParcelableArray( 362 AudioDescriptor.class.getClassLoader(), AudioDescriptor.class); 363 mAudioDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(audioDescriptorsArray)); 364 } 365 366 public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR = 367 new Parcelable.Creator<AudioDeviceAttributes>() { 368 /** 369 * Rebuilds an AudioDeviceAttributes previously stored with writeToParcel(). 370 * @param p Parcel object to read the AudioDeviceAttributes from 371 * @return a new AudioDeviceAttributes created from the data in the parcel 372 */ 373 public AudioDeviceAttributes createFromParcel(Parcel p) { 374 return new AudioDeviceAttributes(p); 375 } 376 377 public AudioDeviceAttributes[] newArray(int size) { 378 return new AudioDeviceAttributes[size]; 379 } 380 }; 381 } 382