1 /* 2 * Copyright (C) 2014 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.NonNull; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.os.Build; 22 23 import com.android.aconfig.annotations.VisibleForTesting; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 29 /** 30 * The AudioDevicePort is a specialized type of AudioPort 31 * describing an input (e.g microphone) or output device (e.g speaker) 32 * of the system. 33 * An AudioDevicePort is an AudioPort controlled by the audio HAL, almost always a physical 34 * device at the boundary of the audio system. 35 * In addition to base audio port attributes, the device descriptor contains: 36 * - the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) 37 * - the device address (e.g MAC address for AD2P sink). 38 * @see AudioPort 39 * @hide 40 */ 41 42 public class AudioDevicePort extends AudioPort { 43 44 /** @hide */ 45 // TODO: b/316864909 - Remove this method once there's a way to fake audio device ports further 46 // down the stack. 47 @VisibleForTesting createForTesting( int type, @NonNull String name, @NonNull String address)48 public static AudioDevicePort createForTesting( 49 int type, @NonNull String name, @NonNull String address) { 50 return new AudioDevicePort( 51 new AudioHandle(/* id= */ 0), 52 name, 53 /* samplingRates= */ null, 54 /* channelMasks= */ null, 55 /* channelIndexMasks= */ null, 56 /* formats= */ null, 57 /* gains= */ null, 58 type, 59 address, 60 /* encapsulationModes= */ null, 61 /* encapsulationMetadataTypes= */ null); 62 } 63 64 /** @hide */ 65 // TODO: b/316864909 - Remove this method once there's a way to fake audio device ports further 66 // down the stack. 67 @VisibleForTesting createForTesting(int speakerLayoutChannelMask)68 public static AudioDevicePort createForTesting(int speakerLayoutChannelMask) { 69 return new AudioDevicePort( 70 new AudioHandle(/* id= */ 0), 71 /* name= */ "testAudioDevicePort", 72 /* profiles= */ new ArrayList<>(), 73 /* gains= */ null, 74 /* type= */ AudioManager.DEVICE_OUT_SPEAKER, 75 /* address= */ "testAddress", 76 /* speakerLayoutChannelMask= */ speakerLayoutChannelMask, 77 /* encapsulationModes= */ null, 78 /* encapsulationMetadataTypes= */ null, 79 /* descriptors= */ new ArrayList<>()); 80 } 81 82 private final int mType; 83 private final String mAddress; 84 private final int mSpeakerLayoutChannelMask; 85 private final int[] mEncapsulationModes; 86 private final int[] mEncapsulationMetadataTypes; 87 88 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) AudioDevicePort(AudioHandle handle, String deviceName, int[] samplingRates, int[] channelMasks, int[] channelIndexMasks, int[] formats, AudioGain[] gains, int type, String address, int[] encapsulationModes, @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes)89 AudioDevicePort(AudioHandle handle, String deviceName, 90 int[] samplingRates, int[] channelMasks, int[] channelIndexMasks, 91 int[] formats, AudioGain[] gains, int type, String address, int[] encapsulationModes, 92 @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes) { 93 super(handle, 94 (AudioManager.isInputDevice(type) == true) ? 95 AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK, 96 deviceName, samplingRates, channelMasks, channelIndexMasks, formats, gains); 97 mType = type; 98 mAddress = address; 99 mSpeakerLayoutChannelMask = AudioFormat.CHANNEL_INVALID; 100 mEncapsulationModes = encapsulationModes; 101 mEncapsulationMetadataTypes = encapsulationMetadataTypes; 102 } 103 AudioDevicePort( AudioHandle handle, String deviceName, List<AudioProfile> profiles, AudioGain[] gains, int type, String address, int speakerLayoutChannelMask, int[] encapsulationModes, @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes, List<AudioDescriptor> descriptors)104 AudioDevicePort( 105 AudioHandle handle, 106 String deviceName, 107 List<AudioProfile> profiles, 108 AudioGain[] gains, 109 int type, 110 String address, 111 int speakerLayoutChannelMask, 112 int[] encapsulationModes, 113 @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes, 114 List<AudioDescriptor> descriptors) { 115 super(handle, 116 AudioManager.isInputDevice(type) ? AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK, 117 deviceName, profiles, gains, descriptors); 118 mType = type; 119 mAddress = address; 120 mSpeakerLayoutChannelMask = speakerLayoutChannelMask; 121 mEncapsulationModes = encapsulationModes; 122 mEncapsulationMetadataTypes = encapsulationMetadataTypes; 123 } 124 125 /** 126 * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) 127 */ 128 @UnsupportedAppUsage type()129 public int type() { 130 return mType; 131 } 132 133 /** 134 * Get the device address. Address format varies with the device type. 135 * - USB devices ({@link AudioManager#DEVICE_OUT_USB_DEVICE}, 136 * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number 137 * and device number: "card=2;device=1" 138 * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, 139 * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, 140 * {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP}), 141 * {@link AudioManager#DEVICE_OUT_BLE_HEADSET}, {@link AudioManager#DEVICE_OUT_BLE_SPEAKER}) 142 * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by 143 * {@link BluetoothDevice#getAddress()}. 144 * - Bluetooth LE broadcast group ({@link AudioManager#DEVICE_OUT_BLE_BROADCAST} use the group number. 145 * - Devices that do not have an address will indicate an empty string "". 146 */ address()147 public String address() { 148 return mAddress; 149 } 150 151 /** Get the channel mask representing the physical output speaker layout of the device. 152 * 153 * The layout channel mask only indicates which speaker channels are present, the 154 * physical layout of the speakers should be informed by a standard for multi-channel 155 * sound playback systems, such as ITU-R BS.2051. 156 */ speakerLayoutChannelMask()157 public int speakerLayoutChannelMask() { 158 return mSpeakerLayoutChannelMask; 159 } 160 161 /** 162 * Get supported encapsulation modes. 163 */ encapsulationModes()164 public @NonNull @AudioTrack.EncapsulationMode int[] encapsulationModes() { 165 if (mEncapsulationModes == null) { 166 return new int[0]; 167 } 168 return Arrays.stream(mEncapsulationModes).boxed() 169 .filter(mode -> mode != AudioTrack.ENCAPSULATION_MODE_HANDLE) 170 .mapToInt(Integer::intValue).toArray(); 171 } 172 173 /** 174 * Get supported encapsulation metadata types. 175 */ encapsulationMetadataTypes()176 public @NonNull @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes() { 177 if (mEncapsulationMetadataTypes == null) { 178 return new int[0]; 179 } 180 int[] encapsulationMetadataTypes = new int[mEncapsulationMetadataTypes.length]; 181 System.arraycopy(mEncapsulationMetadataTypes, 0, 182 encapsulationMetadataTypes, 0, mEncapsulationMetadataTypes.length); 183 return encapsulationMetadataTypes; 184 } 185 186 /** 187 * Build a specific configuration of this audio device port for use by methods 188 * like AudioManager.connectAudioPatch(). 189 */ buildConfig(int samplingRate, int channelMask, int format, AudioGainConfig gain)190 public AudioDevicePortConfig buildConfig(int samplingRate, int channelMask, int format, 191 AudioGainConfig gain) { 192 return new AudioDevicePortConfig(this, samplingRate, channelMask, format, gain); 193 } 194 195 @Override equals(Object o)196 public boolean equals(Object o) { 197 if (o == null || !(o instanceof AudioDevicePort)) { 198 return false; 199 } 200 AudioDevicePort other = (AudioDevicePort)o; 201 if (mType != other.type()) { 202 return false; 203 } 204 if (mAddress == null && other.address() != null) { 205 return false; 206 } 207 if (!mAddress.equals(other.address())) { 208 return false; 209 } 210 return super.equals(o); 211 } 212 213 /** 214 * Returns true if the AudioDevicePort passed as argument represents the same device (same 215 * type and same address). This is different from equals() in that the port IDs are not compared 216 * which allows matching devices across native audio server restarts. 217 * @param other the other audio device port to compare to. 218 * @return true if both device port correspond to the same audio device, false otherwise. 219 * @hide 220 */ isSameAs(AudioDevicePort other)221 public boolean isSameAs(AudioDevicePort other) { 222 if (mType != other.type()) { 223 return false; 224 } 225 if (mAddress == null && other.address() != null) { 226 return false; 227 } 228 if (!mAddress.equals(other.address())) { 229 return false; 230 } 231 return true; 232 } 233 234 @Override toString()235 public String toString() { 236 String type = (mRole == ROLE_SOURCE ? 237 AudioSystem.getInputDeviceName(mType) : 238 AudioSystem.getOutputDeviceName(mType)); 239 return "{" + super.toString() 240 + ", mType: " + type 241 + ", mAddress: " + Utils.anonymizeBluetoothAddress(mType, mAddress) 242 + "}"; 243 } 244 } 245