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