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 17package com.android.server.usb; 18 19import android.annotation.NonNull; 20import android.media.AudioSystem; 21import android.media.IAudioService; 22import android.os.RemoteException; 23import android.service.usb.UsbAlsaDeviceProto; 24import android.util.Slog; 25 26import com.android.internal.util.dump.DualDumpOutputStream; 27import com.android.server.audio.AudioService; 28 29/** 30 * Represents the ALSA specification, and attributes of an ALSA device. 31 */ 32public final class UsbAlsaDevice { 33 private static final String TAG = "UsbAlsaDevice"; 34 protected static final boolean DEBUG = false; 35 36 private final int mCardNum; 37 private final int mDeviceNum; 38 private final String mDeviceAddress; 39 private final boolean mHasOutput; 40 private final boolean mHasInput; 41 42 private final boolean mIsInputHeadset; 43 private final boolean mIsOutputHeadset; 44 45 private boolean mSelected = false; 46 private int mOutputState; 47 private int mInputState; 48 private UsbAlsaJackDetector mJackDetector; 49 private IAudioService mAudioService; 50 51 private String mDeviceName = ""; 52 private String mDeviceDescription = ""; 53 54 public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress, 55 boolean hasOutput, boolean hasInput, 56 boolean isInputHeadset, boolean isOutputHeadset) { 57 mAudioService = audioService; 58 mCardNum = card; 59 mDeviceNum = device; 60 mDeviceAddress = deviceAddress; 61 mHasOutput = hasOutput; 62 mHasInput = hasInput; 63 mIsInputHeadset = isInputHeadset; 64 mIsOutputHeadset = isOutputHeadset; 65 } 66 67 /** 68 * @returns the ALSA card number associated with this peripheral. 69 */ 70 public int getCardNum() { 71 return mCardNum; 72 } 73 74 /** 75 * @returns the ALSA device number associated with this peripheral. 76 */ 77 public int getDeviceNum() { 78 return mDeviceNum; 79 } 80 81 /** 82 * @returns the USB device device address associated with this peripheral. 83 */ 84 public String getDeviceAddress() { 85 return mDeviceAddress; 86 } 87 88 /** 89 * @returns the ALSA card/device address string. 90 */ 91 public String getAlsaCardDeviceString() { 92 if (mCardNum < 0 || mDeviceNum < 0) { 93 Slog.e(TAG, "Invalid alsa card or device alsaCard: " + mCardNum 94 + " alsaDevice: " + mDeviceNum); 95 return null; 96 } 97 return AudioService.makeAlsaAddressString(mCardNum, mDeviceNum); 98 } 99 100 /** 101 * @returns true if the device supports output. 102 */ 103 public boolean hasOutput() { 104 return mHasOutput; 105 } 106 107 /** 108 * @returns true if the device supports input (recording). 109 */ 110 public boolean hasInput() { 111 return mHasInput; 112 } 113 114 /** 115 * @returns true if the device is a headset for purposes of input. 116 */ 117 public boolean isInputHeadset() { 118 return mIsInputHeadset; 119 } 120 121 /** 122 * @returns true if the device is a headset for purposes of output. 123 */ 124 public boolean isOutputHeadset() { 125 return mIsOutputHeadset; 126 } 127 128 /** 129 * @returns true if input jack is detected or jack detection is not supported. 130 */ 131 private synchronized boolean isInputJackConnected() { 132 if (mJackDetector == null) { 133 return true; // If jack detect isn't supported, say it's connected. 134 } 135 return mJackDetector.isInputJackConnected(); 136 } 137 138 /** 139 * @returns true if input jack is detected or jack detection is not supported. 140 */ 141 private synchronized boolean isOutputJackConnected() { 142 if (mJackDetector == null) { 143 return true; // if jack detect isn't supported, say it's connected. 144 } 145 return mJackDetector.isOutputJackConnected(); 146 } 147 148 /** Begins a jack-detection thread. */ 149 private synchronized void startJackDetect() { 150 // If no jack detect capabilities exist, mJackDetector will be null. 151 mJackDetector = UsbAlsaJackDetector.startJackDetect(this); 152 } 153 154 /** Stops a jack-detection thread. */ 155 private synchronized void stopJackDetect() { 156 if (mJackDetector != null) { 157 mJackDetector.pleaseStop(); 158 } 159 mJackDetector = null; 160 } 161 162 /** Start using this device as the selected USB Audio Device. */ 163 public synchronized void start() { 164 mSelected = true; 165 mInputState = 0; 166 mOutputState = 0; 167 startJackDetect(); 168 updateWiredDeviceConnectionState(true); 169 } 170 171 /** Stop using this device as the selected USB Audio Device. */ 172 public synchronized void stop() { 173 stopJackDetect(); 174 updateWiredDeviceConnectionState(false); 175 mSelected = false; 176 } 177 178 /** Updates AudioService with the connection state of the alsaDevice. 179 * Checks ALSA Jack state for inputs and outputs before reporting. 180 */ 181 public synchronized void updateWiredDeviceConnectionState(boolean enable) { 182 if (!mSelected) { 183 Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!"); 184 return; 185 } 186 String alsaCardDeviceString = getAlsaCardDeviceString(); 187 if (alsaCardDeviceString == null) { 188 return; 189 } 190 try { 191 // Output Device 192 if (mHasOutput) { 193 int device = mIsOutputHeadset 194 ? AudioSystem.DEVICE_OUT_USB_HEADSET 195 : AudioSystem.DEVICE_OUT_USB_DEVICE; 196 if (DEBUG) { 197 Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device) 198 + " addr:" + alsaCardDeviceString 199 + " name:" + mDeviceName); 200 } 201 boolean connected = isOutputJackConnected(); 202 Slog.i(TAG, "OUTPUT JACK connected: " + connected); 203 int outputState = (enable && connected) ? 1 : 0; 204 if (outputState != mOutputState) { 205 mOutputState = outputState; 206 mAudioService.setWiredDeviceConnectionState(device, outputState, 207 alsaCardDeviceString, 208 mDeviceName, TAG); 209 } 210 } 211 212 // Input Device 213 if (mHasInput) { 214 int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET 215 : AudioSystem.DEVICE_IN_USB_DEVICE; 216 boolean connected = isInputJackConnected(); 217 Slog.i(TAG, "INPUT JACK connected: " + connected); 218 int inputState = (enable && connected) ? 1 : 0; 219 if (inputState != mInputState) { 220 mInputState = inputState; 221 mAudioService.setWiredDeviceConnectionState( 222 device, inputState, alsaCardDeviceString, 223 mDeviceName, TAG); 224 } 225 } 226 } catch (RemoteException e) { 227 Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState"); 228 } 229 } 230 231 232 /** 233 * @Override 234 * @returns a string representation of the object. 235 */ 236 public synchronized String toString() { 237 return "UsbAlsaDevice: [card: " + mCardNum 238 + ", device: " + mDeviceNum 239 + ", name: " + mDeviceName 240 + ", hasOutput: " + mHasOutput 241 + ", hasInput: " + mHasInput + "]"; 242 } 243 244 /** 245 * Write a description of the device to a dump stream. 246 */ 247 public synchronized void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { 248 long token = dump.start(idName, id); 249 250 dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum); 251 dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum); 252 dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName); 253 dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput); 254 dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput); 255 dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress); 256 257 dump.end(token); 258 } 259 260 // called by logDevices 261 synchronized String toShortString() { 262 return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]"; 263 } 264 265 synchronized String getDeviceName() { 266 return mDeviceName; 267 } 268 269 synchronized void setDeviceNameAndDescription(String deviceName, String deviceDescription) { 270 mDeviceName = deviceName; 271 mDeviceDescription = deviceDescription; 272 } 273 274 /** 275 * @Override 276 * @returns true if the objects are equivalent. 277 */ 278 public boolean equals(Object obj) { 279 if (!(obj instanceof UsbAlsaDevice)) { 280 return false; 281 } 282 UsbAlsaDevice other = (UsbAlsaDevice) obj; 283 return (mCardNum == other.mCardNum 284 && mDeviceNum == other.mDeviceNum 285 && mHasOutput == other.mHasOutput 286 && mHasInput == other.mHasInput 287 && mIsInputHeadset == other.mIsInputHeadset 288 && mIsOutputHeadset == other.mIsOutputHeadset); 289 } 290 291 /** 292 * @Override 293 * @returns a hash code generated from the object contents. 294 */ 295 public int hashCode() { 296 final int prime = 31; 297 int result = 1; 298 result = prime * result + mCardNum; 299 result = prime * result + mDeviceNum; 300 result = prime * result + (mHasOutput ? 0 : 1); 301 result = prime * result + (mHasInput ? 0 : 1); 302 result = prime * result + (mIsInputHeadset ? 0 : 1); 303 result = prime * result + (mIsOutputHeadset ? 0 : 1); 304 305 return result; 306 } 307} 308 309