1 /* 2 * Copyright (C) 2014 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 package android.alsa; 17 18 import android.util.Slog; 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.FileNotFoundException; 22 import java.io.FileReader; 23 import java.io.IOException; 24 import java.util.Vector; 25 26 /** 27 * @hide 28 * Retrieves information from an ALSA "devices" file. 29 */ 30 public class AlsaDevicesParser { 31 private static final String TAG = "AlsaDevicesParser"; 32 33 private static final int kIndex_CardDeviceField = 5; 34 private static final int kStartIndex_CardNum = 6; 35 private static final int kEndIndex_CardNum = 8; // one past 36 private static final int kStartIndex_DeviceNum = 9; 37 private static final int kEndIndex_DeviceNum = 11; // one past 38 private static final int kStartIndex_Type = 14; 39 40 private static LineTokenizer mTokenizer = new LineTokenizer(" :[]-"); 41 42 private boolean mHasCaptureDevices = false; 43 private boolean mHasPlaybackDevices = false; 44 private boolean mHasMIDIDevices = false; 45 46 public class AlsaDeviceRecord { 47 public static final int kDeviceType_Unknown = -1; 48 public static final int kDeviceType_Audio = 0; 49 public static final int kDeviceType_Control = 1; 50 public static final int kDeviceType_MIDI = 2; 51 52 public static final int kDeviceDir_Unknown = -1; 53 public static final int kDeviceDir_Capture = 0; 54 public static final int kDeviceDir_Playback = 1; 55 56 int mCardNum = -1; 57 int mDeviceNum = -1; 58 int mDeviceType = kDeviceType_Unknown; 59 int mDeviceDir = kDeviceDir_Unknown; 60 61 public AlsaDeviceRecord() { 62 } 63 64 public boolean parse(String line) { 65 // "0123456789012345678901234567890" 66 // " 2: [ 0-31]: digital audio playback" 67 // " 3: [ 0-30]: digital audio capture" 68 // " 35: [ 1] : control" 69 // " 36: [ 2- 0]: raw midi" 70 71 final int kToken_LineNum = 0; 72 final int kToken_CardNum = 1; 73 final int kToken_DeviceNum = 2; 74 final int kToken_Type0 = 3; // "digital", "control", "raw" 75 final int kToken_Type1 = 4; // "audio", "midi" 76 final int kToken_Type2 = 5; // "capture", "playback" 77 78 int tokenOffset = 0; 79 int delimOffset = 0; 80 int tokenIndex = kToken_LineNum; 81 while (true) { 82 tokenOffset = mTokenizer.nextToken(line, delimOffset); 83 if (tokenOffset == LineTokenizer.kTokenNotFound) { 84 break; // bail 85 } 86 delimOffset = mTokenizer.nextDelimiter(line, tokenOffset); 87 if (delimOffset == LineTokenizer.kTokenNotFound) { 88 delimOffset = line.length(); 89 } 90 String token = line.substring(tokenOffset, delimOffset); 91 92 switch (tokenIndex) { 93 case kToken_LineNum: 94 // ignore 95 break; 96 97 case kToken_CardNum: 98 mCardNum = Integer.parseInt(token); 99 if (line.charAt(delimOffset) != '-') { 100 tokenIndex++; // no device # in the token stream 101 } 102 break; 103 104 case kToken_DeviceNum: 105 mDeviceNum = Integer.parseInt(token); 106 break; 107 108 case kToken_Type0: 109 if (token.equals("digital")) { 110 // NOP 111 } else if (token.equals("control")) { 112 mDeviceType = kDeviceType_Control; 113 } else if (token.equals("raw")) { 114 // NOP 115 } 116 break; 117 118 case kToken_Type1: 119 if (token.equals("audio")) { 120 mDeviceType = kDeviceType_Audio; 121 } else if (token.equals("midi")) { 122 mDeviceType = kDeviceType_MIDI; 123 mHasMIDIDevices = true; 124 } 125 break; 126 127 case kToken_Type2: 128 if (token.equals("capture")) { 129 mDeviceDir = kDeviceDir_Capture; 130 mHasCaptureDevices = true; 131 } else if (token.equals("playback")) { 132 mDeviceDir = kDeviceDir_Playback; 133 mHasPlaybackDevices = true; 134 } 135 break; 136 } // switch (tokenIndex) 137 138 tokenIndex++; 139 } // while (true) 140 141 return true; 142 } // parse() 143 144 public String textFormat() { 145 StringBuilder sb = new StringBuilder(); 146 sb.append("[" + mCardNum + ":" + mDeviceNum + "]"); 147 148 switch (mDeviceType) { 149 case kDeviceType_Unknown: 150 sb.append(" N/A"); 151 break; 152 case kDeviceType_Audio: 153 sb.append(" Audio"); 154 break; 155 case kDeviceType_Control: 156 sb.append(" Control"); 157 break; 158 case kDeviceType_MIDI: 159 sb.append(" MIDI"); 160 break; 161 } 162 163 switch (mDeviceDir) { 164 case kDeviceDir_Unknown: 165 sb.append(" N/A"); 166 break; 167 case kDeviceDir_Capture: 168 sb.append(" Capture"); 169 break; 170 case kDeviceDir_Playback: 171 sb.append(" Playback"); 172 break; 173 } 174 175 return sb.toString(); 176 } 177 } 178 179 private Vector<AlsaDeviceRecord> 180 deviceRecords_ = new Vector<AlsaDeviceRecord>(); 181 182 private boolean isLineDeviceRecord(String line) { 183 return line.charAt(kIndex_CardDeviceField) == '['; 184 } 185 186 public AlsaDevicesParser() { 187 } 188 189 public int getNumDeviceRecords() { 190 return deviceRecords_.size(); 191 } 192 193 public AlsaDeviceRecord getDeviceRecordAt(int index) { 194 return deviceRecords_.get(index); 195 } 196 197 public void Log() { 198 int numDevRecs = getNumDeviceRecords(); 199 for (int index = 0; index < numDevRecs; ++index) { 200 Slog.w(TAG, "usb:" + getDeviceRecordAt(index).textFormat()); 201 } 202 } 203 204 public boolean hasPlaybackDevices() { 205 return mHasPlaybackDevices; 206 } 207 208 public boolean hasPlaybackDevices(int card) { 209 for (int index = 0; index < deviceRecords_.size(); index++) { 210 AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); 211 if (deviceRecord.mCardNum == card && 212 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && 213 deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) { 214 return true; 215 } 216 } 217 return false; 218 } 219 220 public boolean hasCaptureDevices() { 221 return mHasCaptureDevices; 222 } 223 224 public boolean hasCaptureDevices(int card) { 225 for (int index = 0; index < deviceRecords_.size(); index++) { 226 AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); 227 if (deviceRecord.mCardNum == card && 228 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && 229 deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) { 230 return true; 231 } 232 } 233 return false; 234 } 235 236 public boolean hasMIDIDevices() { 237 return mHasMIDIDevices; 238 } 239 240 public boolean hasMIDIDevices(int card) { 241 for (int index = 0; index < deviceRecords_.size(); index++) { 242 AlsaDeviceRecord deviceRecord = deviceRecords_.get(index); 243 if (deviceRecord.mCardNum == card && 244 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) { 245 return true; 246 } 247 } 248 return false; 249 } 250 251 public void scan() { 252 deviceRecords_.clear(); 253 254 final String devicesFilePath = "/proc/asound/devices"; 255 File devicesFile = new File(devicesFilePath); 256 try { 257 FileReader reader = new FileReader(devicesFile); 258 BufferedReader bufferedReader = new BufferedReader(reader); 259 String line = ""; 260 while ((line = bufferedReader.readLine()) != null) { 261 if (isLineDeviceRecord(line)) { 262 AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord(); 263 deviceRecord.parse(line); 264 deviceRecords_.add(deviceRecord); 265 } 266 } 267 reader.close(); 268 } catch (FileNotFoundException e) { 269 e.printStackTrace(); 270 } catch (IOException e) { 271 e.printStackTrace(); 272 } 273 } 274 } // class AlsaDevicesParser 275 276