1 /*
2 **
3 ** Copyright 2012, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #define LOG_TAG "AudioHAL:AudioHotplugThread"
19 #include <utils/Log.h>
20
21 #include <assert.h>
22 #include <dirent.h>
23 #include <poll.h>
24 #include <sys/eventfd.h>
25 #include <sys/inotify.h>
26 #include <sys/ioctl.h>
27
28 // Bionic's copy of asound.h contains references to these kernel macros.
29 // They need to be removed in order to include the file from userland.
30 #define __force
31 #define __bitwise
32 #define __user
33 #include <sound/asound.h>
34 #undef __force
35 #undef __bitwise
36 #undef __user
37
38 #include <utils/misc.h>
39 #include <utils/String8.h>
40
41 #include "AudioHotplugThread.h"
42
43 // This name is used to recognize the AndroidTV Remote mic so we can
44 // use it for voice recognition.
45 #define ANDROID_TV_REMOTE_AUDIO_DEVICE_NAME "ATVRAudio"
46
47 namespace android {
48
49 /*
50 * ALSA parameter manipulation routines
51 *
52 * TODO: replace this when TinyAlsa offers a suitable API
53 */
54
param_is_mask(int p)55 static inline int param_is_mask(int p)
56 {
57 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
58 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
59 }
60
param_is_interval(int p)61 static inline int param_is_interval(int p)
62 {
63 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
64 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
65 }
66
param_to_interval(struct snd_pcm_hw_params * p,int n)67 static inline struct snd_interval *param_to_interval(
68 struct snd_pcm_hw_params *p, int n)
69 {
70 assert(p->intervals);
71 assert(param_is_interval(n));
72 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
73 }
74
param_to_mask(struct snd_pcm_hw_params * p,int n)75 static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
76 {
77 assert(p->masks);
78 assert(param_is_mask(n));
79 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
80 }
81
snd_mask_any(struct snd_mask * mask)82 static inline void snd_mask_any(struct snd_mask *mask)
83 {
84 memset(mask, 0xff, sizeof(struct snd_mask));
85 }
86
snd_interval_any(struct snd_interval * i)87 static inline void snd_interval_any(struct snd_interval *i)
88 {
89 i->min = 0;
90 i->openmin = 0;
91 i->max = UINT_MAX;
92 i->openmax = 0;
93 i->integer = 0;
94 i->empty = 0;
95 }
96
param_init(struct snd_pcm_hw_params * p)97 static void param_init(struct snd_pcm_hw_params *p)
98 {
99 int n, k;
100
101 memset(p, 0, sizeof(*p));
102 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
103 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
104 struct snd_mask *m = param_to_mask(p, n);
105 snd_mask_any(m);
106 }
107 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
108 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
109 struct snd_interval *i = param_to_interval(p, n);
110 snd_interval_any(i);
111 }
112 p->rmask = 0xFFFFFFFF;
113 }
114
115 /*
116 * Hotplug thread
117 */
118
119 const char* AudioHotplugThread::kThreadName = "ATVRemoteAudioHotplug";
120
121 // directory where ALSA device nodes appear
122 const char* AudioHotplugThread::kAlsaDeviceDir = "/dev/snd";
123
124 // filename suffix for ALSA nodes representing capture devices
125 const char AudioHotplugThread::kDeviceTypeCapture = 'c';
126
AudioHotplugThread(Callback & callback)127 AudioHotplugThread::AudioHotplugThread(Callback& callback)
128 : mCallback(callback)
129 , mShutdownEventFD(-1)
130 {
131 }
132
~AudioHotplugThread()133 AudioHotplugThread::~AudioHotplugThread()
134 {
135 if (mShutdownEventFD != -1) {
136 ::close(mShutdownEventFD);
137 }
138 }
139
start()140 bool AudioHotplugThread::start()
141 {
142 mShutdownEventFD = eventfd(0, EFD_NONBLOCK);
143 if (mShutdownEventFD == -1) {
144 return false;
145 }
146
147 return (run(kThreadName) == NO_ERROR);
148 }
149
shutdown()150 void AudioHotplugThread::shutdown()
151 {
152 requestExit();
153 uint64_t tmp = 1;
154 ::write(mShutdownEventFD, &tmp, sizeof(tmp));
155 join();
156 }
157
parseCaptureDeviceName(const char * name,unsigned int * card,unsigned int * device)158 bool AudioHotplugThread::parseCaptureDeviceName(const char* name,
159 unsigned int* card,
160 unsigned int* device)
161 {
162 char deviceType;
163 int ret = sscanf(name, "pcmC%uD%u%c", card, device, &deviceType);
164 return (ret == 3 && deviceType == kDeviceTypeCapture);
165 }
166
getAlsaParamInterval(const struct snd_pcm_hw_params & params,int n,unsigned int * min,unsigned int * max)167 static inline void getAlsaParamInterval(const struct snd_pcm_hw_params& params,
168 int n, unsigned int* min,
169 unsigned int* max)
170 {
171 struct snd_interval* interval = param_to_interval(
172 const_cast<struct snd_pcm_hw_params*>(¶ms), n);
173 *min = interval->min;
174 *max = interval->max;
175 }
176
177 // This was hacked out of "alsa_utils.cpp".
s_get_alsa_card_name(char * name,size_t len,int card_id)178 static int s_get_alsa_card_name(char *name, size_t len, int card_id)
179 {
180 int fd;
181 int amt = -1;
182 snprintf(name, len, "/proc/asound/card%d/id", card_id);
183 fd = open(name, O_RDONLY);
184 if (fd >= 0) {
185 amt = read(fd, name, len - 1);
186 if (amt > 0) {
187 // replace the '\n' at the end of the proc file with '\0'
188 name[amt - 1] = 0;
189 }
190 close(fd);
191 }
192 return amt;
193 }
194
getDeviceInfo(unsigned int pcmCard,unsigned int pcmDevice,DeviceInfo * info)195 bool AudioHotplugThread::getDeviceInfo(unsigned int pcmCard,
196 unsigned int pcmDevice,
197 DeviceInfo* info)
198 {
199 bool result = false;
200 int ret;
201 int len;
202 char cardName[64] = "";
203
204 String8 devicePath = String8::format("%s/pcmC%dD%d%c",
205 kAlsaDeviceDir, pcmCard, pcmDevice, kDeviceTypeCapture);
206
207 ALOGD("AudioHotplugThread::getDeviceInfo opening %s", devicePath.string());
208 int alsaFD = open(devicePath.string(), O_RDONLY);
209 if (alsaFD == -1) {
210 ALOGE("AudioHotplugThread::getDeviceInfo open failed for %s", devicePath.string());
211 goto done;
212 }
213
214 // query the device's ALSA configuration space
215 struct snd_pcm_hw_params params;
216 param_init(¶ms);
217 ret = ioctl(alsaFD, SNDRV_PCM_IOCTL_HW_REFINE, ¶ms);
218 if (ret == -1) {
219 ALOGE("AudioHotplugThread: refine ioctl failed");
220 goto done;
221 }
222
223 info->pcmCard = pcmCard;
224 info->pcmDevice = pcmDevice;
225 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
226 &info->minSampleBits, &info->maxSampleBits);
227 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
228 &info->minChannelCount, &info->maxChannelCount);
229 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_RATE,
230 &info->minSampleRate, &info->maxSampleRate);
231
232 // Ugly hack to recognize Remote mic and mark it for voice recognition
233 info->forVoiceRecognition = false;
234 len = s_get_alsa_card_name(cardName, sizeof(cardName), pcmCard);
235 ALOGD("AudioHotplugThread get_alsa_card_name returned %d, %s", len, cardName);
236 if (len > 0) {
237 if (strcmp(ANDROID_TV_REMOTE_AUDIO_DEVICE_NAME, cardName) == 0) {
238 ALOGD("AudioHotplugThread found Android TV remote mic on Card %d, for VOICE_RECOGNITION", pcmCard);
239 info->forVoiceRecognition = true;
240 }
241 }
242
243 result = true;
244
245 done:
246 if (alsaFD != -1) {
247 close(alsaFD);
248 }
249 return result;
250 }
251
252 // scan the ALSA device directory for a usable capture device
scanForDevice()253 void AudioHotplugThread::scanForDevice()
254 {
255 DIR* alsaDir;
256 DeviceInfo deviceInfo;
257
258 alsaDir = opendir(kAlsaDeviceDir);
259 if (alsaDir == NULL)
260 return;
261
262 while (true) {
263 struct dirent entry, *result;
264 int ret = readdir_r(alsaDir, &entry, &result);
265 if (ret != 0 || result == NULL)
266 break;
267 unsigned int pcmCard, pcmDevice;
268 if (parseCaptureDeviceName(entry.d_name, &pcmCard, &pcmDevice)) {
269 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
270 mCallback.onDeviceFound(deviceInfo);
271 }
272 }
273 }
274
275 closedir(alsaDir);
276 }
277
threadLoop()278 bool AudioHotplugThread::threadLoop()
279 {
280 int inotifyFD = -1;
281 int watchFD = -1;
282 int flags;
283
284 // watch for changes to the ALSA device directory
285 inotifyFD = inotify_init();
286 if (inotifyFD == -1) {
287 ALOGE("AudioHotplugThread: inotify_init failed");
288 goto done;
289 }
290 flags = fcntl(inotifyFD, F_GETFL, 0);
291 if (flags == -1) {
292 ALOGE("AudioHotplugThread: F_GETFL failed");
293 goto done;
294 }
295 if (fcntl(inotifyFD, F_SETFL, flags | O_NONBLOCK) == -1) {
296 ALOGE("AudioHotplugThread: F_SETFL failed");
297 goto done;
298 }
299
300 watchFD = inotify_add_watch(inotifyFD, kAlsaDeviceDir,
301 IN_CREATE | IN_DELETE);
302 if (watchFD == -1) {
303 ALOGE("AudioHotplugThread: inotify_add_watch failed");
304 goto done;
305 }
306
307 // check for any existing capture devices
308 scanForDevice();
309
310 while (!exitPending()) {
311 // wait for a change to the ALSA directory or a shutdown signal
312 struct pollfd fds[2] = {
313 { inotifyFD, POLLIN, 0 },
314 { mShutdownEventFD, POLLIN, 0 }
315 };
316 int ret = poll(fds, NELEM(fds), -1);
317 if (ret == -1) {
318 ALOGE("AudioHotplugThread: poll failed");
319 break;
320 } else if (fds[1].revents & POLLIN) {
321 // shutdown requested
322 break;
323 }
324
325 if (!(fds[0].revents & POLLIN)) {
326 continue;
327 }
328
329 // parse the filesystem change events
330 char eventBuf[256];
331 ret = read(inotifyFD, eventBuf, sizeof(eventBuf));
332 if (ret == -1) {
333 ALOGE("AudioHotplugThread: read failed");
334 break;
335 }
336
337 for (int i = 0; i < ret;) {
338 if ((ret - i) < (int)sizeof(struct inotify_event)) {
339 ALOGE("AudioHotplugThread: read an invalid inotify_event");
340 break;
341 }
342
343 struct inotify_event *event =
344 reinterpret_cast<struct inotify_event*>(eventBuf + i);
345
346 if ((ret - i) < (int)(sizeof(struct inotify_event) + event->len)) {
347 ALOGE("AudioHotplugThread: read a bad inotify_event length");
348 break;
349 }
350
351 char *name = ((char *) event) +
352 offsetof(struct inotify_event, name);
353
354 unsigned int pcmCard, pcmDevice;
355 if (parseCaptureDeviceName(name, &pcmCard, &pcmDevice)) {
356 if (event->mask & IN_CREATE) {
357 // Some devices can not be opened immediately after the
358 // inotify event occurs. Add a delay to avoid these
359 // races. (50ms was chosen arbitrarily)
360 const int kOpenTimeoutMs = 50;
361 struct pollfd pfd = {mShutdownEventFD, POLLIN, 0};
362 if (poll(&pfd, 1, kOpenTimeoutMs) == -1) {
363 ALOGE("AudioHotplugThread: poll failed");
364 break;
365 } else if (pfd.revents & POLLIN) {
366 // shutdown requested
367 break;
368 }
369
370 DeviceInfo deviceInfo;
371 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
372 mCallback.onDeviceFound(deviceInfo);
373 }
374 } else if (event->mask & IN_DELETE) {
375 mCallback.onDeviceRemoved(pcmCard, pcmDevice);
376 }
377 }
378
379 i += sizeof(struct inotify_event) + event->len;
380 }
381 }
382
383 done:
384 if (watchFD != -1) {
385 inotify_rm_watch(inotifyFD, watchFD);
386 }
387 if (inotifyFD != -1) {
388 close(inotifyFD);
389 }
390
391 return false;
392 }
393
394 }; // namespace android
395