Home | History | Annotate | Download | only in alsa
      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 
     73         public AlsaDeviceRecord() {}
     74 
     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 
    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 
    200     public AlsaDevicesParser() {}
    201 
    202     //
    203     // Access
    204     //
    205     public int getDefaultDeviceNum(int card) {
    206         // TODO - This (obviously) isn't sufficient. Revisit.
    207         return 0;
    208     }
    209 
    210     //
    211     // Predicates
    212     //
    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 
    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 
    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     //
    248     private boolean isLineDeviceRecord(String line) {
    249         return line.charAt(kIndex_CardDeviceField) == '[';
    250     }
    251 
    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 
    292     public int getScanStatus() {
    293         return mScanStatus;
    294     }
    295 
    296     //
    297     // Loging
    298     //
    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