/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.alsa; import android.util.Slog; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; /** * @hide * Retrieves information from an ALSA "devices" file. */ public class AlsaDevicesParser { private static final String TAG = "AlsaDevicesParser"; protected static final boolean DEBUG = false; private static final String kDevicesFilePath = "/proc/asound/devices"; private static final int kIndex_CardDeviceField = 5; private static final int kStartIndex_CardNum = 6; private static final int kEndIndex_CardNum = 8; // one past private static final int kStartIndex_DeviceNum = 9; private static final int kEndIndex_DeviceNum = 11; // one past private static final int kStartIndex_Type = 14; private static LineTokenizer mTokenizer = new LineTokenizer(" :[]-"); private boolean mHasCaptureDevices = false; private boolean mHasPlaybackDevices = false; private boolean mHasMIDIDevices = false; public class AlsaDeviceRecord { public static final int kDeviceType_Unknown = -1; public static final int kDeviceType_Audio = 0; public static final int kDeviceType_Control = 1; public static final int kDeviceType_MIDI = 2; public static final int kDeviceDir_Unknown = -1; public static final int kDeviceDir_Capture = 0; public static final int kDeviceDir_Playback = 1; int mCardNum = -1; int mDeviceNum = -1; int mDeviceType = kDeviceType_Unknown; int mDeviceDir = kDeviceDir_Unknown; public AlsaDeviceRecord() {} public boolean parse(String line) { // "0123456789012345678901234567890" // " 2: [ 0-31]: digital audio playback" // " 3: [ 0-30]: digital audio capture" // " 35: [ 1] : control" // " 36: [ 2- 0]: raw midi" final int kToken_LineNum = 0; final int kToken_CardNum = 1; final int kToken_DeviceNum = 2; final int kToken_Type0 = 3; // "digital", "control", "raw" final int kToken_Type1 = 4; // "audio", "midi" final int kToken_Type2 = 5; // "capture", "playback" int tokenOffset = 0; int delimOffset = 0; int tokenIndex = kToken_LineNum; while (true) { tokenOffset = mTokenizer.nextToken(line, delimOffset); if (tokenOffset == LineTokenizer.kTokenNotFound) { break; // bail } delimOffset = mTokenizer.nextDelimiter(line, tokenOffset); if (delimOffset == LineTokenizer.kTokenNotFound) { delimOffset = line.length(); } String token = line.substring(tokenOffset, delimOffset); try { switch (tokenIndex) { case kToken_LineNum: // ignore break; case kToken_CardNum: mCardNum = Integer.parseInt(token); if (line.charAt(delimOffset) != '-') { tokenIndex++; // no device # in the token stream } break; case kToken_DeviceNum: mDeviceNum = Integer.parseInt(token); break; case kToken_Type0: if (token.equals("digital")) { // NOP } else if (token.equals("control")) { mDeviceType = kDeviceType_Control; } else if (token.equals("raw")) { // NOP } break; case kToken_Type1: if (token.equals("audio")) { mDeviceType = kDeviceType_Audio; } else if (token.equals("midi")) { mDeviceType = kDeviceType_MIDI; mHasMIDIDevices = true; } break; case kToken_Type2: if (token.equals("capture")) { mDeviceDir = kDeviceDir_Capture; mHasCaptureDevices = true; } else if (token.equals("playback")) { mDeviceDir = kDeviceDir_Playback; mHasPlaybackDevices = true; } break; } // switch (tokenIndex) } catch (NumberFormatException e) { Slog.e(TAG, "Failed to parse token " + tokenIndex + " of " + kDevicesFilePath + " token: " + token); return false; } tokenIndex++; } // while (true) return true; } // parse() public String textFormat() { StringBuilder sb = new StringBuilder(); sb.append("[" + mCardNum + ":" + mDeviceNum + "]"); switch (mDeviceType) { case kDeviceType_Unknown: sb.append(" N/A"); break; case kDeviceType_Audio: sb.append(" Audio"); break; case kDeviceType_Control: sb.append(" Control"); break; case kDeviceType_MIDI: sb.append(" MIDI"); break; } switch (mDeviceDir) { case kDeviceDir_Unknown: sb.append(" N/A"); break; case kDeviceDir_Capture: sb.append(" Capture"); break; case kDeviceDir_Playback: sb.append(" Playback"); break; } return sb.toString(); } } private final ArrayList mDeviceRecords = new ArrayList(); public AlsaDevicesParser() {} // // Access // public int getDefaultDeviceNum(int card) { // TODO - This (obviously) isn't sufficient. Revisit. return 0; } // // Predicates // /* public boolean hasPlaybackDevices() { return mHasPlaybackDevices; } */ public boolean hasPlaybackDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Playback) { return true; } } return false; } /* public boolean hasCaptureDevices() { return mHasCaptureDevices; } */ public boolean hasCaptureDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_Audio && deviceRecord.mDeviceDir == AlsaDeviceRecord.kDeviceDir_Capture) { return true; } } return false; } /* public boolean hasMIDIDevices() { return mHasMIDIDevices; } */ public boolean hasMIDIDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && deviceRecord.mDeviceType == AlsaDeviceRecord.kDeviceType_MIDI) { return true; } } return false; } // // Process // private boolean isLineDeviceRecord(String line) { return line.charAt(kIndex_CardDeviceField) == '['; } public void scan() { mDeviceRecords.clear(); File devicesFile = new File(kDevicesFilePath); try { FileReader reader = new FileReader(devicesFile); BufferedReader bufferedReader = new BufferedReader(reader); String line = ""; while ((line = bufferedReader.readLine()) != null) { if (isLineDeviceRecord(line)) { AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord(); deviceRecord.parse(line); mDeviceRecords.add(deviceRecord); } } reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // // Loging // public void Log(String heading) { if (DEBUG) { Slog.i(TAG, heading); for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { Slog.i(TAG, deviceRecord.textFormat()); } } } } // class AlsaDevicesParser