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 
17 package com.android.internal.alsa;
18 
19 import android.util.Slog;
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 
27 /**
28  * @hide Retrieves information from an ALSA "cards" file.
29  */
30 public class AlsaCardsParser {
31     private static final String TAG = "AlsaCardsParser";
32     protected static final boolean DEBUG = false;
33 
34     private static final String kCardsFilePath = "/proc/asound/cards";
35 
36     private static LineTokenizer mTokenizer = new LineTokenizer(" :[]");
37 
38     private ArrayList<AlsaCardRecord> mCardRecords = new ArrayList<AlsaCardRecord>();
39 
40     public class AlsaCardRecord {
41         private static final String TAG = "AlsaCardRecord";
42         private static final String kUsbCardKeyStr = "at usb-";
43 
44         public int mCardNum = -1;
45         public String mField1 = "";
46         public String mCardName = "";
47         public String mCardDescription = "";
48         public boolean mIsUsb = false;
49 
AlsaCardRecord()50         public AlsaCardRecord() {}
51 
parse(String line, int lineIndex)52         public boolean parse(String line, int lineIndex) {
53             int tokenIndex = 0;
54             int delimIndex = 0;
55 
56             if (lineIndex == 0) {
57                 // line # (skip)
58                 tokenIndex = mTokenizer.nextToken(line, tokenIndex);
59                 delimIndex = mTokenizer.nextDelimiter(line, tokenIndex);
60 
61                 try {
62                     // mCardNum
63                     mCardNum = Integer.parseInt(line.substring(tokenIndex, delimIndex));
64                 } catch (NumberFormatException e) {
65                     Slog.e(TAG, "Failed to parse line " + lineIndex + " of " + kCardsFilePath
66                         + ": " + line.substring(tokenIndex, delimIndex));
67                     return false;
68                 }
69 
70                 // mField1
71                 tokenIndex = mTokenizer.nextToken(line, delimIndex);
72                 delimIndex = mTokenizer.nextDelimiter(line, tokenIndex);
73                 mField1 = line.substring(tokenIndex, delimIndex);
74 
75                 // mCardName
76                 tokenIndex = mTokenizer.nextToken(line, delimIndex);
77                 mCardName = line.substring(tokenIndex);
78 
79                 // done
80               } else if (lineIndex == 1) {
81                   tokenIndex = mTokenizer.nextToken(line, 0);
82                   if (tokenIndex != -1) {
83                       int keyIndex = line.indexOf(kUsbCardKeyStr);
84                       mIsUsb = keyIndex != -1;
85                       if (mIsUsb) {
86                           mCardDescription = line.substring(tokenIndex, keyIndex - 1);
87                       }
88                   }
89             }
90 
91             return true;
92         }
93 
textFormat()94         public String textFormat() {
95           return mCardName + " : " + mCardDescription;
96         }
97 
log(int listIndex)98         public void log(int listIndex) {
99             Slog.d(TAG, "" + listIndex +
100                 " [" + mCardNum + " " + mCardName + " : " + mCardDescription +
101                 " usb:" + mIsUsb);
102         }
103     }
104 
AlsaCardsParser()105     public AlsaCardsParser() {}
106 
scan()107     public void scan() {
108         if (DEBUG) {
109             Slog.i(TAG, "AlsaCardsParser.scan()");
110         }
111         mCardRecords = new ArrayList<AlsaCardRecord>();
112 
113         File cardsFile = new File(kCardsFilePath);
114         try {
115             FileReader reader = new FileReader(cardsFile);
116             BufferedReader bufferedReader = new BufferedReader(reader);
117             String line = "";
118             while ((line = bufferedReader.readLine()) != null) {
119                 AlsaCardRecord cardRecord = new AlsaCardRecord();
120                 if (DEBUG) {
121                     Slog.i(TAG, "  " + line);
122                 }
123                 cardRecord.parse(line, 0);
124 
125                 line = bufferedReader.readLine();
126                 if (line == null) {
127                     break;
128                 }
129                 if (DEBUG) {
130                     Slog.i(TAG, "  " + line);
131                 }
132                 cardRecord.parse(line, 1);
133 
134                 mCardRecords.add(cardRecord);
135             }
136             reader.close();
137         } catch (FileNotFoundException e) {
138             e.printStackTrace();
139         } catch (IOException e) {
140             e.printStackTrace();
141         }
142     }
143 
getScanRecords()144     public ArrayList<AlsaCardRecord> getScanRecords() {
145         return mCardRecords;
146     }
147 
getCardRecordAt(int index)148     public AlsaCardRecord getCardRecordAt(int index) {
149         return mCardRecords.get(index);
150     }
151 
getCardRecordFor(int cardNum)152     public AlsaCardRecord getCardRecordFor(int cardNum) {
153         for (AlsaCardRecord rec : mCardRecords) {
154             if (rec.mCardNum == cardNum) {
155                 return rec;
156             }
157         }
158 
159         return null;
160     }
161 
getNumCardRecords()162     public int getNumCardRecords() {
163         return mCardRecords.size();
164     }
165 
isCardUsb(int cardNum)166     public boolean isCardUsb(int cardNum) {
167         for (AlsaCardRecord rec : mCardRecords) {
168             if (rec.mCardNum == cardNum) {
169                 return rec.mIsUsb;
170             }
171         }
172 
173         return false;
174     }
175 
176     // return -1 if none found
getDefaultUsbCard()177     public int getDefaultUsbCard() {
178         // save the current list of devices
179         ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
180         if (DEBUG) {
181             LogDevices("Previous Devices:", prevRecs);
182         }
183 
184         // get the new list of devices
185         scan();
186         if (DEBUG) {
187             LogDevices("Current Devices:", mCardRecords);
188         }
189 
190         // Calculate the difference between the old and new device list
191         ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
192         if (DEBUG) {
193             LogDevices("New Devices:", newRecs);
194         }
195 
196         // Choose the most-recently added EXTERNAL card
197         // Check recently added devices
198         for (AlsaCardRecord rec : newRecs) {
199             if (DEBUG) {
200                 Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
201             }
202             if (rec.mIsUsb) {
203                 // Found it
204                 return rec.mCardNum;
205             }
206         }
207 
208         // or return the first added EXTERNAL card?
209         for (AlsaCardRecord rec : prevRecs) {
210             if (DEBUG) {
211                 Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
212             }
213             if (rec.mIsUsb) {
214                 return rec.mCardNum;
215             }
216         }
217 
218         return -1;
219     }
220 
getDefaultCard()221     public int getDefaultCard() {
222         // return an external card if possible
223         int card = getDefaultUsbCard();
224         if (DEBUG) {
225             Slog.d(TAG, "getDefaultCard() default usb card:" + card);
226         }
227 
228         if (card < 0 && getNumCardRecords() > 0) {
229             // otherwise return the (internal) card with the highest number
230             card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
231         }
232         if (DEBUG) {
233             Slog.d(TAG, "  returns card:" + card);
234         }
235         return card;
236     }
237 
hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum)238     static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) {
239         for (AlsaCardRecord cardRec : recs) {
240             if (cardRec.mCardNum == cardNum) {
241                 return true;
242             }
243         }
244         return false;
245     }
246 
getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs)247     public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) {
248         ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>();
249         for (AlsaCardRecord rec : mCardRecords) {
250             // now scan to see if this card number is in the previous scan list
251             if (!hasCardNumber(prevScanRecs, rec.mCardNum)) {
252                 newRecs.add(rec);
253             }
254         }
255         return newRecs;
256     }
257 
258     //
259     // Logging
260     //
Log(String heading)261     public void Log(String heading) {
262         if (DEBUG) {
263             Slog.i(TAG, heading);
264             for (AlsaCardRecord cardRec : mCardRecords) {
265                 Slog.i(TAG, cardRec.textFormat());
266             }
267         }
268     }
269 
LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList)270     static public void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
271         Slog.d(TAG, caption + " ----------------");
272         int listIndex = 0;
273         for (AlsaCardRecord device : deviceList) {
274             device.log(listIndex++);
275         }
276         Slog.d(TAG, "----------------");
277     }
278 }
279