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