1 /* 2 * Copyright (C) 2017 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 com.android.cts.verifier.audio.peripheralprofile; 18 19 import androidx.annotation.Nullable; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.util.ArrayList; 25 26 import javax.xml.parsers.ParserConfigurationException; 27 import javax.xml.parsers.SAXParser; 28 import javax.xml.parsers.SAXParserFactory; 29 30 import org.xml.sax.Attributes; 31 import org.xml.sax.InputSource; 32 import org.xml.sax.SAXException; 33 import org.xml.sax.XMLReader; 34 import org.xml.sax.helpers.DefaultHandler; 35 36 public class ProfileManager { 37 private static final String TAG = "ProfileManager"; 38 private static final boolean DEBUG = false; 39 40 private static final String mBuiltInprofiles = 41 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" + 42 "<ProfileList Version=\"1.0.0\">" + 43 "<PeripheralProfile ProfileName=\"Google USB-C Headset\" ProfileDescription=\"Google USB-C Headset\" ProductName=\"USB-Audio - Pixel USB-C earbuds\">" + 44 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"44100,48000\" />" + 45 "<InputDevInfo ChanCounts=\"1\" ChanPosMasks=\"16\" ChanIndexMasks=\"1\" Encodings=\"2\" SampleRates=\"44100,48000\" />" + 46 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" HasBtnD=\"0\" />" + 47 "</PeripheralProfile>" + 48 "<PeripheralProfile ProfileName=\"AudioBox USB 96\" ProfileDescription=\"PreSonus AudioBox USB 96\" ProductName=\"USB-Audio - AudioBox USB 96\">" + 49 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"22,4\" SampleRates=\"44100,48000,88200,96000\"/>" + 50 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"22,4\" SampleRates=\"44100,48000,88200,96000\"/>" + 51 "</PeripheralProfile>" + 52 "<PeripheralProfile ProfileName=\"AudioBox 44VSL\" ProfileDescription=\"Presonus AudioBox 44VSL\" ProductName=\"USB-Audio - AudioBox 44 VSL\">" + 53 "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"21,22,4\" SampleRates=\"44100,48000,88200,96000\" />" + 54 "<InputDevInfo ChanCounts=\"1,2,3,4\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3,7,15\" Encodings=\"21,22,4\" SampleRates=\"44100,48000,88200,96000\" />" + 55 "</PeripheralProfile>" + 56 "<PeripheralProfile ProfileName=\"AudioBox 22VSL\" ProfileDescription=\"Presonus AudioBox 22VSL\" ProductName=\"USB-Audio - AudioBox 22 VSL\">" + 57 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"21,22,4\" SampleRates=\"44100,48000,88200,96000\" />" + 58 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"21,22,4\" SampleRates=\"44100,48000,88200,96000\" />" + 59 "</PeripheralProfile>" + 60 "<PeripheralProfile ProfileName=\"AudioBox USB\" ProfileDescription=\"Presonus AudioBox USB\" ProductName=\"USB-Audio - AudioBox USB\">" + 61 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"44100,48000\" />" + 62 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"21,4\" SampleRates=\"44100,48000\" />" + 63 "</PeripheralProfile>" + 64 "<PeripheralProfile ProfileName=\"Focusrite 2i4\" ProfileDescription=\"Focusrite Scarlett 2i4\" ProductName=\"USB-Audio - Scarlett 2i4 USB\">" + 65 "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"21,4\" SampleRates=\"44100,48000,88200,96000\"/>" + 66 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"21,4\" SampleRates=\"44100,48000,88200,96000\"/>" + 67 "</PeripheralProfile>" + 68 "<PeripheralProfile ProfileName=\"Behringer UMC204HD\" ProfileDescription=\"Behringer UMC204HD\" ProductName=\"USB-Audio - UMC204HD 192k\">" + 69 "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"22,4,2\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" + 70 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"16,12\" ChanIndexMasks=\"1,3\" Encodings=\"22,4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" + 71 "</PeripheralProfile>" + 72 "<PeripheralProfile ProfileName=\"Roland Rubix24\" ProfileDescription=\"Roland Rubix24\" ProductName=\"USB-Audio - Rubix24\">" + 73 "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"22,4\" SampleRates=\"44100,48000,96000,192000\"/>" + 74 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"22,4\" SampleRates=\"44100,48000,96000,192000\"/>" + 75 "</PeripheralProfile>" + 76 "<PeripheralProfile ProfileName=\"Pixel USB-C Dongle + Wired Analog Headset\" ProfileDescription=\"Reference USB Dongle\" ProductName=\"USB-Audio - USB-C to 3.5mm-Headphone Adapte\">" + 77 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"48000\" />" + 78 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"48000\" />" + 79 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" + 80 "</PeripheralProfile>" + 81 "<PeripheralProfile ProfileName=\"HTC Dongle\" ProfileDescription=\"Type-C to 3.5mm Headphone\" ProductName=\"USB-Audio - HTC Type-C to 3.5mm Headphone J\">" + 82 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"48000\" />" + 83 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"21,4\" SampleRates=\"48000\"/>" + 84 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" + 85 "</PeripheralProfile>" + 86 "<PeripheralProfile ProfileName=\"JBL Reflect Aware\" ProfileDescription=\"JBL Reflect Aware\" ProductName=\"USB-Audio - JBL Reflect Aware\">" + 87 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"2\" SampleRates=\"44100,48000\" />" + 88 "<InputDevInfo ChanCounts=\"1\" ChanPosMasks=\"16\" ChanIndexMasks=\"1\" Encodings=\"2\" SampleRates=\"44100,48000\" />" + 89 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" + 90 "</PeripheralProfile>" + 91 "<PeripheralProfile ProfileName=\"Libratone Q Adapt\" ProfileDescription=\"Libratone Q Adapt Headset\" ProductName=\"USB-Audio - Libratone_INEAR\">" + 92 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"2\" SampleRates=\"48000\" />" + 93 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1\" Encodings=\"2\" SampleRates=\"48000\" />"+ 94 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" + 95 "</PeripheralProfile>" + 96 "<PeripheralProfile ProfileName=\"Vantec\" ProfileDescription=\"Vantec 7.1 USB Interface\" ProductName=\"USB-Audio - USB Sound Device\">" + 97 "<OutputDevInfo ChanCounts=\"2,3,4,5,6,7,8\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15,31,63,127,255\" Encodings=\"2\" SampleRates=\"48000,96000,44100\" />" + 98 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"16,12\" ChanIndexMasks=\"1,3\" Encodings=\"2\" SampleRates=\"48000,44100\" />" + 99 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" + 100 "</PeripheralProfile>" + 101 "</ProfileList>"; 102 103 // XML Tags and Attributes 104 private final static String kTag_ProfileList = "ProfileList"; 105 private final static String kAttrName_Version = "Version"; 106 private final static String kValueStr_Version = "1.0.0"; 107 108 private final ArrayList<PeripheralProfile> mProfiles = 109 new ArrayList<PeripheralProfile>(); 110 111 private PeripheralProfile mParsingProfile = null; 112 addProfile(PeripheralProfile profile)113 public boolean addProfile(PeripheralProfile profile) { 114 mProfiles.add(profile); 115 116 return true; 117 } 118 119 private class ProfileLoader extends DefaultHandler { 120 @Override startElement(String namespaceURI, String localName, String qName, Attributes atts)121 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 122 if (localName.equals(kTag_ProfileList)) { 123 // Maybe check the version here. 124 } else if (localName.equals(PeripheralProfile.kTag_Profile)){ 125 mParsingProfile = new PeripheralProfile(null, null, null, null, null); 126 mParsingProfile.startElement(namespaceURI, localName, qName, atts); 127 } else { 128 mParsingProfile.startElement(namespaceURI, localName, qName, atts); 129 } 130 } 131 132 @Override endElement(String uri, String localName, String qName)133 public void endElement(String uri, String localName, String qName) { 134 if (localName.equals(kTag_ProfileList)) { 135 // Version Checking here maybe? 136 } else if (localName.equals(PeripheralProfile.kTag_Profile)){ 137 mProfiles.add(mParsingProfile); 138 mParsingProfile = null; 139 } 140 } 141 } 142 143 @Override toString()144 public String toString() { 145 return super.toString(); 146 } 147 loadProfiles(InputStream inStream)148 public boolean loadProfiles(InputStream inStream) { 149 SAXParserFactory spf = SAXParserFactory.newInstance(); 150 SAXParser sp; 151 try { 152 sp = spf.newSAXParser(); 153 XMLReader xr = sp.getXMLReader(); 154 xr.setContentHandler(new ProfileLoader()); 155 xr.parse(new InputSource(inStream)); 156 } catch (ParserConfigurationException e) { 157 e.printStackTrace(); 158 } catch (SAXException e) { 159 e.printStackTrace(); 160 } catch (IOException e) { 161 e.printStackTrace(); 162 } 163 164 return true; 165 } 166 loadProfiles(String profilesXML)167 public boolean loadProfiles(String profilesXML) { 168 mProfiles.clear(); 169 170 return loadProfiles(new ByteArrayInputStream(profilesXML.getBytes())); 171 } 172 loadProfiles()173 public boolean loadProfiles() { 174 return loadProfiles(mBuiltInprofiles); 175 } 176 177 // 178 // Access 179 // getProfiles()180 public ArrayList<PeripheralProfile> getProfiles() { return mProfiles; } 181 getNumProfiles()182 public int getNumProfiles() { 183 return mProfiles.size(); 184 } getProfile(int index)185 public PeripheralProfile getProfile(int index) { 186 return mProfiles.get(index); 187 } 188 189 @Nullable getProfile(String productName)190 public PeripheralProfile getProfile(String productName) { 191 for(PeripheralProfile profile : mProfiles) { 192 if (productName.equals(profile.getProductName())) { 193 return profile; 194 } 195 } 196 return null; 197 } 198 } 199