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