diff options
author | Mike Lockwood <lockwood@google.com> | 2015-02-27 18:42:11 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-02-27 18:42:13 +0000 |
commit | 238cba35934e11c062c5f5da1768fcb474a8715d (patch) | |
tree | d356ac475b3cdf15803644a9798a29ed34aa4717 /core/java/com | |
parent | 228fd600cebe51e612357489310bc239d6bf24cc (diff) | |
parent | 464c655871f4c1dd280474723e4b33f55044baa5 (diff) | |
download | frameworks_base-238cba35934e11c062c5f5da1768fcb474a8715d.zip frameworks_base-238cba35934e11c062c5f5da1768fcb474a8715d.tar.gz frameworks_base-238cba35934e11c062c5f5da1768fcb474a8715d.tar.bz2 |
Merge "Move ALSA utilities to com.android.internal.alsa package"
Diffstat (limited to 'core/java/com')
3 files changed, 570 insertions, 0 deletions
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java new file mode 100644 index 0000000..5c0a888 --- /dev/null +++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java @@ -0,0 +1,225 @@ +/* + * 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 "cards" file. + */ +public class AlsaCardsParser { + private static final String TAG = "AlsaCardsParser"; + protected static final boolean DEBUG = true; + + private static final String kCardsFilePath = "/proc/asound/cards"; + + private static LineTokenizer mTokenizer = new LineTokenizer(" :[]"); + + private ArrayList<AlsaCardRecord> mCardRecords = new ArrayList<AlsaCardRecord>(); + + public class AlsaCardRecord { + private static final String TAG = "AlsaCardRecord"; + private static final String kUsbCardKeyStr = "at usb-"; + + public int mCardNum = -1; + public String mField1 = ""; + public String mCardName = ""; + public String mCardDescription = ""; + public boolean mIsUsb = false; + + public AlsaCardRecord() {} + + public boolean parse(String line, int lineIndex) { + int tokenIndex = 0; + int delimIndex = 0; + + if (lineIndex == 0) { + // line # (skip) + tokenIndex = mTokenizer.nextToken(line, tokenIndex); + delimIndex = mTokenizer.nextDelimiter(line, tokenIndex); + + try { + // mCardNum + mCardNum = Integer.parseInt(line.substring(tokenIndex, delimIndex)); + } catch (NumberFormatException e) { + Slog.e(TAG, "Failed to parse line " + lineIndex + " of " + kCardsFilePath + + ": " + line.substring(tokenIndex, delimIndex)); + return false; + } + + // mField1 + tokenIndex = mTokenizer.nextToken(line, delimIndex); + delimIndex = mTokenizer.nextDelimiter(line, tokenIndex); + mField1 = line.substring(tokenIndex, delimIndex); + + // mCardName + tokenIndex = mTokenizer.nextToken(line, delimIndex); + mCardName = line.substring(tokenIndex); + + // done + } else if (lineIndex == 1) { + tokenIndex = mTokenizer.nextToken(line, 0); + if (tokenIndex != -1) { + int keyIndex = line.indexOf(kUsbCardKeyStr); + mIsUsb = keyIndex != -1; + if (mIsUsb) { + mCardDescription = line.substring(tokenIndex, keyIndex - 1); + } + } + } + + return true; + } + + public String textFormat() { + return mCardName + " : " + mCardDescription; + } + } + + public AlsaCardsParser() {} + + public void scan() { + if (DEBUG) { + Slog.i(TAG, "AlsaCardsParser.scan()"); + } + mCardRecords = new ArrayList<AlsaCardRecord>(); + + File cardsFile = new File(kCardsFilePath); + try { + FileReader reader = new FileReader(cardsFile); + BufferedReader bufferedReader = new BufferedReader(reader); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + AlsaCardRecord cardRecord = new AlsaCardRecord(); + if (DEBUG) { + Slog.i(TAG, " " + line); + } + cardRecord.parse(line, 0); + + line = bufferedReader.readLine(); + if (line == null) { + break; + } + if (DEBUG) { + Slog.i(TAG, " " + line); + } + cardRecord.parse(line, 1); + + mCardRecords.add(cardRecord); + } + reader.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public ArrayList<AlsaCardRecord> getScanRecords() { + return mCardRecords; + } + + public AlsaCardRecord getCardRecordAt(int index) { + return mCardRecords.get(index); + } + + public AlsaCardRecord getCardRecordFor(int cardNum) { + for (AlsaCardRecord rec : mCardRecords) { + if (rec.mCardNum == cardNum) { + return rec; + } + } + + return null; + } + + public int getNumCardRecords() { + return mCardRecords.size(); + } + + public boolean isCardUsb(int cardNum) { + for (AlsaCardRecord rec : mCardRecords) { + if (rec.mCardNum == cardNum) { + return rec.mIsUsb; + } + } + + return false; + } + + // return -1 if none found + public int getDefaultUsbCard() { + // Choose the most-recently added EXTERNAL card + // or return the first added EXTERNAL card? + for (AlsaCardRecord rec : mCardRecords) { + if (rec.mIsUsb) { + return rec.mCardNum; + } + } + + return -1; + } + + public int getDefaultCard() { + // return an external card if possible + int card = getDefaultUsbCard(); + + if (card < 0 && getNumCardRecords() > 0) { + // otherwise return the (internal) card with the highest number + card = getCardRecordAt(getNumCardRecords() - 1).mCardNum; + } + return card; + } + + static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) { + for (AlsaCardRecord cardRec : recs) { + if (cardRec.mCardNum == cardNum) { + return true; + } + } + return false; + } + + public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) { + ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>(); + for (AlsaCardRecord rec : mCardRecords) { + // now scan to see if this card number is in the previous scan list + if (!hasCardNumber(prevScanRecs, rec.mCardNum)) { + newRecs.add(rec); + } + } + return newRecs; + } + + // + // Logging + // + public void Log(String heading) { + if (DEBUG) { + Slog.i(TAG, heading); + for (AlsaCardRecord cardRec : mCardRecords) { + Slog.i(TAG, cardRec.textFormat()); + } + } + } +} diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java new file mode 100644 index 0000000..81b7943 --- /dev/null +++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java @@ -0,0 +1,288 @@ +/* + * 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 ArrayList<AlsaDeviceRecord> mDeviceRecords = new ArrayList<AlsaDeviceRecord>(); + + 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 + diff --git a/core/java/com/android/internal/alsa/LineTokenizer.java b/core/java/com/android/internal/alsa/LineTokenizer.java new file mode 100644 index 0000000..43047a9 --- /dev/null +++ b/core/java/com/android/internal/alsa/LineTokenizer.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * @hide + * Breaks lines in an ALSA "cards" or "devices" file into tokens. + * TODO(pmclean) Look into replacing this with String.split(). + */ +public class LineTokenizer { + public static final int kTokenNotFound = -1; + + private String mDelimiters = ""; + + public LineTokenizer(String delimiters) { + mDelimiters = delimiters; + } + + int nextToken(String line, int startIndex) { + int len = line.length(); + int offset = startIndex; + for (; offset < len; offset++) { + if (mDelimiters.indexOf(line.charAt(offset)) == -1) { + // past a delimiter + break; + } + } + + return offset < len ? offset : kTokenNotFound; + } + + int nextDelimiter(String line, int startIndex) { + int len = line.length(); + int offset = startIndex; + for (; offset < len; offset++) { + if (mDelimiters.indexOf(line.charAt(offset)) != -1) { + // past a delimiter + break; + } + } + + return offset < len ? offset : kTokenNotFound; + } +} |