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 com.android.internal.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.ArrayList; 25 26 /** 27 * @hide 28 * Retrieves information from an ALSA "devices" file. 29 */ 30 /* 31 * NOTE: This class is currently not being used, but may be needed in the future. 32 */ 33 public class AlsaDevicesParser { 34 private static final String TAG = "AlsaDevicesParser"; 35 protected static final boolean DEBUG = false; 36 37 private static final String kDevicesFilePath = "/proc/asound/devices"; 38 39 private static final int kIndex_CardDeviceField = 5; 40 private static final int kStartIndex_CardNum = 6; 41 private static final int kEndIndex_CardNum = 8; // one past 42 private static final int kStartIndex_DeviceNum = 9; 43 private static final int kEndIndex_DeviceNum = 11; // one past 44 private static final int kStartIndex_Type = 14; 45 46 private static LineTokenizer mTokenizer = new LineTokenizer(" :[]-"); 47 48 private boolean mHasCaptureDevices = false; 49 private boolean mHasPlaybackDevices = false; 50 private boolean mHasMIDIDevices = false; 51 52 public static final int SCANSTATUS_NOTSCANNED = -1; 53 public static final int SCANSTATUS_SUCCESS = 0; 54 public static final int SCANSTATUS_FAIL = 1; 55 public static final int SCANSTATUS_EMPTY = 2; 56 private int mScanStatus = SCANSTATUS_NOTSCANNED; 57 58 public class AlsaDeviceRecord { 59 public static final int kDeviceType_Unknown = -1; 60 public static final int kDeviceType_Audio = 0; 61 public static final int kDeviceType_Control = 1; 62 public static final int kDeviceType_MIDI = 2; 63 64 public static final int kDeviceDir_Unknown = -1; 65 public static final int kDeviceDir_Capture = 0; 66 public static final int kDeviceDir_Playback = 1; 67 68 int mCardNum = -1; 69 int mDeviceNum = -1; 70 int mDeviceType = kDeviceType_Unknown; 71 int mDeviceDir = kDeviceDir_Unknown; 72 AlsaDeviceRecord()73 public AlsaDeviceRecord() {} 74 parse(String line)75 public boolean parse(String line) { 76 // "0123456789012345678901234567890" 77 // " 2: [ 0-31]: digital audio playback" 78 // " 3: [ 0-30]: digital audio capture" 79 // " 35: [ 1] : control" 80 // " 36: [ 2- 0]: raw midi" 81 82 final int kToken_LineNum = 0; 83 final int kToken_CardNum = 1; 84 final int kToken_DeviceNum = 2; 85 final int kToken_Type0 = 3; // "digital", "control", "raw" 86 final int kToken_Type1 = 4; // "audio", "midi" 87 final int kToken_Type2 = 5; // "capture", "playback" 88 89 int tokenOffset = 0; 90 int delimOffset = 0; 91 int tokenIndex = kToken_LineNum; 92 while (true) { 93 tokenOffset = mTokenizer.nextToken(line, delimOffset); 94 if (tokenOffset == LineTokenizer.kTokenNotFound) { 95 break; // bail 96 } 97 delimOffset = mTokenizer.nextDelimiter(line, tokenOffset); 98 if (delimOffset == LineTokenizer.kTokenNotFound) { 99 delimOffset = line.length(); 100 } 101 String token = line.substring(tokenOffset, delimOffset); 102 103 try { 104 switch (tokenIndex) { 105 case kToken_LineNum: 106 // ignore 107 break; 108 109 case kToken_CardNum: 110 mCardNum = Integer.parseInt(token); 111 if (line.charAt(delimOffset) != '-') { 112 tokenIndex++; // no device # in the token stream 113 } 114 break; 115 116 case kToken_DeviceNum: 117 mDeviceNum = Integer.parseInt(token); 118 break; 119 120 case kToken_Type0: 121 if (token.equals("digital")) { 122 // NOP 123 } else if (token.equals("control")) { 124 mDeviceType = kDeviceType_Control; 125 } else if (token.equals("raw")) { 126 // NOP 127 } 128 break; 129 130 case kToken_Type1: 131 if (token.equals("audio")) { 132 mDeviceType = kDeviceType_Audio; 133 } else if (token.equals("midi")) { 134 mDeviceType = kDeviceType_MIDI; 135 mHasMIDIDevices = true; 136 } 137 break; 138 139 case kToken_Type2: 140 if (token.equals("capture")) { 141 mDeviceDir = kDeviceDir_Capture; 142 mHasCaptureDevices = true; 143 } else if (token.equals("playback")) { 144 mDeviceDir = kDeviceDir_Playback; 145 mHasPlaybackDevices = true; 146 } 147 break; 148 } // switch (tokenIndex) 149 } catch (NumberFormatException e) { 150 Slog.e(TAG, "Failed to parse token " + tokenIndex + " of " + kDevicesFilePath 151 + " token: " + token); 152 return false; 153 } 154 155 tokenIndex++; 156 } // while (true) 157 158 return true; 159 } // parse() 160 textFormat()161 public String textFormat() { 162 StringBuilder sb = new StringBuilder(); 163 sb.append("[" + mCardNum + ":" + mDeviceNum + "]"); 164 165 switch (mDeviceType) { 166 case kDeviceType_Unknown: 167 default: 168 sb.append(" N/A"); 169 break; 170 case kDeviceType_Audio: 171 sb.append(" Audio"); 172 break; 173 case kDeviceType_Control: 174 sb.append(" Control"); 175 break; 176 case kDeviceType_MIDI: 177 sb.append(" MIDI"); 178 break; 179 } 180 181 switch (mDeviceDir) { 182 case kDeviceDir_Unknown: 183 default: 184 sb.append(" N/A"); 185 break; 186 case kDeviceDir_Capture: 187 sb.append(" Capture"); 188 break; 189 case kDeviceDir_Playback: 190 sb.append(" Playback"); 191 break; 192 } 193 194 return sb.toString(); 195 } 196 } 197 198 private final ArrayList<AlsaDeviceRecord> mDeviceRecords = new ArrayList<AlsaDeviceRecord>(); 199 AlsaDevicesParser()200 public AlsaDevicesParser() {} 201 202 // 203 // Access 204 // getDefaultDeviceNum(int card)205 public int getDefaultDeviceNum(int card) { 206 // TODO - This (obviously) isn't sufficient. Revisit. 207 return 0; 208 } 209 210 // 211 // Predicates 212 // hasPlaybackDevices(int card)213 public boolean hasPlaybackDevices(int card) { 214 for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { 215 if (deviceRecord.mCardNum == card && 216 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && 217 deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) { 218 return true; 219 } 220 } 221 return false; 222 } 223 hasCaptureDevices(int card)224 public boolean hasCaptureDevices(int card) { 225 for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { 226 if (deviceRecord.mCardNum == card && 227 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && 228 deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) { 229 return true; 230 } 231 } 232 return false; 233 } 234 hasMIDIDevices(int card)235 public boolean hasMIDIDevices(int card) { 236 for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { 237 if (deviceRecord.mCardNum == card && 238 deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) { 239 return true; 240 } 241 } 242 return false; 243 } 244 245 // 246 // Process 247 // isLineDeviceRecord(String line)248 private boolean isLineDeviceRecord(String line) { 249 return line.charAt(kIndex_CardDeviceField) == '['; 250 } 251 scan()252 public int scan() { 253 if (DEBUG) { 254 Slog.i(TAG, "AlsaDevicesParser.scan()...."); 255 } 256 257 mDeviceRecords.clear(); 258 259 File devicesFile = new File(kDevicesFilePath); 260 try { 261 FileReader reader = new FileReader(devicesFile); 262 BufferedReader bufferedReader = new BufferedReader(reader); 263 String line = ""; 264 while ((line = bufferedReader.readLine()) != null) { 265 if (isLineDeviceRecord(line)) { 266 AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord(); 267 deviceRecord.parse(line); 268 Slog.i(TAG, deviceRecord.textFormat()); 269 mDeviceRecords.add(deviceRecord); 270 } 271 } 272 reader.close(); 273 // success if we add at least 1 record 274 if (mDeviceRecords.size() > 0) { 275 mScanStatus = SCANSTATUS_SUCCESS; 276 } else { 277 mScanStatus = SCANSTATUS_EMPTY; 278 } 279 } catch (FileNotFoundException e) { 280 e.printStackTrace(); 281 mScanStatus = SCANSTATUS_FAIL; 282 } catch (IOException e) { 283 e.printStackTrace(); 284 mScanStatus = SCANSTATUS_FAIL; 285 } 286 if (DEBUG) { 287 Slog.i(TAG, " status:" + mScanStatus); 288 } 289 return mScanStatus; 290 } 291 getScanStatus()292 public int getScanStatus() { 293 return mScanStatus; 294 } 295 296 // 297 // Loging 298 // Log(String heading)299 private void Log(String heading) { 300 if (DEBUG) { 301 Slog.i(TAG, heading); 302 for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { 303 Slog.i(TAG, deviceRecord.textFormat()); 304 } 305 } 306 } 307 } // class AlsaDevicesParser 308 309