Update logic for determining if an USB device is a headset or not.
Update the function of calculating likelihood of being an output headset. 1. Change the function name from probability to likelihood. It is fine to have the value be negative or greater than 1.0f. 2. If there is an output terminal with type as HEADPHONES or BIDIR_HEADSET, likelihood += 0.75, goto 5. Otherwise, goto 2. 3. If there is an output terminal with type as SPEAKER, likelihood += 0.5. Goto 3. 4. If there is associated input terminal for an output terminal exists, likelihood += 0.25. Goto 4. 5. If multiple channels are supported, likelihood -= 0.25. Goto 5. 6. If there is an output terminal with type as SPEAKER, HEADPHONES or BIDIR_HEADSET and there is HID interface, likelihood += 0.25. Test: connect USB devices and check the reported device type. Bug: 231973571 Change-Id: I98e433b5333f8bb317b6ae89bd65fee2c42236eb
This commit is contained in:
@@ -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