Merge "Update logic for determining if an USB device is a headset or not."
This commit is contained in:
committed by
Android (Google) Code Review
commit
4f5afe3ad7
@@ -22,7 +22,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
|
||||
* An audio class-specific Input Terminal interface.
|
||||
* see audio10.pdf section 4.3.2.1
|
||||
*/
|
||||
public final class Usb10ACInputTerminal extends UsbACTerminal {
|
||||
public final class Usb10ACInputTerminal extends UsbACTerminal implements UsbAudioChannelCluster {
|
||||
private static final String TAG = "Usb10ACInputTerminal";
|
||||
|
||||
private byte mNrChannels; // 7:1 1 Channel (0x01)
|
||||
@@ -36,14 +36,17 @@ public final class Usb10ACInputTerminal extends UsbACTerminal {
|
||||
super(length, type, subtype, subclass);
|
||||
}
|
||||
|
||||
public byte getNrChannels() {
|
||||
@Override
|
||||
public byte getChannelCount() {
|
||||
return mNrChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannelConfig() {
|
||||
return mChannelConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelNames() {
|
||||
return mChannelNames;
|
||||
}
|
||||
@@ -69,9 +72,7 @@ public final class Usb10ACInputTerminal extends UsbACTerminal {
|
||||
super.report(canvas);
|
||||
|
||||
canvas.openList();
|
||||
canvas.writeListItem("Associated Terminal: "
|
||||
+ ReportCanvas.getHexString(getAssocTerminal()));
|
||||
canvas.writeListItem("" + getNrChannels() + " Chans. Config: "
|
||||
canvas.writeListItem("" + getChannelCount() + " Chans. Config: "
|
||||
+ ReportCanvas.getHexString(getChannelConfig()));
|
||||
canvas.closeList();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
|
||||
* An audio class-specific Mixer Interface.
|
||||
* see audio10.pdf section 4.3.2.3
|
||||
*/
|
||||
public final class Usb10ACMixerUnit extends UsbACMixerUnit {
|
||||
public final class Usb10ACMixerUnit extends UsbACMixerUnit implements UsbAudioChannelCluster {
|
||||
private static final String TAG = "Usb10ACMixerUnit";
|
||||
|
||||
private int mChannelConfig; // Spatial location of output channels
|
||||
@@ -34,11 +34,18 @@ public final class Usb10ACMixerUnit extends UsbACMixerUnit {
|
||||
super(length, type, subtype, subClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelCount() {
|
||||
return mNumOutputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannelConfig() {
|
||||
return mChannelConfig;
|
||||
}
|
||||
|
||||
public byte getChanNameID() {
|
||||
@Override
|
||||
public byte getChannelNames() {
|
||||
return mChanNameID;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
|
||||
* An audio class-specific Input Terminal interface.
|
||||
* see Audio20.pdf section 3.13.2 Input Terminal
|
||||
*/
|
||||
public final class Usb20ACInputTerminal extends UsbACTerminal {
|
||||
public final class Usb20ACInputTerminal extends UsbACTerminal implements UsbAudioChannelCluster {
|
||||
private static final String TAG = "Usb20ACInputTerminal";
|
||||
|
||||
// See Audio20.pdf - Table 4-9
|
||||
@@ -47,14 +47,21 @@ public final class Usb20ACInputTerminal extends UsbACTerminal {
|
||||
return mClkSourceID;
|
||||
}
|
||||
|
||||
public byte getNumChannels() {
|
||||
@Override
|
||||
public byte getChannelCount() {
|
||||
return mNumChannels;
|
||||
}
|
||||
|
||||
public int getChanConfig() {
|
||||
@Override
|
||||
public int getChannelConfig() {
|
||||
return mChanConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelNames() {
|
||||
return mChanNames;
|
||||
}
|
||||
|
||||
public int getControls() {
|
||||
return mControls;
|
||||
}
|
||||
@@ -79,8 +86,8 @@ public final class Usb20ACInputTerminal extends UsbACTerminal {
|
||||
|
||||
canvas.openList();
|
||||
canvas.writeListItem("Clock Source: " + getClkSourceID());
|
||||
canvas.writeListItem("" + getNumChannels() + " Channels. Config: "
|
||||
+ ReportCanvas.getHexString(getChanConfig()));
|
||||
canvas.writeListItem("" + getChannelCount() + " Channels. Config: "
|
||||
+ ReportCanvas.getHexString(getChannelConfig()));
|
||||
canvas.closeList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ package com.android.server.usb.descriptors;
|
||||
* An audio class-specific Mixer Unit interface.
|
||||
* see Audio20.pdf section 4.7.2.6 Mixer Unit Descriptor
|
||||
*/
|
||||
public final class Usb20ACMixerUnit extends UsbACMixerUnit {
|
||||
public final class Usb20ACMixerUnit extends UsbACMixerUnit implements UsbAudioChannelCluster {
|
||||
private static final String TAG = "Usb20ACMixerUnit";
|
||||
|
||||
private int mChanConfig; // 6+p:4 Describes the spatial location of the
|
||||
@@ -37,6 +37,21 @@ public final class Usb20ACMixerUnit extends UsbACMixerUnit {
|
||||
super(length, type, subtype, subClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelCount() {
|
||||
return mNumOutputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannelConfig() {
|
||||
return mChanConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelNames() {
|
||||
return mChanNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parseRawDescriptors(ByteStream stream) {
|
||||
super.parseRawDescriptors(stream);
|
||||
|
||||
@@ -71,6 +71,7 @@ public final class Usb20ACOutputTerminal extends UsbACTerminal {
|
||||
super.report(canvas);
|
||||
|
||||
canvas.openList();
|
||||
canvas.writeListItem("Source ID:" + getSourceID());
|
||||
canvas.writeListItem("Clock Source ID: " + getClkSourceID());
|
||||
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
|
||||
canvas.writeListItem("Terminal Name ID: " + getTerminalID());
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
|
||||
* Audio20.pdf - 4.9.2 Class-Specific AS Interface Descriptor
|
||||
* 16 bytes
|
||||
*/
|
||||
public final class Usb20ASGeneral extends UsbACInterface {
|
||||
public final class Usb20ASGeneral extends UsbACInterface implements UsbAudioChannelCluster {
|
||||
private static final String TAG = "Usb20ASGeneral";
|
||||
|
||||
// Audio20.pdf - Table 4-27
|
||||
@@ -61,14 +61,17 @@ public final class Usb20ASGeneral extends UsbACInterface {
|
||||
return mFormats;
|
||||
}
|
||||
|
||||
public byte getNumChannels() {
|
||||
@Override
|
||||
public byte getChannelCount() {
|
||||
return mNumChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannelConfig() {
|
||||
return mChannelConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getChannelNames() {
|
||||
return mChannelNames;
|
||||
}
|
||||
@@ -96,7 +99,7 @@ public final class Usb20ASGeneral extends UsbACInterface {
|
||||
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
|
||||
canvas.writeListItem("Format Type: " + ReportCanvas.getHexString(getFormatType()));
|
||||
canvas.writeListItem("Formats: " + ReportCanvas.getHexString(getFormats()));
|
||||
canvas.writeListItem("Num Channels: " + getNumChannels());
|
||||
canvas.writeListItem("Channel Count: " + getChannelCount());
|
||||
canvas.writeListItem("Channel Config: " + ReportCanvas.getHexString(getChannelConfig()));
|
||||
canvas.writeListItem("Channel Names String ID: " + getChannelNames());
|
||||
canvas.closeList();
|
||||
|
||||
@@ -66,6 +66,8 @@ public abstract class UsbACTerminal extends UsbACInterface {
|
||||
canvas.writeListItem("Type: " + ReportCanvas.getHexString(terminalType) + ": "
|
||||
+ UsbStrings.getTerminalName(terminalType));
|
||||
canvas.writeListItem("ID: " + ReportCanvas.getHexString(getTerminalID()));
|
||||
canvas.writeListItem("Associated terminal: "
|
||||
+ ReportCanvas.getHexString(getAssocTerminal()));
|
||||
canvas.closeList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.descriptors;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Group of logical audio channels that carry tightly related synchronous audio information.
|
||||
* See Audio10.pdf section 3.7.2.3 Audio Channel Cluster format and Audio20.pdf section 3.13.1
|
||||
* audio channel cluster.
|
||||
*/
|
||||
public interface UsbAudioChannelCluster {
|
||||
/**
|
||||
* @return logical channels in the cluster.
|
||||
*/
|
||||
byte getChannelCount();
|
||||
|
||||
/**
|
||||
* @return a bit field that indicates which spatial locations are present in the cluster.
|
||||
*/
|
||||
int getChannelConfig();
|
||||
|
||||
/**
|
||||
* @return index to a string descriptor that describes the spatial location of the first
|
||||
* non-predefined logical channel in the cluster.
|
||||
*/
|
||||
byte getChannelNames();
|
||||
}
|
||||
@@ -832,31 +832,47 @@ public final class UsbDescriptorParser {
|
||||
return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
|
||||
}
|
||||
|
||||
// TODO: Up/Downmix process descriptor is not yet parsed, which may affect the result here.
|
||||
private int getMaximumChannelCount() {
|
||||
int maxChannelCount = 0;
|
||||
for (UsbDescriptor descriptor : mDescriptors) {
|
||||
if (descriptor instanceof UsbAudioChannelCluster) {
|
||||
maxChannelCount = Math.max(maxChannelCount,
|
||||
((UsbAudioChannelCluster) descriptor).getChannelCount());
|
||||
}
|
||||
}
|
||||
return maxChannelCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public float getOutputHeadsetProbability() {
|
||||
public float getOutputHeadsetLikelihood() {
|
||||
if (hasMIDIInterface()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float probability = 0.0f;
|
||||
float likelihood = 0.0f;
|
||||
ArrayList<UsbDescriptor> acDescriptors;
|
||||
|
||||
// Look for a "speaker"
|
||||
boolean hasSpeaker = false;
|
||||
boolean hasAssociatedInputTerminal = false;
|
||||
boolean hasHeadphoneOrHeadset = false;
|
||||
acDescriptors =
|
||||
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
|
||||
UsbACInterface.AUDIO_AUDIOCONTROL);
|
||||
for (UsbDescriptor descriptor : acDescriptors) {
|
||||
if (descriptor instanceof UsbACTerminal) {
|
||||
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
|
||||
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
|
||||
|| outDescr.getTerminalType()
|
||||
== UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
|
||||
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
|
||||
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER) {
|
||||
hasSpeaker = true;
|
||||
break;
|
||||
if (outDescr.getAssocTerminal() != 0x0) {
|
||||
hasAssociatedInputTerminal = true;
|
||||
}
|
||||
} else if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
|
||||
|| outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
|
||||
hasHeadphoneOrHeadset = true;
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
|
||||
@@ -864,15 +880,27 @@ public final class UsbDescriptorParser {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSpeaker) {
|
||||
probability += 0.75f;
|
||||
if (hasHeadphoneOrHeadset) {
|
||||
likelihood += 0.75f;
|
||||
} else if (hasSpeaker) {
|
||||
// The device only reports output terminal as speaker. Try to figure out if the device
|
||||
// is a headset or not by checking if it has associated input terminal and if multiple
|
||||
// channels are supported or not.
|
||||
likelihood += 0.5f;
|
||||
if (hasAssociatedInputTerminal) {
|
||||
likelihood += 0.25f;
|
||||
}
|
||||
if (getMaximumChannelCount() > 2) {
|
||||
// When multiple channels are supported, it is less likely to be a headset.
|
||||
likelihood -= 0.25f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSpeaker && hasHIDInterface()) {
|
||||
probability += 0.25f;
|
||||
if ((hasHeadphoneOrHeadset || hasSpeaker) && hasHIDInterface()) {
|
||||
likelihood += 0.25f;
|
||||
}
|
||||
|
||||
return probability;
|
||||
return likelihood;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,7 +910,7 @@ public final class UsbDescriptorParser {
|
||||
* to count on the peripheral being a headset.
|
||||
*/
|
||||
public boolean isOutputHeadset() {
|
||||
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
|
||||
return getOutputHeadsetLikelihood() >= OUT_HEADSET_TRIGGER;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user