Fix SVACE defect (SEC_INVALID_ASSIGNMENT_WITH_UNSIGNED_VALUES)
[platform/core/multimedia/libmm-sound.git] / mm_sound_keysound.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Seungbae Shin <seungbae.shin@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 <memory.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26
27 #include <mm_error.h>
28 #include <mm_debug.h>
29 #include <mm_sound.h>
30 #include <glib.h>
31
32 #include "include/mm_sound_common.h"
33
34 #ifdef USE_LWIPC
35 #include <lwipc.h>
36 #define PULSEAUDIO_READY "/tmp/.pulseaudio_ready"
37 #endif
38
39 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY     "SimplePlay"
40 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP     "SimpleStop"
41 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL "SimpleStopAll"
42
43 #define AUDIO_VOLUME_CONFIG_TYPE(vol) (vol & 0x00FF)
44 #define AUDIO_VOLUME_CONFIG_GAIN(vol) (vol & 0xFF00)
45
46 #define USE_PIPE /* default is pipe now */
47
48 #ifdef USE_PIPE
49
50 #define KEYTONE_PATH "/tmp/keytone"
51 #define FILENAME_LEN 1024
52 #define ROLE_LEN 64
53 #define VOLUME_GAIN_TYPE_LEN 32
54 #define METHOD_LEN 32
55
56 #define MAX_WRITE_RETRY 50
57 #define WRITE_RETRY_INTERVAL_MS 20
58
59 typedef struct ipc_data {
60         char filename[FILENAME_LEN];
61         char role[ROLE_LEN];
62         char volume_gain_type[VOLUME_GAIN_TYPE_LEN];
63         char method[METHOD_LEN];
64 } ipc_t;
65
66 static int __mm_sound_simple_pipe(const char *filename, int volume_config, const char *method);
67 #else /* USE_PIPE */
68 #include <gio/gio.h>
69
70 #define PA_BUS_NAME                                 "org.pulseaudio.Server"
71 #define PA_SOUND_PLAYER_OBJECT_PATH                 "/org/pulseaudio/SoundPlayer"
72 #define PA_SOUND_PLAYER_INTERFACE                   "org.pulseaudio.SoundPlayer"
73 #define PA_SOUND_RESPONSE_TIMEOUT                   2000
74
75 static int __mm_sound_simple_dbus(const char *filename, int volume_config, const char *method);
76 #endif /* USE_PIPE */
77
78 static const char* __convert_volume_type_to_role(int volume_type)
79 {
80         debug_log("volume_type(%d)", volume_type);
81         switch (volume_type) {
82         case VOLUME_TYPE_MEDIA:
83                 return "media";
84         case VOLUME_TYPE_SYSTEM:
85                 return "system";
86         case VOLUME_TYPE_NOTIFICATION:
87                 return "notification";
88         case VOLUME_TYPE_ALARM:
89                 return "alarm";
90         case VOLUME_TYPE_VOICE:
91                 return "voice";
92         case VOLUME_TYPE_RINGTONE:
93                 return "ringtone-call";
94         default:
95                 debug_warning("not supported type(%d), we change it SYSTEM type forcibly", volume_type);
96                 return "system";
97         }
98 }
99
100 static const char* __convert_volume_gain_type_to_string(int volume_gain_type)
101 {
102         debug_log("volume_gain_type(0x%x)", volume_gain_type);
103         switch (volume_gain_type) {
104         case VOLUME_GAIN_DEFAULT:
105                 return "";
106         case VOLUME_GAIN_DIALER:
107                 return "dialer";
108         case VOLUME_GAIN_TOUCH:
109                 return "touch";
110         case VOLUME_GAIN_AF:
111                 return "af";
112         case VOLUME_GAIN_SHUTTER1:
113                 return "shutter1";
114         case VOLUME_GAIN_SHUTTER2:
115                 return "shutter2";
116         case VOLUME_GAIN_CAMCORDING:
117                 return "camcording";
118         case VOLUME_GAIN_MIDI:
119                 return "midi";
120         case VOLUME_GAIN_BOOTING:
121                 return "booting";
122         case VOLUME_GAIN_VIDEO:
123                 return "video";
124         case VOLUME_GAIN_TTS:
125                 return "tts";
126         default:
127                 return "";
128         }
129 }
130
131 EXPORT_API
132 int mm_sound_play_keysound(const char *filename, int volume_config)
133 {
134 #ifdef USE_PIPE
135         return __mm_sound_simple_pipe(filename, volume_config, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY);
136 #else
137         return __mm_sound_simple_dbus(filename, volume_config, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY);
138 #endif
139 }
140
141 EXPORT_API
142 int mm_sound_stop_keysound(const char *filename)
143 {
144 #ifdef USE_PIPE
145         return __mm_sound_simple_pipe(filename, 0,
146                         (filename) ? PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP : PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL);
147 #else
148         return __mm_sound_simple_dbus(filename, 0,
149                         (filename) ? PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP : PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL);
150 #endif
151 }
152
153 #ifdef USE_LWIPC
154 static bool _mm_sound_check_pa_ready()
155 {
156         int ret = 0;
157         static bool is_pa_ready = false;
158
159         if (is_pa_ready)
160                 return true;
161
162         debug_msg("LwipcIsDone start >> ");
163         ret = LwipcIsDone(PULSEAUDIO_READY);
164         debug_msg("LwipcIsDone end << %d", ret);
165
166         is_pa_ready = (ret > 0) ? true : false;
167
168         return is_pa_ready;
169 }
170 #endif
171
172 static int __verify_input_file(const char *filename)
173 {
174         if (!filename)
175                 return MM_ERROR_SOUND_INVALID_FILE;
176
177         /* Check whether file exists */
178         if (access(filename, F_OK) == -1) {
179                 char str_error[256];
180
181                 strerror_r(errno, str_error, sizeof(str_error));
182                 debug_error("file [%s] doesn't exists : [%s][%d]", filename, str_error, errno);
183
184                 return MM_ERROR_SOUND_FILE_NOT_FOUND;
185         }
186
187         return MM_ERROR_NONE;
188 }
189
190 #ifdef USE_PIPE
191 static int __mm_sound_simple_pipe(const char *filename, int volume_config, const char *method)
192 {
193         int ret = MM_ERROR_NONE;
194         int retry_remaining = MAX_WRITE_RETRY;
195         size_t size_to_write = sizeof(ipc_t);
196         ssize_t written = 0;
197         int fd = -1;
198         ipc_t data = { 0, };
199
200 #ifdef USE_LWIPC
201         if (!_mm_sound_check_pa_ready()) {
202                 debug_error("Pulseaudio is not ready!");
203                 return MM_ERROR_SOUND_INVALID_STATE;
204         }
205 #endif
206
207         if (!method)
208                 return MM_ERROR_INVALID_ARGUMENT;
209
210         /* Prepare data to be send */
211         g_strlcpy(data.method, method, METHOD_LEN);
212
213         if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL) != 0) {
214                 ret = __verify_input_file(filename);
215                 if (ret != MM_ERROR_NONE)
216                         return ret;
217
218                 g_strlcpy(data.filename, filename, FILENAME_LEN);
219         }
220
221         if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY) == 0) {
222                 g_strlcpy(data.role,
223                                 __convert_volume_type_to_role(AUDIO_VOLUME_CONFIG_TYPE(volume_config)),
224                                 ROLE_LEN);
225                 g_strlcpy(data.volume_gain_type,
226                                 __convert_volume_gain_type_to_string(AUDIO_VOLUME_CONFIG_GAIN(volume_config)),
227                                 VOLUME_GAIN_TYPE_LEN);
228         }
229
230         debug_msg("filepath=[%s], role=[%s], volume_gain_type=[%s], method=[%s]",
231                         data.filename, data.role, data.volume_gain_type, data.method);
232
233         /* Open PIPE */
234         fd = open(KEYTONE_PATH, O_WRONLY | O_NONBLOCK);
235         if (fd == -1) {
236                 char str_error[256];
237
238                 strerror_r(errno, str_error, sizeof(str_error));
239                 debug_error("Fail to open pipe: [%s][%d]", str_error, errno);
240
241                 return MM_ERROR_SOUND_INTERNAL;
242         }
243
244         /* Write to PIPE */
245         do {
246                 written = write(fd, &data, size_to_write);
247                 if (written == -1) {
248                         char str_error[256];
249                         int errsv = errno;
250
251                         strerror_r(errsv, str_error, sizeof(str_error));
252                         debug_error("[%d] Fail to write, written = %zd, [%s][%d]",
253                                                 MAX_WRITE_RETRY - retry_remaining, written, str_error, errsv);
254
255                         if (errsv != EAGAIN || --retry_remaining == 0) {
256                                 ret = MM_ERROR_SOUND_INTERNAL;
257                                 break;
258                         }
259
260                         usleep(WRITE_RETRY_INTERVAL_MS * 1000);
261                 } else if (retry_remaining != MAX_WRITE_RETRY) {
262                         debug_msg("retry success!!");
263                 }
264         } while (written != size_to_write);
265
266         /* Close PIPE */
267         close(fd);
268
269         return ret;
270 }
271
272 #else /* USE_PIPE */
273 static int __mm_sound_simple_dbus(const char *filename, int volume_config, const char *method)
274 {
275         int ret = MM_ERROR_NONE;
276         const char *role = NULL;
277         const char *vol_gain_type = NULL;
278         GVariant *result = NULL;
279         GDBusConnection *conn = NULL;
280         GError *err = NULL;
281         int idx = 0;
282
283 #ifdef USE_LWIPC
284         if (!_mm_sound_check_pa_ready()) {
285                 debug_error("Pulseaudio is not ready!");
286                 return MM_ERROR_SOUND_INVALID_STATE;
287         }
288 #endif
289         if (!method)
290                 return MM_ERROR_INVALID_ARGUMENT;
291
292         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
293         if (!conn || err) {
294                 debug_error("g_bus_get_sync() error (%s)", err ? err->message : NULL);
295                 g_error_free(err);
296                 return MM_ERROR_SOUND_INTERNAL;
297         }
298
299         if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY) == 0) {
300                 ret = __verify_input_file(filename);
301                 if (ret != MM_ERROR_NONE)
302                         goto END;
303
304                 role = __convert_volume_type_to_role(AUDIO_VOLUME_CONFIG_TYPE(volume_config));
305                 vol_gain_type = __convert_volume_gain_type_to_string(AUDIO_VOLUME_CONFIG_GAIN(volume_config));
306
307                 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
308                                                                                         PA_SOUND_PLAYER_OBJECT_PATH,
309                                                                                         PA_SOUND_PLAYER_INTERFACE,
310                                                                                         PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY,
311                                                                                         g_variant_new("(sss)", filename, role, vol_gain_type),
312                                                                                         NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
313                 if (!result || err) {
314                         debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
315                         ret = MM_ERROR_SOUND_INTERNAL;
316                         goto END;
317                 }
318
319                 g_variant_get(result, "(i)", &idx);
320                 if (idx == -1) {
321                         debug_error("[%s] failure, filename(%s)/role(%s)/gain(%s)/stream idx(%d)",
322                                         method, filename, role, vol_gain_type, idx);
323                         ret = MM_ERROR_SOUND_INTERNAL;
324                         goto END;
325                 }
326                 debug_msg("[%s] success, filename(%s)/role(%s)/gain(%s)/stream idx(%d)",
327                                 method, filename, role, vol_gain_type, idx);
328
329         } else if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP) == 0) {
330                 ret = __verify_input_file(filename);
331                 if (ret != MM_ERROR_NONE)
332                         goto END;
333
334                 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
335                                                                                         PA_SOUND_PLAYER_OBJECT_PATH,
336                                                                                         PA_SOUND_PLAYER_INTERFACE,
337                                                                                         PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP,
338                                                                                         g_variant_new("(s)", filename),
339                                                                                         NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
340                 if (!result || err) {
341                         debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
342                         ret = MM_ERROR_SOUND_INTERNAL;
343                         goto END;
344                 }
345                 debug_msg("[%s] done, filename(%s)", method, filename);
346
347         } else if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL) == 0) {
348                 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
349                                                                                         PA_SOUND_PLAYER_OBJECT_PATH,
350                                                                                         PA_SOUND_PLAYER_INTERFACE,
351                                                                                         PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL,
352                                                                                         NULL,
353                                                                                         NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
354                 if (!result || err) {
355                         debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
356                         ret = MM_ERROR_SOUND_INTERNAL;
357                         goto END;
358                 }
359                 debug_msg("[%s] done", method);
360
361         } else {
362                 debug_error("Invalid Method : %s", method);
363                 ret = MM_ERROR_INVALID_ARGUMENT;
364         }
365
366 END:
367         if (err)
368                 g_error_free(err);
369         if (result)
370                 g_variant_unref(result);
371         if (conn)
372                 g_object_unref(conn);
373
374         return ret;
375 }
376 #endif /* USE_PIPE */