blob: 9d4db003a297854b08807fecafadb68b1deeea24 [file] [log] [blame]
/*
* Copyright (C) 2014 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 com.android.server.usb;
import android.annotation.NonNull;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.os.RemoteException;
import android.service.usb.UsbAlsaDeviceProto;
import android.util.Slog;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.audio.AudioService;
/**
* Represents the ALSA specification, and attributes of an ALSA device.
*/
public final class UsbAlsaDevice {
private static final String TAG = "UsbAlsaDevice";
protected static final boolean DEBUG = false;
private final int mCardNum;
private final int mDeviceNum;
private final String mDeviceAddress;
private final boolean mHasOutput;
private final boolean mHasInput;
private final boolean mIsInputHeadset;
private final boolean mIsOutputHeadset;
private boolean mSelected = false;
private int mOutputState;
private int mInputState;
private UsbAlsaJackDetector mJackDetector;
private IAudioService mAudioService;
private String mDeviceName = "";
private String mDeviceDescription = "";
public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
boolean hasOutput, boolean hasInput,
boolean isInputHeadset, boolean isOutputHeadset) {
mAudioService = audioService;
mCardNum = card;
mDeviceNum = device;
mDeviceAddress = deviceAddress;
mHasOutput = hasOutput;
mHasInput = hasInput;
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
}
/**
* @returns the ALSA card number associated with this peripheral.
*/
public int getCardNum() {
return mCardNum;
}
/**
* @returns the ALSA device number associated with this peripheral.
*/
public int getDeviceNum() {
return mDeviceNum;
}
/**
* @returns the USB device device address associated with this peripheral.
*/
public String getDeviceAddress() {
return mDeviceAddress;
}
/**
* @returns the ALSA card/device address string.
*/
public String getAlsaCardDeviceString() {
if (mCardNum < 0 || mDeviceNum < 0) {
Slog.e(TAG, "Invalid alsa card or device alsaCard: " + mCardNum
+ " alsaDevice: " + mDeviceNum);
return null;
}
return AudioService.makeAlsaAddressString(mCardNum, mDeviceNum);
}
/**
* @returns true if the device supports output.
*/
public boolean hasOutput() {
return mHasOutput;
}
/**
* @returns true if the device supports input (recording).
*/
public boolean hasInput() {
return mHasInput;
}
/**
* @returns true if the device is a headset for purposes of input.
*/
public boolean isInputHeadset() {
return mIsInputHeadset;
}
/**
* @returns true if the device is a headset for purposes of output.
*/
public boolean isOutputHeadset() {
return mIsOutputHeadset;
}
/**
* @returns true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isInputJackConnected() {
if (mJackDetector == null) {
return true; // If jack detect isn't supported, say it's connected.
}
return mJackDetector.isInputJackConnected();
}
/**
* @returns true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isOutputJackConnected() {
if (mJackDetector == null) {
return true; // if jack detect isn't supported, say it's connected.
}
return mJackDetector.isOutputJackConnected();
}
/** Begins a jack-detection thread. */
private synchronized void startJackDetect() {
// If no jack detect capabilities exist, mJackDetector will be null.
mJackDetector = UsbAlsaJackDetector.startJackDetect(this);
}
/** Stops a jack-detection thread. */
private synchronized void stopJackDetect() {
if (mJackDetector != null) {
mJackDetector.pleaseStop();
}
mJackDetector = null;
}
/** Start using this device as the selected USB Audio Device. */
public synchronized void start() {
mSelected = true;
mInputState = 0;
mOutputState = 0;
startJackDetect();
updateWiredDeviceConnectionState(true);
}
/** Stop using this device as the selected USB Audio Device. */
public synchronized void stop() {
stopJackDetect();
updateWiredDeviceConnectionState(false);
mSelected = false;
}
/** Updates AudioService with the connection state of the alsaDevice.
* Checks ALSA Jack state for inputs and outputs before reporting.
*/
public synchronized void updateWiredDeviceConnectionState(boolean enable) {
if (!mSelected) {
Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!");
return;
}
String alsaCardDeviceString = getAlsaCardDeviceString();
if (alsaCardDeviceString == null) {
return;
}
try {
// Output Device
if (mHasOutput) {
int device = mIsOutputHeadset
? AudioSystem.DEVICE_OUT_USB_HEADSET
: AudioSystem.DEVICE_OUT_USB_DEVICE;
if (DEBUG) {
Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
+ " addr:" + alsaCardDeviceString
+ " name:" + mDeviceName);
}
boolean connected = isOutputJackConnected();
Slog.i(TAG, "OUTPUT JACK connected: " + connected);
int outputState = (enable && connected) ? 1 : 0;
if (outputState != mOutputState) {
mOutputState = outputState;
mAudioService.setWiredDeviceConnectionState(device, outputState,
alsaCardDeviceString,
mDeviceName, TAG);
}
}
// Input Device
if (mHasInput) {
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);
}
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
}
}
/**
* @Override
* @returns a string representation of the object.
*/
public synchronized String toString() {
return "UsbAlsaDevice: [card: " + mCardNum
+ ", device: " + mDeviceNum
+ ", name: " + mDeviceName
+ ", hasOutput: " + mHasOutput
+ ", hasInput: " + mHasInput + "]";
}
/**
* Write a description of the device to a dump stream.
*/
public synchronized void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
long token = dump.start(idName, id);
dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum);
dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum);
dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName);
dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput);
dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput);
dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress);
dump.end(token);
}
// called by logDevices
synchronized String toShortString() {
return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]";
}
synchronized String getDeviceName() {
return mDeviceName;
}
synchronized void setDeviceNameAndDescription(String deviceName, String deviceDescription) {
mDeviceName = deviceName;
mDeviceDescription = deviceDescription;
}
/**
* @Override
* @returns true if the objects are equivalent.
*/
public boolean equals(Object obj) {
if (!(obj instanceof UsbAlsaDevice)) {
return false;
}
UsbAlsaDevice other = (UsbAlsaDevice) obj;
return (mCardNum == other.mCardNum
&& mDeviceNum == other.mDeviceNum
&& mHasOutput == other.mHasOutput
&& mHasInput == other.mHasInput
&& mIsInputHeadset == other.mIsInputHeadset
&& mIsOutputHeadset == other.mIsOutputHeadset);
}
/**
* @Override
* @returns a hash code generated from the object contents.
*/
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mCardNum;
result = prime * result + mDeviceNum;
result = prime * result + (mHasOutput ? 0 : 1);
result = prime * result + (mHasInput ? 0 : 1);
result = prime * result + (mIsInputHeadset ? 0 : 1);
result = prime * result + (mIsOutputHeadset ? 0 : 1);
return result;
}
}