seperate sound device from feedback internal code
[platform/core/system/libsvi.git] / src / sound.c
1 /*
2  * libfeedback
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
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
19 #include <stdio.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <limits.h>
27 #include <vconf.h>
28 #include <mm_sound_private.h>
29
30 #include "feedback.h"
31 #include "feedback-internal.h"
32 #include "feedback-str.h"
33 #include "feedback-log.h"
34 #include "devices.h"
35
36 #define FEEDBACK_SOUND_DIR                      FEEDBACK_DATA_DIR"/sound"
37 #define FEEDBACK_SOUND_TOUCH_DIR        FEEDBACK_SOUND_DIR"/touch"
38 #define FEEDBACK_SOUND_OPER_DIR         FEEDBACK_SOUND_DIR"/operation"
39
40 static const char* sound_file[] = {
41         /* TOUCH : SCREEN TOUCH : TAP(TOUCH & RELEASE) : GENERAL */
42         FEEDBACK_SOUND_TOUCH_DIR"/touch.wav",
43         /* TOUCH : SCREEN TOUCH : TAP(TOUCH & RELEASE) : TEXT_NUMERIC_INPUT */
44         FEEDBACK_SOUND_TOUCH_DIR"/sip.wav",
45         FEEDBACK_SOUND_TOUCH_DIR"/sip_backspace.wav",
46         FEEDBACK_SOUND_TOUCH_DIR"/sip.wav",
47         /* TOUCH : SCREEN TOUCH : TAP(TOUCH & RELEASE) : DAILER */
48         FEEDBACK_SOUND_TOUCH_DIR"/key0.wav",
49         FEEDBACK_SOUND_TOUCH_DIR"/key1.wav",
50         FEEDBACK_SOUND_TOUCH_DIR"/key2.wav",
51         FEEDBACK_SOUND_TOUCH_DIR"/key3.wav",
52         FEEDBACK_SOUND_TOUCH_DIR"/key4.wav",
53         FEEDBACK_SOUND_TOUCH_DIR"/key5.wav",
54         FEEDBACK_SOUND_TOUCH_DIR"/key6.wav",
55         FEEDBACK_SOUND_TOUCH_DIR"/key7.wav",
56         FEEDBACK_SOUND_TOUCH_DIR"/key8.wav",
57         FEEDBACK_SOUND_TOUCH_DIR"/key9.wav",
58         FEEDBACK_SOUND_TOUCH_DIR"/keyasterisk.wav",
59         FEEDBACK_SOUND_TOUCH_DIR"/keysharp.wav",
60         /* TOUCH : H/W OR SOFT TOUCH : HOLD(TAP & HOLD) */
61         NULL,
62         /* TOUCH : H/W OR SOFT TOUCH : MULTI TAP */
63         NULL,
64         /* TOUCH : H/W OR SOFT TOUCH : TAP */
65         NULL,
66         /* TOUCH : H/W OR SOFT TOUCH : TAP & HOLD */
67         NULL,
68
69         /* NOTIFICATION : INCOMING : MESSAGE */
70         NULL,
71         /* NOTIFICATION : INCOMING : MESSAGE ALERT ON CALL */
72         NULL,
73         /* NOTIFICATION : INCOMING : EMAIL */
74         NULL,
75         /* NOTIFICATION : INCOMING : EMAIL ALERT ON CALL */
76         NULL,
77         /* NOTIFICATION : ALARM : WAKEUP */
78         NULL,
79         /* NOTIFICATION : ALARM : WAKEUP ALERT ON CALL */
80         NULL,
81         /* NOTIFICATION : ALARM : SCHEDULE */
82         NULL,
83         /* NOTIFICATION : ALARM : SCHEDULE ALERT ON CALL */
84         NULL,
85         /* NOTIFICATION : ALARM : TIMER */
86         NULL,
87         /* NOTIFICATION : ALARM : TIMER ALERT ON CALL */
88         NULL,
89         /* NOTIFICATION : GENERAL(TICKER/IM/SMS ETC) */
90         FEEDBACK_SOUND_OPER_DIR"/call_connect.wav",
91         /* NOTIFICATION : GENERAL(TICKER/IM/SMS ETC) ALERT ON CALL */
92         FEEDBACK_SOUND_OPER_DIR"/call_connect.wav",
93
94         /* OPERATION : POWER ON/OFF */
95         FEEDBACK_SOUND_OPER_DIR"/power_on.wav",
96         NULL,
97         /* OPERATION : CHARGECONN */
98         FEEDBACK_SOUND_OPER_DIR"/charger_connection.wav",
99         /* OPERATION : CHARGECONN ALERT ON CALL */
100         FEEDBACK_SOUND_OPER_DIR"/charger_connection.wav",
101         /* OPERATION : FULLCHAREGED */
102         FEEDBACK_SOUND_OPER_DIR"/fully_charged.wav",
103         /* OPERATION : FULLCHAREGED ALERT ON CALL */
104         FEEDBACK_SOUND_OPER_DIR"/fully_charged.wav",
105         /* OPERATION : LOW BATTERY */
106         FEEDBACK_SOUND_OPER_DIR"/low_battery.wav",
107         /* OPERATION : LOW BATTERY ALERT ON CALL */
108         FEEDBACK_SOUND_OPER_DIR"/low_battery.wav",
109         /* OPERATION : LOCK/UNLOCK */
110         FEEDBACK_SOUND_OPER_DIR"/lock.wav",
111         FEEDBACK_SOUND_OPER_DIR"/unlock.wav",
112         /* OPERATION : CALL CONNECT/ DISCONNECT */
113         FEEDBACK_SOUND_OPER_DIR"/call_connect.wav",
114         FEEDBACK_SOUND_OPER_DIR"/call_disconnect.wav",
115         /* OPERATION : MINUTE MINDER */
116         FEEDBACK_SOUND_OPER_DIR"/minute_minder.wav",
117         /* OPERATION : VIBRATION */
118         NULL,
119         /* OPERATION : CAMERA SHUTTER / SCREEN CAPTURE */
120         FEEDBACK_SOUND_OPER_DIR"/shutter.wav",
121         /* OPERATION : LIST RE-ORDER */
122         FEEDBACK_SOUND_OPER_DIR"/list_reorder.wav",
123         /* OPERATION : LIST SLIDER */
124         FEEDBACK_SOUND_OPER_DIR"/slider_sweep.wav",
125         /* OPERATION : VOLUME KEY */
126         FEEDBACK_SOUND_OPER_DIR"/volume_control.wav",
127 };
128
129 static int sndstatus;
130 static int touch_sndstatus;
131 static int soundon;
132
133 static void feedback_sndstatus_cb(keynode_t *key, void* data)
134 {
135         sndstatus = vconf_keynode_get_bool(key);
136 }
137
138 static void feedback_touch_sndstatus_cb(keynode_t *key, void* data)
139 {
140         touch_sndstatus = vconf_keynode_get_bool(key);
141 }
142
143 static void feedback_soundon_cb(keynode_t *key, void* data)
144 {
145         soundon = vconf_keynode_get_int(key);
146 }
147
148 static volume_type_t get_volume_type(feedback_pattern_e pattern)
149 {
150         if (pattern == FEEDBACK_PATTERN_TAP)
151                 return VOLUME_TYPE_SYSTEM|VOLUME_GAIN_TOUCH;
152         else if (pattern >= FEEDBACK_PATTERN_KEY0 && pattern <= FEEDBACK_PATTERN_KEY_SHARP)
153                 return VOLUME_TYPE_SYSTEM|VOLUME_GAIN_DIALER;
154         else if (pattern == FEEDBACK_PATTERN_VOLUME_KEY)
155                 return VOLUME_TYPE_RINGTONE;
156
157         return VOLUME_TYPE_SYSTEM;
158 }
159
160 static bool get_always_alert_case(feedback_pattern_e pattern)
161 {
162         switch (pattern) {
163         case FEEDBACK_PATTERN_WAKEUP:
164         case FEEDBACK_PATTERN_WAKEUP_ON_CALL:
165                 return true;
166         default:
167                 break;
168         }
169         return false;
170 }
171
172 static int change_symlink(const char *sym_path, const char *new_path)
173 {
174         struct stat buf;
175
176         assert(sym_path != NULL && strlen(sym_path));
177         assert(new_path != NULL && strlen(new_path));
178
179         /* check symbolic link file existence */
180         if (stat(sym_path, &buf)) {
181                 FEEDBACK_ERROR("file(%s) is not presents", sym_path);
182                 return -EPERM;
183         }
184
185         if (unlink(sym_path) < 0)
186                 FEEDBACK_LOG("unlink(%s) : %s", sym_path, strerror(errno));
187
188         if (symlink(new_path, sym_path) < 0) {
189                 FEEDBACK_ERROR("symlink(%s) : %s", sym_path, strerror(errno));
190                 return -EPERM;
191         }
192
193         return 0;
194 }
195
196 static int restore_default_file(feedback_pattern_e pattern)
197 {
198         char default_path[PATH_MAX] = {0,};
199         const char *cur_path;
200         char *temp;
201         int ret;
202
203         cur_path = sound_file[pattern];
204         /* if there isn't cur_path, it already returns before calling this api */
205         if (cur_path == NULL || strlen(cur_path) == 0) {
206                 FEEDBACK_ERROR("Invalid parameter : cur_path(NULL)");
207                 return -EPERM;
208         }
209
210         temp = strcat(default_path, FEEDBACK_ORIGIN_DATA_DIR);
211         strcat(temp, cur_path+strlen(FEEDBACK_DATA_DIR));
212         FEEDBACK_LOG("default_path : %s", default_path);
213
214         ret = change_symlink(cur_path, default_path);
215         if (ret < 0) {
216                 FEEDBACK_ERROR("change_symlink is failed");
217                 return -EPERM;
218         }
219
220         return 0;
221 }
222
223 static void sound_init(void)
224 {
225         /* Sound Init */
226         if (vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &sndstatus) < 0)
227                 FEEDBACK_ERROR("vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &sndstatus) ==> FAIL!!");
228
229         if (vconf_get_bool(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL, &touch_sndstatus) < 0)
230                 FEEDBACK_ERROR("vconf_get_bool(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL, &touch_sndstatus) ==> FAIL!!");
231
232         if (vconf_get_int(VCONFKEY_SOUND_STATUS, &soundon) < 0)
233                 FEEDBACK_ERROR("vconf_get_int(VCONFKEY_SOUND_STATUS, &soundon) ==> FAIL!!");
234
235         /* add watch for status value */
236         vconf_notify_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, feedback_sndstatus_cb, NULL);
237         vconf_notify_key_changed(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL, feedback_touch_sndstatus_cb, NULL);
238         vconf_notify_key_changed(VCONFKEY_SOUND_STATUS, feedback_soundon_cb, NULL);
239 }
240
241 static void sound_exit(void)
242 {
243         /* remove watch */
244         vconf_ignore_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, feedback_sndstatus_cb);
245         vconf_ignore_key_changed(VCONFKEY_SOUND_STATUS, feedback_soundon_cb);
246         vconf_ignore_key_changed(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL, feedback_touch_sndstatus_cb);
247 }
248
249 static int sound_play(feedback_pattern_e pattern)
250 {
251         struct stat buf;
252         int retry = FEEDBACK_RETRY_CNT, ret;
253
254         if (sndstatus == 0 && !get_always_alert_case(pattern)) {
255                 FEEDBACK_LOG("Sound condition is OFF (sndstatus : %d)", sndstatus);
256                 return 0;
257         }
258
259         if (soundon == 1 && pattern >= FEEDBACK_PATTERN_TAP && pattern <= FEEDBACK_PATTERN_HW_HOLD) {
260                 FEEDBACK_LOG("Touch feedback sound doesn't work during playing sound");
261                 return 0;
262         }
263
264         if (touch_sndstatus == 0 && pattern >= FEEDBACK_PATTERN_TAP && pattern <= FEEDBACK_PATTERN_HW_HOLD) {
265                 FEEDBACK_LOG("Touch Sound condition is OFF and pattern is touch type (touch_sndstatus : %d, pattern : %s)", touch_sndstatus, str_pattern[pattern]);
266                 return 0;
267         }
268
269         if (callstatus != VCONFKEY_CALL_OFF) {
270                 pattern = feedback_get_alert_on_call_key(pattern);
271                 FEEDBACK_LOG("Call status is connected or connecting. pattern changed : %s", str_pattern[pattern]);
272         }
273
274         if (sound_file[pattern] == NULL) {
275                 FEEDBACK_LOG("This case(%s) does not play sound", str_pattern[pattern]);
276                 return 0;
277         }
278
279         if (stat(sound_file[pattern], &buf)) {
280                 FEEDBACK_ERROR("%s is not presents", sound_file[pattern]);
281                 ret = restore_default_file(pattern);
282                 if (ret < 0) {
283                         FEEDBACK_ERROR("restore_default_file(%s) error", str_pattern[pattern]);
284                         return -EPERM;
285                 }
286                 FEEDBACK_LOG("%s is restored", sound_file[pattern]);
287         }
288
289         do {
290                 ret = mm_sound_play_keysound(sound_file[pattern], get_volume_type(pattern));
291                 if (ret == MM_ERROR_NONE) {
292                         FEEDBACK_LOG("Play success! SND filename is %s", sound_file[pattern]);
293                         return 0;
294                 }
295                 FEEDBACK_ERROR("mm_sound_play_keysound() returned error(%d)", ret);
296         } while(retry--);
297
298         return -EPERM;
299 }
300
301 static int sound_get_path(feedback_pattern_e pattern, char *buf, unsigned int buflen)
302 {
303         const char *cur_path;
304         int retry = FEEDBACK_RETRY_CNT;
305
306         assert(buf != NULL && buflen > 0);
307
308         cur_path = sound_file[pattern];
309         if (cur_path == NULL) {
310                 FEEDBACK_ERROR("This pattern(%s) in sound type is not supported to play", str_pattern[pattern]);
311                 snprintf(buf, buflen, "NULL");
312                 return -ENOENT;
313         }
314
315         do {
316                 if(readlink(cur_path, buf, buflen) < 0) {
317                         FEEDBACK_ERROR("readlink is failed : %s", strerror(errno));
318                         return -EPERM;
319                 }
320         } while(retry--);
321
322         return 0;
323 }
324
325 static int sound_set_path(feedback_pattern_e pattern, char *path)
326 {
327         const char *cur_path;
328         int ret;
329
330         assert(path != NULL);
331
332         if (access(path, F_OK) != 0) {
333                 FEEDBACK_ERROR("Invalid parameter : path does not exist");
334                 return -ENOENT;
335         }
336
337         cur_path = sound_file[pattern];
338         if (cur_path == NULL) {
339                 FEEDBACK_ERROR("This pattern(%s) in sound type is not supported to play", str_pattern[pattern]);
340                 return -ENOENT;
341         }
342
343         ret = change_symlink(cur_path, path);
344         if (ret < 0) {
345                 FEEDBACK_ERROR("change_symlink is failed");
346                 return -EPERM;
347         }
348
349         return 0;
350 }
351
352 static const struct device_ops sound_device_ops = {
353         .type = FEEDBACK_TYPE_SOUND,
354         .init = sound_init,
355         .exit = sound_exit,
356         .play = sound_play,
357         .get_path = sound_get_path,
358         .set_path = sound_set_path,
359 };
360
361 DEVICE_OPS_REGISTER(&sound_device_ops);