blob: 03fe8b8b8af0df47f631401c105b0aa35cef36f4 [file] [log] [blame]
Lei Qian7bf98232018-09-20 17:56:38 +08001/*
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** @author Hugo Hong
17** @version 1.0
18** @date 2018/04/01
19** @par function description:
20** - 1 bluetooth rc audio device hot plug thread
21*/
22
23#define LOG_TAG "AudioHAL:AudioHotplugThread"
xingri.gao989a73a2022-12-28 06:58:14 +000024#include <cutils/log.h>
Lei Qian7bf98232018-09-20 17:56:38 +080025
26#include <assert.h>
27#include <dirent.h>
28#include <poll.h>
29#include <sys/eventfd.h>
30#include <sys/inotify.h>
31#include <sys/ioctl.h>
32#include <sys/resource.h>
33#include <sound/asound.h>
34
35#include <utils/misc.h>
36#include <utils/String8.h>
37
38#include "AudioHotplugThread.h"
39
40#include <linux/input.h>
41#include <linux/hidraw.h>
42#include "huitong_audio.h"
43
44// This name is used to recognize the AndroidTV Remote mic so we can
45// use it for voice recognition.
46#define ANDROID_TV_REMOTE_AUDIO_DEVICE_NAME "ATVRAudio"
47// Name of the virtual sound card created by the hid driver.
48#ifndef DIA_REMOTE_AUDIO_DEVICE_ID
49#define DIA_REMOTE_AUDIO_DEVICE_ID "DIAAudio"
50#endif
51#define AUDIO_HOTPLUG_THREAD_PRIORITY (-16)
52namespace android {
53
54/*
55 * ALSA parameter manipulation routines
56 *
57 * TODO: replace this when TinyAlsa offers a suitable API
58 */
59static inline int param_is_mask(int p)
60{
61 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
62 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
63}
64
65static inline int param_is_interval(int p)
66{
67 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
68 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
69}
70
71static inline struct snd_interval *param_to_interval(
72 struct snd_pcm_hw_params *p, int n)
73{
74 assert(p->intervals);
75 if (!param_is_interval(n)) return NULL;
76 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
77}
78
79static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
80{
81 assert(p->masks);
82 if (!param_is_mask(n)) return NULL;
83 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
84}
85
86static inline void snd_mask_any(struct snd_mask *mask)
87{
88 memset(mask, 0xff, sizeof(struct snd_mask));
89}
90
91static inline void snd_interval_any(struct snd_interval *i)
92{
93 i->min = 0;
94 i->openmin = 0;
95 i->max = UINT_MAX;
96 i->openmax = 0;
97 i->integer = 0;
98 i->empty = 0;
99}
100
101static void param_init(struct snd_pcm_hw_params *p)
102{
103 int n = 0;
104
105 memset(p, 0, sizeof(*p));
106 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
107 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
108 struct snd_mask *m = param_to_mask(p, n);
109 snd_mask_any(m);
110 }
111 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
112 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
113 struct snd_interval *i = param_to_interval(p, n);
114 snd_interval_any(i);
115 }
116 p->rmask = 0xFFFFFFFF;
117}
118
119/*
120 * Hotplug thread
121 */
122
123const char* AudioHotplugThread::kThreadName = "ATVRemoteAudioHotplug";
124
125// directory where device nodes appear
126const char* AudioHotplugThread::kDeviceDir = "/dev";
127// directory where ALSA device nodes appear
128const char* AudioHotplugThread::kAlsaDeviceDir = "/dev/snd";
129
130
131// filename suffix for ALSA nodes representing capture devices
132const char AudioHotplugThread::kDeviceTypeCapture = 'c';
133
Lei Qian7bf98232018-09-20 17:56:38 +0800134AudioHotplugThread::AudioHotplugThread(Callback& callback)
135 : mCallback(callback)
wei.du06ada4c2021-06-24 04:04:01 -0400136 , mShutdownEventFD(-1)
Lei Qian7bf98232018-09-20 17:56:38 +0800137{
138}
139
140AudioHotplugThread::~AudioHotplugThread()
141{
wei.du06ada4c2021-06-24 04:04:01 -0400142 if (mShutdownEventFD != -1) {
143 ::close(mShutdownEventFD);
Lei Qian7bf98232018-09-20 17:56:38 +0800144 }
145}
146
147bool AudioHotplugThread::start()
148{
wei.du06ada4c2021-06-24 04:04:01 -0400149 mShutdownEventFD = eventfd(0, EFD_NONBLOCK);
150 if (mShutdownEventFD == -1) {
Lei Qian7bf98232018-09-20 17:56:38 +0800151 return false;
152 }
153
154 return (run(kThreadName) == NO_ERROR);
155}
156
157void AudioHotplugThread::shutdown()
158{
159 requestExit();
wei.du06ada4c2021-06-24 04:04:01 -0400160 uint64_t tmp = 1;
161 ::write(mShutdownEventFD, &tmp, sizeof(tmp));
Lei Qian7bf98232018-09-20 17:56:38 +0800162 join();
163}
164
Lei Qian7bf98232018-09-20 17:56:38 +0800165bool AudioHotplugThread::parseCaptureDeviceName(const char* name,
166 unsigned int* card,
167 unsigned int* device)
168{
169 char deviceType;
170 int ret = sscanf(name, "pcmC%uD%u%c", card, device, &deviceType);
171 return (ret == 3 && deviceType == kDeviceTypeCapture);
172}
173
174static inline void getAlsaParamInterval(const struct snd_pcm_hw_params& params,
175 int n, unsigned int* min,
176 unsigned int* max)
177{
178 struct snd_interval* interval = param_to_interval(
179 const_cast<struct snd_pcm_hw_params*>(&params), n);
180 *min = interval->min;
181 *max = interval->max;
182}
183
184// This was hacked out of "alsa_utils.cpp".
185static int s_get_alsa_card_name(char *name, size_t len, int card_id)
186{
187 int fd;
188 int amt = -1;
189 snprintf(name, len, "/proc/asound/card%d/id", card_id);
190 fd = open(name, O_RDONLY);
191 if (fd >= 0) {
192 amt = read(fd, name, len - 1);
193 if (amt > 0) {
194 // replace the '\n' at the end of the proc file with '\0'
195 name[amt - 1] = 0;
196 }
197 close(fd);
198 }
199 return amt;
200}
201
202bool AudioHotplugThread::getDeviceInfo(unsigned int pcmCard,
203 unsigned int pcmDevice,
204 DeviceInfo* info)
205{
206 bool result = false;
207 int ret = 0;
208 int len = 0;
209 char cardName[64] = "";
210
211 assert(info != NULL);
212
213 memset(info, 0, sizeof(DeviceInfo));
214
215 String8 devicePath = String8::format("%s/pcmC%dD%d%c",
216 kAlsaDeviceDir, pcmCard, pcmDevice, kDeviceTypeCapture);
217
218 ALOGD("AudioHotplugThread::getDeviceInfo opening %s", devicePath.string());
219 int alsaFD = open(devicePath.string(), O_RDONLY);
220 if (alsaFD == -1) {
221 ALOGE("AudioHotplugThread::getDeviceInfo open failed for %s", devicePath.string());
222 goto done;
223 }
224
225 // query the device's ALSA configuration space
226 struct snd_pcm_hw_params params;
227 param_init(&params);
228 ret = ioctl(alsaFD, SNDRV_PCM_IOCTL_HW_REFINE, &params);
229 if (ret == -1) {
230 ALOGE("AudioHotplugThread: refine ioctl failed");
231 goto done;
232 }
233
234 info->pcmCard = pcmCard;
235 info->pcmDevice = pcmDevice;
236 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
237 &info->minSampleBits, &info->maxSampleBits);
238 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_CHANNELS,
239 &info->minChannelCount, &info->maxChannelCount);
240 getAlsaParamInterval(params, SNDRV_PCM_HW_PARAM_RATE,
241 &info->minSampleRate, &info->maxSampleRate);
242
243 // Ugly hack to recognize Remote mic and mark it for voice recognition
244 info->forVoiceRecognition = false;
245 len = s_get_alsa_card_name(cardName, sizeof(cardName), pcmCard);
246 ALOGD("AudioHotplugThread get_alsa_card_name returned %d, %s", len, cardName);
247 if (len > 0) {
248 if (strcmp(DIA_REMOTE_AUDIO_DEVICE_ID, cardName) == 0) {
249 ALOGD("AudioHotplugThread found Android TV remote mic on Card %d, for VOICE_RECOGNITION", pcmCard);
250 info->forVoiceRecognition = true;
251 }
252 }
253
254 result = info->forVoiceRecognition;
255
256done:
257 if (alsaFD != -1) {
258 close(alsaFD);
259 }
260 return result;
261}
262
263bool AudioHotplugThread::getDeviceInfo(const unsigned int hidrawIndex,
264 DeviceInfo* info)
265{
266 bool result = false;
267 struct hidraw_devinfo devInfo;
268 char devicePath[32] = {0};
269 int fd = -1;
270
271 assert(info != NULL);
272
273 memset(info, 0, sizeof(DeviceInfo));
274
275 sprintf(devicePath, "/dev/hidraw%d", hidrawIndex);
276
277 //check hidraw
278 if (0 != access(devicePath, F_OK)) {
279 //ALOGE("%s could not access %s, %s\n", __FUNCTION__, devicePath, strerror(errno));
280 goto done;
281 }
282
283 fd = open(devicePath, O_RDWR|O_NONBLOCK);
284 if (fd <= 0) {
285 //ALOGE("%s could not open %s, %s\n", __FUNCTION__, devicePath, strerror(errno));
286 goto done;
287 }
288 memset(&devInfo, 0, sizeof(struct hidraw_devinfo));
289 if (ioctl(fd, HIDIOCGRAWINFO, &devInfo)) {
290 goto done;
291 }
292
293 ALOGD("%s info.bustype:0x%x, info.vendor:0x%x, info.product:0x%x\n",
294 __FUNCTION__, devInfo.bustype, devInfo.vendor, devInfo.product);
295
296 /*please define array for differnt vid&pid with the same rc platform*/
297 if (devInfo.bustype == BUS_BLUETOOTH) {
wei.du06ada4c2021-06-24 04:04:01 -0400298 info->forVoiceRecognition = true;
Lei Qian7bf98232018-09-20 17:56:38 +0800299 info->hidraw_index = hidrawIndex;
300 info->hidraw_device = devInfo.bustype;
301 result = true;
Lei Qian7bf98232018-09-20 17:56:38 +0800302 }
303
304done:
305 if (fd > 0) {
306 close(fd);
307 }
308
309 return result;
310}
311
312// scan the ALSA device directory for a usable capture device
313void AudioHotplugThread::scanSoundCardDevice()
314{
315 DIR* alsaDir;
316 DeviceInfo deviceInfo;
317
318 alsaDir = opendir(kAlsaDeviceDir);
319 if (alsaDir == NULL)
320 return;
321
322 while (true) {
323 struct dirent *entry = NULL;
324 entry = readdir(alsaDir);
325 if (entry == NULL)
326 break;
327 unsigned int pcmCard, pcmDevice;
328 if (parseCaptureDeviceName(entry->d_name, &pcmCard, &pcmDevice)) {
329 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
330 mCallback.onDeviceFound(deviceInfo);
331 }
332 }
333 }
334
335 closedir(alsaDir);
336}
337
338void AudioHotplugThread::scanHidrawDevice()
339{
340 DeviceInfo deviceInfo;
341
342 for (unsigned int i=0; i < MAX_HIDRAW_ID; i++) {
343 if (getDeviceInfo(i, &deviceInfo)) {
344 mCallback.onDeviceFound(deviceInfo, true);
345 }
346 }
347}
348
349void AudioHotplugThread::scanForDevice() {
350 scanHidrawDevice();
351 scanSoundCardDevice();
352}
353
354void AudioHotplugThread::handleHidrawEvent(struct inotify_event *event) {
355 unsigned int hidrawId = MAX_HIDRAW_ID;
356 char *name = ((char *) event) + offsetof(struct inotify_event, name);
357
358 if (strstr(name, "hidraw") == NULL) {
359 //no hidraw event, do nothing
360 return;
361 }
362 int ret = sscanf(name, "hidraw%d", &hidrawId);
363 if (ret == 1 && hidrawId < MAX_HIDRAW_ID) {
364 if (event->mask & IN_CREATE) {
365 // Some devices can not be opened immediately after the
366 // inotify event occurs. Add a delay to avoid these
367 // races. (50ms was chosen arbitrarily)
wei.du06ada4c2021-06-24 04:04:01 -0400368 /*
Lei Qian7bf98232018-09-20 17:56:38 +0800369 const int kOpenTimeoutMs = 50;
wei.du06ada4c2021-06-24 04:04:01 -0400370 struct pollfd pfd = {mShutdownEventFD, POLLIN, 0};
Lei Qian7bf98232018-09-20 17:56:38 +0800371 if (poll(&pfd, 1, kOpenTimeoutMs) == -1) {
372 ALOGE("AudioHotplugThread: poll failed");
373 return;
374 } else if (pfd.revents & POLLIN) {
375 // shutdown requested
376 return;
wei.du06ada4c2021-06-24 04:04:01 -0400377 }*/
Lei Qian7bf98232018-09-20 17:56:38 +0800378
379 DeviceInfo deviceInfo;
380 if (getDeviceInfo(hidrawId, &deviceInfo)) {
381 mCallback.onDeviceFound(deviceInfo, true);
382 }
383 }
384 else if (event->mask & IN_DELETE) {
385 mCallback.onDeviceRemoved(hidrawId);
386 }
387 }
388
389}
390
391void AudioHotplugThread::handleSoundCardEvent(struct inotify_event *event) {
392 char *name = ((char *) event) + offsetof(struct inotify_event, name);
393 unsigned int pcmCard, pcmDevice;
394
395 if (parseCaptureDeviceName(name, &pcmCard, &pcmDevice)) {
396 if (event->mask & IN_CREATE) {
397 // Some devices can not be opened immediately after the
398 // inotify event occurs. Add a delay to avoid these
399 // races. (50ms was chosen arbitrarily)
400 const int kOpenTimeoutMs = 50;
wei.du06ada4c2021-06-24 04:04:01 -0400401 struct pollfd pfd = {mShutdownEventFD, POLLIN, 0};
Lei Qian7bf98232018-09-20 17:56:38 +0800402 if (poll(&pfd, 1, kOpenTimeoutMs) == -1) {
403 ALOGE("AudioHotplugThread: poll failed");
404 return;
405 } else if (pfd.revents & POLLIN) {
406 // shutdown requested
407 return;
408 }
409
410 DeviceInfo deviceInfo;
411 if (getDeviceInfo(pcmCard, pcmDevice, &deviceInfo)) {
412 mCallback.onDeviceFound(deviceInfo);
413 }
414 }
415 else if (event->mask & IN_DELETE) {
416 mCallback.onDeviceRemoved(pcmCard, pcmDevice);
417 }
418 }
419}
420
421int AudioHotplugThread::handleDeviceEvent(int inotifyFD, int wfds[]) {
wei.du06ada4c2021-06-24 04:04:01 -0400422
423 // parse the filesystem change events
Lei Qian7bf98232018-09-20 17:56:38 +0800424 char eventBuf[256] = {0};
425 int ret = read(inotifyFD, eventBuf, sizeof(eventBuf));
426 if (ret == -1) {
427 ALOGE("AudioHotplugThread: read failed");
428 return ret;
429 }
430
431 for (int i = 0; i < ret;) {
432 if ((ret - i) < (int)sizeof(struct inotify_event)) {
433 ALOGE("AudioHotplugThread: read an invalid inotify_event");
434 break;
435 }
436
437 struct inotify_event *event =
438 reinterpret_cast<struct inotify_event*>(eventBuf + i);
439
440 if ((ret - i) < (int)(sizeof(struct inotify_event) + event->len)) {
441 ALOGE("AudioHotplugThread: read a bad inotify_event length");
442 break;
443 }
444
445 //dispatch event
446 if (event->wd == wfds[0]) {
447 //hidraw node insert
448 handleHidrawEvent(event);
449 }
450 else {
451 //sound card insert
452 handleSoundCardEvent(event);
453 }
454
455 i += sizeof(struct inotify_event) + event->len;
456 }
457
458 return ret;
459}
460
Lei Qian7bf98232018-09-20 17:56:38 +0800461bool AudioHotplugThread::threadLoop()
462{
463 int flags = 0;
Lei Qian7bf98232018-09-20 17:56:38 +0800464 int inotifyFD = -1;
465 int watchFDs[2] = {-1}; //hidraw + sound card
466 const char *devDir[2] = {kDeviceDir, kAlsaDeviceDir};
467
wei.du06ada4c2021-06-24 04:04:01 -0400468 //raise thread priority
469 setpriority(PRIO_PROCESS, gettid(), AUDIO_HOTPLUG_THREAD_PRIORITY);
470
Lei Qian7bf98232018-09-20 17:56:38 +0800471 // watch for changes to the ALSA device directory
472 inotifyFD = inotify_init();
473 if (inotifyFD == -1) {
474 ALOGE("AudioHotplugThread: inotify_init failed");
475 goto done;
476 }
477 flags = fcntl(inotifyFD, F_GETFL, 0);
478 if (flags == -1) {
479 ALOGE("AudioHotplugThread: F_GETFL failed");
480 goto done;
481 }
482
483 if (fcntl(inotifyFD, F_SETFL, flags | O_NONBLOCK) == -1) {
484 ALOGE("AudioHotplugThread: F_SETFL failed");
485 goto done;
486 }
487
488 //add watch notify
489 for (int i = 0; i < 2; i++) {
490 watchFDs[i] = inotify_add_watch(inotifyFD, devDir[i],
491 IN_CREATE | IN_DELETE);
492 if (watchFDs[i] == -1) {
493 ALOGE("AudioHotplugThread:inotify_add_watch %s failed, i=%d,err=%d", devDir[i], i, errno);
494 goto done;
495 }
496 }
497
498 // check for any existing capture devices
wei.du06ada4c2021-06-24 04:04:01 -0400499 //scanForDevice();
Lei Qian7bf98232018-09-20 17:56:38 +0800500 while (!exitPending()) {
501 // wait for a change to the ALSA directory or a shutdown signal
502 struct pollfd fds[2] = {
503 { inotifyFD, POLLIN, 0 },
wei.du06ada4c2021-06-24 04:04:01 -0400504 { mShutdownEventFD, POLLIN, 0 }
Lei Qian7bf98232018-09-20 17:56:38 +0800505 };
wei.du06ada4c2021-06-24 04:04:01 -0400506 int ret = poll(fds, NELEM(fds), -1);
Lei Qian7bf98232018-09-20 17:56:38 +0800507 if (ret == -1) {
508 ALOGE("AudioHotplugThread: poll failed");
509 break;
wei.du06ada4c2021-06-24 04:04:01 -0400510 } else if (fds[1].revents & POLLIN) {
511 // shutdown requested
512 break;
Lei Qian7bf98232018-09-20 17:56:38 +0800513 }
514
wei.du06ada4c2021-06-24 04:04:01 -0400515 if (!(fds[0].revents & POLLIN)) {
516 continue;
517 }
518
519 // parse the filesystem change events
520 ret = handleDeviceEvent(inotifyFD, watchFDs);
Lei Qian7bf98232018-09-20 17:56:38 +0800521 if (ret == -1) break;
522 }
523
524done:
525 //remove watch fds
526 for (int i = 0; i < 2; i++) {
527 if (watchFDs[i] != -1)
528 inotify_rm_watch(inotifyFD, watchFDs[i]);
529 }
530 if (inotifyFD != -1) {
531 close(inotifyFD);
532 }
533
534 return false;
535}
536
537}; // namespace android