Fix possible buffer overflow using strncat
[platform/core/multimedia/libmm-sound.git] / mm_sound_device.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Sangchul Lee <sc11.lee@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include <mm_debug.h>
26
27 #include "include/mm_sound.h"
28 #include "include/mm_sound_device.h"
29 #include "include/mm_sound_client.h"
30
31 #define VOLUME_TYPE_LEN 64
32
33 mm_sound_device_list_t g_device_list;
34 pthread_mutex_t g_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
35
36 static int _check_for_valid_mask (int flags)
37 {
38         int ret = MM_ERROR_NONE;
39         bool at_least_cond = false;
40
41         if (flags > 0 && flags <= MM_SOUND_DEVICE_ALL_FLAG) {
42                 if (flags & MM_SOUND_DEVICE_IO_DIRECTION_IN_FLAG)
43                         at_least_cond = true;
44                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_OUT_FLAG))
45                         at_least_cond = true;
46                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_BOTH_FLAG))
47                         at_least_cond = true;
48                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_INTERNAL_FLAG))
49                         at_least_cond = true;
50                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_EXTERNAL_FLAG))
51                         at_least_cond = true;
52                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_DEACTIVATED_FLAG))
53                         at_least_cond = true;
54                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_ACTIVATED_FLAG))
55                         at_least_cond = true;
56         } else {
57                 ret = MM_ERROR_INVALID_ARGUMENT;
58         }
59         if (!at_least_cond) {
60                 ret = MM_ERROR_INVALID_ARGUMENT;
61         }
62         if (ret) {
63                 debug_error("flags[0x%x] is not valid\n", flags);
64         }
65         return ret;
66 }
67
68 static int __convert_device_type_to_enum (char *device_type, mm_sound_device_type_e *device_type_enum)
69 {
70         int ret = MM_ERROR_NONE;
71
72         if (!device_type || !device_type_enum) {
73                 return MM_ERROR_INVALID_ARGUMENT;
74         }
75
76         if (!strncmp(device_type, "builtin-speaker", VOLUME_TYPE_LEN)) {
77                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_SPEAKER;
78         } else if (!strncmp(device_type, "builtin-receiver", VOLUME_TYPE_LEN)) {
79                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_RECEIVER;
80         } else if (!strncmp(device_type, "builtin-mic", VOLUME_TYPE_LEN)) {
81                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_MIC;
82         } else if (!strncmp(device_type, "audio-jack", VOLUME_TYPE_LEN)) {
83                 *device_type_enum = MM_SOUND_DEVICE_TYPE_AUDIOJACK;
84         } else if (!strncmp(device_type, "bt-a2dp", VOLUME_TYPE_LEN)) {
85                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_A2DP;
86         } else if (!strncmp(device_type, "bt-sco", VOLUME_TYPE_LEN)) {
87                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_SCO;
88         } else if (!strncmp(device_type, "hdmi", VOLUME_TYPE_LEN)) {
89                 *device_type_enum = MM_SOUND_DEVICE_TYPE_HDMI;
90         } else if (!strncmp(device_type, "forwarding", VOLUME_TYPE_LEN)) {
91                 *device_type_enum = MM_SOUND_DEVICE_TYPE_MIRRORING;
92         } else if (!strncmp(device_type, "usb-audio", VOLUME_TYPE_LEN)) {
93                 *device_type_enum = MM_SOUND_DEVICE_TYPE_USB_AUDIO;
94         } else {
95                 ret = MM_ERROR_INVALID_ARGUMENT;
96                 debug_error("not supported device_type(%s), err(0x%08x)", device_type, ret);
97         }
98
99         return ret;
100 }
101
102 static int __free_device_list(mm_sound_device_list_t *device_list_t)
103 {
104         if (!device_list_t)
105                 return MM_ERROR_INVALID_ARGUMENT;
106
107         debug_log("free device list %p", device_list_t);
108         g_list_free_full(device_list_t->list, g_free);
109         g_free(device_list_t);
110
111         return MM_ERROR_NONE;
112 }
113
114 EXPORT_API
115 int mm_sound_add_device_connected_callback(int flags, mm_sound_device_connected_cb func, void *user_data, unsigned int *id)
116 {
117         int ret = MM_ERROR_NONE;
118
119         if (func == NULL || id == NULL) {
120                 debug_error("argument is not valid\n");
121                 return MM_ERROR_INVALID_ARGUMENT;
122         }
123         ret = _check_for_valid_mask(flags);
124         if (ret == MM_ERROR_NONE) {
125                 ret = mm_sound_client_add_device_connected_callback(flags, func, user_data, id);
126                 if (ret < 0) {
127                         debug_error("Could not add device connected callback, ret = %x\n", ret);
128                 }
129         }
130
131         return ret;
132 }
133
134 EXPORT_API
135 int mm_sound_remove_device_connected_callback(unsigned int id)
136 {
137         int ret = MM_ERROR_NONE;
138
139         ret = mm_sound_client_remove_device_connected_callback(id);
140         if (ret < 0) {
141                 debug_error("Could not remove device connected callback, ret = %x\n", ret);
142         }
143
144         return ret;
145 }
146
147 EXPORT_API
148 int mm_sound_add_device_information_changed_callback(int flags, mm_sound_device_info_changed_cb func, void *user_data, unsigned int *id)
149 {
150         int ret = MM_ERROR_NONE;
151
152         if (func == NULL || id == NULL) {
153                 debug_error("argument is not valid\n");
154                 return MM_ERROR_INVALID_ARGUMENT;
155         }
156         ret = _check_for_valid_mask(flags);
157         if (ret == MM_ERROR_NONE) {
158                 ret = mm_sound_client_add_device_info_changed_callback(flags, func, user_data, id);
159                 if (ret < 0) {
160                         debug_error("Could not add device information changed callback, ret = %x\n", ret);
161                 }
162         }
163
164         return ret;
165 }
166
167 EXPORT_API
168 int mm_sound_remove_device_information_changed_callback(unsigned int id)
169 {
170         int ret = MM_ERROR_NONE;
171
172         ret = mm_sound_client_remove_device_info_changed_callback(id);
173         if (ret < 0) {
174                 debug_error("Could not remove device information changed callback, ret = %x\n", ret);
175         }
176
177         return ret;
178 }
179
180 EXPORT_API
181 int mm_sound_add_device_state_changed_callback(int flags, mm_sound_device_state_changed_cb func, void *user_data, unsigned int *id)
182 {
183         int ret = MM_ERROR_NONE;
184
185         if (func == NULL || id == NULL) {
186                 debug_error("argument is not valid\n");
187                 return MM_ERROR_INVALID_ARGUMENT;
188         }
189         ret = _check_for_valid_mask(flags);
190         if (ret == MM_ERROR_NONE) {
191                 ret = mm_sound_client_add_device_state_changed_callback(flags, func, user_data, id);
192                 if (ret < 0) {
193                         debug_error("Could not add device state changed callback, ret = %x\n", ret);
194                 }
195         }
196
197         return ret;
198 }
199
200 EXPORT_API
201 int mm_sound_remove_device_state_changed_callback(unsigned int id)
202 {
203         int ret = MM_ERROR_NONE;
204
205         ret = mm_sound_client_remove_device_state_changed_callback(id);
206         if (ret < 0) {
207                 debug_error("Could not remove device state changed callback, ret = %x\n", ret);
208         }
209
210         return ret;
211 }
212
213
214 EXPORT_API
215 int mm_sound_get_current_device_list(mm_sound_device_flags_e flags, MMSoundDeviceList_t *device_list)
216 {
217         int ret = MM_ERROR_NONE;
218
219         if (!device_list) {
220                 return MM_ERROR_INVALID_ARGUMENT;
221         }
222         ret = _check_for_valid_mask(flags);
223         if (ret != MM_ERROR_NONE) {
224                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
225                 return ret;
226         }
227
228         pthread_mutex_lock(&g_thread_mutex);
229
230         if (g_device_list.list != NULL) {
231                 g_list_free_full(g_device_list.list, g_free);
232                 g_device_list.list = NULL;
233         }
234
235         g_device_list.is_new_device_list = true;
236
237         ret = mm_sound_client_get_current_connected_device_list(flags, &g_device_list);
238         if (ret < 0) {
239                 debug_error("Could not get current connected device list, ret = %x\n", ret);
240                 g_device_list.list = NULL;
241         } else {
242                 *device_list = &g_device_list;
243         }
244
245         pthread_mutex_unlock(&g_thread_mutex);
246
247         return ret;
248 }
249
250 EXPORT_API
251 int mm_sound_get_device_list(int flags, MMSoundDeviceList_t *device_list)
252 {
253         int ret = MM_ERROR_NONE;
254         mm_sound_device_list_t *_device_list;
255
256         if (!device_list) {
257                 return MM_ERROR_INVALID_ARGUMENT;
258         }
259         ret = _check_for_valid_mask(flags);
260         if (ret != MM_ERROR_NONE) {
261                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
262                 return ret;
263         }
264
265         if (!(_device_list = g_malloc0(sizeof(mm_sound_device_list_t)))) {
266                 debug_error("[Client] Allocate device list failed");
267                 return MM_ERROR_SOUND_INTERNAL;
268         }
269
270         _device_list->is_new_device_list = true;
271
272         ret = mm_sound_client_get_current_connected_device_list(flags, _device_list);
273         if (ret < 0) {
274                 debug_error("Could not get current connected device list, ret = %x\n", ret);
275                 g_free(_device_list);
276         } else {
277                 *device_list = _device_list;
278         }
279
280         return ret;
281 }
282
283 EXPORT_API
284 int mm_sound_free_device_list(MMSoundDeviceList_t device_list)
285 {
286         return __free_device_list((mm_sound_device_list_t*) device_list);
287 }
288
289 EXPORT_API
290 int mm_sound_free_device(MMSoundDevice_t device_h)
291 {
292         if (device_h == NULL)
293                 return MM_ERROR_INVALID_ARGUMENT;
294
295         g_free(device_h);
296
297         return MM_ERROR_NONE;
298 }
299
300 EXPORT_API
301 int mm_sound_get_device_by_id(int device_id, MMSoundDevice_t *device_h)
302 {
303         int ret = MM_ERROR_NONE;
304         mm_sound_device_t *device = NULL;
305
306         if (device_id < 1 || device_h == NULL)
307                 return MM_ERROR_INVALID_ARGUMENT;
308
309         ret = mm_sound_client_get_device_by_id(device_id, &device);
310         if (ret < 0)
311                 debug_error("Could not get device by id, ret = %x\n", ret);
312         else
313                 *device_h = device;
314
315         return ret;
316 }
317
318 EXPORT_API
319 int mm_sound_get_next_device (MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
320 {
321         int ret = MM_ERROR_NONE;
322         mm_sound_device_list_t *device_list_t = NULL;
323         GList *node = NULL;
324         if (!device_list || !device) {
325                 return MM_ERROR_INVALID_ARGUMENT;
326         }
327         device_list_t = (mm_sound_device_list_t*) device_list;
328         if (device_list_t->is_new_device_list) {
329                 node = g_list_first(device_list_t->list);
330         } else {
331                 node = g_list_next(device_list_t->list);
332         }
333         if (!node) {
334                 ret = MM_ERROR_SOUND_NO_DATA;
335         } else {
336                 if (device_list_t->is_new_device_list) {
337                         device_list_t->is_new_device_list = false;
338                 } else {
339                         device_list_t->list = node;
340                 }
341                 *device = (mm_sound_device_t*)node->data;
342                 debug_log("next device[0x%x]\n", *device);
343         }
344         return ret;
345 }
346
347 EXPORT_API
348 int mm_sound_get_prev_device (MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
349 {
350         int ret = MM_ERROR_NONE;
351         mm_sound_device_list_t *device_list_t = NULL;
352         GList *node = NULL;
353         if (!device_list || !device) {
354                 return MM_ERROR_INVALID_ARGUMENT;
355         }
356         device_list_t = (mm_sound_device_list_t*) device_list;
357         node = g_list_previous(device_list_t->list);
358         if (!node) {
359                 ret = MM_ERROR_SOUND_NO_DATA;
360                 debug_error("Could not get previous device, ret = %x\n", ret);
361         } else {
362                 device_list_t->list = node;
363                 *device = (mm_sound_device_t*)node->data;
364                 debug_log("previous device[0x%x]\n", *device);
365         }
366         return ret;
367 }
368
369 EXPORT_API
370 int mm_sound_get_device_type(MMSoundDevice_t device_h, mm_sound_device_type_e *type)
371 {
372         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
373         if(!device || !type) {
374                 debug_error("invalid argument\n");
375                 return MM_ERROR_INVALID_ARGUMENT;
376         }
377         __convert_device_type_to_enum(device->type, type);
378         debug_log("device_handle:0x%x, type:%d\n", device, *type);
379
380         return MM_ERROR_NONE;
381 }
382
383 EXPORT_API
384 int mm_sound_get_device_io_direction(MMSoundDevice_t device_h, mm_sound_device_io_direction_e *io_direction)
385 {
386         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
387         if(!device) {
388                 debug_error("invalid handle\n");
389                 return MM_ERROR_INVALID_ARGUMENT;
390         }
391         *io_direction = device->io_direction;
392         debug_log("device_handle:0x%x, io_direction:%d (1:IN,2:OUT,3:INOUT)\n", device, *io_direction);
393
394         return MM_ERROR_NONE;
395 }
396
397 EXPORT_API
398 int mm_sound_get_device_id(MMSoundDevice_t device_h, int *id)
399 {
400         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
401         if(!device) {
402                 debug_error("invalid handle\n");
403                 return MM_ERROR_INVALID_ARGUMENT;
404         }
405         *id = device->id;
406         debug_log("device_handle:0x%x, id:%d\n", device, *id);
407
408         return MM_ERROR_NONE;
409 }
410
411 EXPORT_API
412 int mm_sound_get_device_state(MMSoundDevice_t device_h, mm_sound_device_state_e *state)
413 {
414         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
415         if(!device) {
416                 debug_error("invalid handle\n");
417                 return MM_ERROR_INVALID_ARGUMENT;
418         }
419         *state = device->state;
420         debug_log("device_handle:0x%x, state:%d (0:INACTIVATED,1:ACTIVATED)\n", device, *state);
421
422         return MM_ERROR_NONE;
423 }
424
425 EXPORT_API
426 int mm_sound_get_device_name(MMSoundDevice_t device_h, char **name)
427 {
428         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
429         if(!device) {
430                 debug_error("invalid handle\n");
431                 return MM_ERROR_INVALID_ARGUMENT;
432         }
433         *name = device->name;
434         debug_log("device_handle:0x%x, name:%s\n", device, *name);
435
436         return MM_ERROR_NONE;
437 }
438
439 EXPORT_API
440 int mm_sound_get_device_vendor_id(MMSoundDevice_t device_h, int *vendor_id)
441 {
442         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
443         if(!device) {
444                 debug_error("invalid handle\n");
445                 return MM_ERROR_INVALID_ARGUMENT;
446         }
447         *vendor_id = device->vendor_id;
448         debug_log("device_handle:0x%x, vendor id:%04x\n", device, *vendor_id);
449
450         return MM_ERROR_NONE;
451 }
452
453 EXPORT_API
454 int mm_sound_get_device_product_id(MMSoundDevice_t device_h, int *product_id)
455 {
456         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
457         if(!device) {
458                 debug_error("invalid handle\n");
459                 return MM_ERROR_INVALID_ARGUMENT;
460         }
461         *product_id = device->product_id;
462         debug_log("device_handle:0x%x, product id:%04x\n", device, *product_id);
463
464         return MM_ERROR_NONE;
465 }
466
467 EXPORT_API
468 int mm_sound_is_stream_on_device(int stream_id, MMSoundDevice_t device_h, bool *is_on)
469 {
470         int ret = MM_ERROR_NONE;
471         int i;
472         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
473         bool _is_on = false;
474
475         if(!device || !is_on) {
476                 debug_error("invalid argument\n");
477                 return MM_ERROR_INVALID_ARGUMENT;
478         }
479
480         if (device->stream_num >= 0) {
481                 debug_log("device_handle has stream id");
482                 for (i = 0; i < device->stream_num; i++) {
483                         if (device->stream_id[i] == stream_id) {
484                                 _is_on = true;
485                                 break;
486                         }
487                 }
488         } else {
489                 debug_log("device_handle dosn't have stream id");
490                 /* No information about stream in client-side, should ask to server-side */
491                 if ((ret = mm_sound_client_is_stream_on_device(stream_id, device->id, &_is_on)) < 0) {
492                         debug_error("Failed to query is stream on");
493                         return MM_ERROR_SOUND_INTERNAL;
494                 }
495         }
496
497         debug_log("device(%d) %s stream(%d)\n", device->id, _is_on ? "has" : "doesn't have", stream_id);
498         *is_on = _is_on;
499
500         return ret;
501 }