1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "volume_listener"
18//#define LOG_NDEBUG 0
19#include <stdlib.h>
20#include <dlfcn.h>
21
22#include <cutils/list.h>
23#include <cutils/log.h>
24#include <hardware/audio_effect.h>
25#include <cutils/properties.h>
26
27#define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
28#define XSTR(x) STR(x)
29#define STR(x) #x
30
31#define VOL_FLAG ( EFFECT_FLAG_TYPE_INSERT | \
32                   EFFECT_FLAG_VOLUME_IND | \
33                   EFFECT_FLAG_DEVICE_IND | \
34                   EFFECT_FLAG_OFFLOAD_SUPPORTED)
35
36#define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\
37                                                            i == MUSIC?"MUSIC": \
38                                                            i == RING?"RING": \
39                                                            i == ALARM?"ALARM": \
40                                                            i == VOICE_CALL?"Voice_call": \
41                                                            i == NOTIFICATION?"Notification":\
42                                                            "--INVALID--"); \
43
44#define MAX_GAIN_LEVELS 5
45
46#define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration"
47
48enum {
49    VOL_LISTENER_STATE_UNINITIALIZED,
50    VOL_LISTENER_STATE_INITIALIZED,
51    VOL_LISTENER_STATE_ACTIVE,
52};
53
54typedef struct vol_listener_context_s vol_listener_context_t;
55static const struct effect_interface_s effect_interface;
56
57/* flag to avoid multiple initialization */
58static bool initialized = false;
59
60/* current gain dep cal level that was pushed succesfully */
61static int current_gain_dep_cal_level = -1;
62
63enum STREAM_TYPE {
64    MUSIC,
65    RING,
66    ALARM,
67    VOICE_CALL,
68    NOTIFICATION,
69    MAX_STREAM_TYPES,
70};
71
72struct vol_listener_context_s {
73    const struct effect_interface_s *itfe;
74    struct listnode effect_list_node;
75    effect_config_t config;
76    const effect_descriptor_t *desc;
77    uint32_t stream_type;
78    uint32_t session_id;
79    uint32_t state;
80    uint32_t dev_id;
81    float left_vol;
82    float right_vol;
83};
84
85/* volume listener, music UUID: 08b8b058-0590-11e5-ac71-0025b32654a0 */
86const effect_descriptor_t vol_listener_music_descriptor = {
87    { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // type
88    { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // uuid
89    EFFECT_CONTROL_API_VERSION,
90    VOL_FLAG,
91    0, /* TODO */
92    1,
93    "Volume listener for Music",
94    "Qualcomm Technologies Inc.",
95};
96
97/* volume listener, ring UUID: 0956df94-0590-11e5-bdbe-0025b32654a0 */
98const effect_descriptor_t vol_listener_ring_descriptor = {
99    { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
100    { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
101    EFFECT_CONTROL_API_VERSION,
102    VOL_FLAG,
103    0, /* TODO */
104    1,
105    "Volume listener for ring",
106    "Qualcomm Technologies Inc",
107};
108
109/* volume listener, alarm UUID: 09f303e2-0590-11e5-8fdb-0025b32654a0 */
110const effect_descriptor_t vol_listener_alarm_descriptor = {
111    { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
112    { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
113    EFFECT_CONTROL_API_VERSION,
114    VOL_FLAG,
115    0, /* TODO */
116    1,
117    "Volume listener for alarm",
118    "Qualcomm Technologies Inc",
119};
120
121/* volume listener, voice call UUID: 0ace5c08-0590-11e5-ae9e-0025b32654a0 */
122const effect_descriptor_t vol_listener_voice_call_descriptor = {
123    { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
124    { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
125    EFFECT_CONTROL_API_VERSION,
126    VOL_FLAG,
127    0, /* TODO */
128    1,
129    "Volume listener for voice call",
130    "Qualcomm Technologies Inc",
131};
132
133/* volume listener, notification UUID: 0b776dde-0590-11e5-81ba-0025b32654a0 */
134const effect_descriptor_t vol_listener_notification_descriptor = {
135    { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
136    { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
137    EFFECT_CONTROL_API_VERSION,
138    VOL_FLAG,
139    0, /* TODO */
140    1,
141    "Volume listener for notification",
142    "Qualcomm Technologies Inc",
143};
144
145struct amp_db_and_gain_table {
146    float amp;
147    float db;
148    uint32_t level;
149} amp_to_dBLevel_table;
150
151// using gain level for non-drc volume curve
152static const struct amp_db_and_gain_table  volume_curve_gain_mapping_table[MAX_GAIN_LEVELS] =
153{
154    /* Level 0 in the calibration database contains default calibration */
155    { 0.001774, -55, 5 },
156    { 0.501187,  -6, 4 },
157    { 0.630957,  -4, 3 },
158    { 0.794328,  -2, 2 },
159    { 1.0,        0, 1 },
160};
161
162static const effect_descriptor_t *descriptors[] = {
163    &vol_listener_music_descriptor,
164    &vol_listener_ring_descriptor,
165    &vol_listener_alarm_descriptor,
166    &vol_listener_voice_call_descriptor,
167    &vol_listener_notification_descriptor,
168    NULL,
169};
170
171pthread_once_t once = PTHREAD_ONCE_INIT;
172/* flag to indicate if init was success */
173static int init_status;
174
175/* current volume level for which gain dep cal level was selected */
176static float current_vol = 0.0;
177
178/* HAL interface to send calibration */
179static bool (*send_gain_dep_cal)(int);
180
181/* if dumping allowed */
182static bool dumping_enabled = false;
183
184/* list of created effects. */
185struct listnode vol_effect_list;
186
187/* lock must be held when modifying or accessing created_effects_list */
188pthread_mutex_t vol_listner_init_lock;
189
190/*
191 *  Local functions
192 */
193static void dump_list_l()
194{
195    struct listnode *node;
196    vol_listener_context_t *context;
197
198    ALOGW("DUMP_START :: ===========");
199
200    list_for_each(node, &vol_effect_list) {
201        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
202        // dump stream_type / Device / session_id / left / righ volume
203        ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ",
204                __func__,
205                context->stream_type == MUSIC ? "MUSIC" :
206                context->stream_type == RING ? "RING" :
207                context->stream_type == ALARM ? "ALARM" :
208                context->stream_type == VOICE_CALL ? "VOICE_CALL" :
209                context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--",
210                context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol);
211    }
212
213    ALOGW("DUMP_END :: ===========");
214}
215
216static void check_and_set_gain_dep_cal()
217{
218    // iterate through list and make decision to set new gain dep cal level for speaker device
219    // 1. find all usecase active on speaker
220    // 2. find average of left and right for each usecase
221    // 3. find the highest of all the active usecase
222    // 4. if new value is different than the current value then load new calibration
223
224    struct listnode *node = NULL;
225    float new_vol = 0.0;
226    int max_level = 0;
227    vol_listener_context_t *context = NULL;
228    if (dumping_enabled) {
229        dump_list_l();
230    }
231
232    ALOGV("%s ==> Start ...", __func__);
233
234    // select the highest volume on speaker device
235    list_for_each(node, &vol_effect_list) {
236        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
237        if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
238            (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
239            (new_vol < (context->left_vol + context->right_vol) / 2)) {
240            new_vol = (context->left_vol + context->right_vol) / 2;
241        }
242    }
243
244    if (new_vol != current_vol) {
245        ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",
246              __func__, current_vol, new_vol);
247
248        if (send_gain_dep_cal != NULL) {
249            // send Gain dep cal level
250            int gain_dep_cal_level = -1;
251
252            if (new_vol >= 1) { // max amplitude, use highest DRC level
253                gain_dep_cal_level = volume_curve_gain_mapping_table[MAX_GAIN_LEVELS - 1].level;
254            } else if (new_vol <= 0) {
255                gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
256            } else {
257                for (max_level = 0; max_level + 1 < MAX_GAIN_LEVELS; max_level++) {
258                    if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
259                        new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
260                        gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
261                        ALOGV("%s: volume(%f), gain dep cal selcetd %d ",
262                              __func__, current_vol, gain_dep_cal_level);
263                        break;
264                    }
265                }
266            }
267
268            // check here if previous gain dep cal level was not same
269            if (gain_dep_cal_level != -1) {
270                if (gain_dep_cal_level != current_gain_dep_cal_level) {
271                    // decision made .. send new level now
272                    if (!send_gain_dep_cal(gain_dep_cal_level)) {
273                        ALOGE("%s: Failed to set gain dep cal level", __func__);
274                    } else {
275                        // Success in setting the gain dep cal level, store new level and Volume
276                        if (dumping_enabled) {
277                            ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)",
278                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
279                                  gain_dep_cal_level);
280                        } else {
281                            ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)",
282                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
283                                  gain_dep_cal_level);
284                        }
285                        current_gain_dep_cal_level = gain_dep_cal_level;
286                        current_vol = new_vol;
287                    }
288                } else {
289                    if (dumping_enabled) {
290                        ALOGW("%s: volume changed but gain dep cal level is still the same",
291                              __func__);
292                    } else {
293                        ALOGV("%s: volume changed but gain dep cal level is still the same",
294                              __func__);
295                    }
296                }
297            } else {
298                ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol);
299            }
300        } else {
301            ALOGE("%s: not able to send calibration, NULL function pointer",
302                  __func__);
303        }
304    } else {
305        ALOGV("%s:: volume not changed, stick to same config ..... ", __func__);
306    }
307
308    ALOGV("check_and_set_gain_dep_cal ==> End ");
309}
310
311/*
312 * Effect Control Interface Implementation
313 */
314
315static inline int16_t clamp16(int32_t sample)
316{
317    if ((sample>>15) ^ (sample>>31))
318        sample = 0x7FFF ^ (sample>>31);
319    return sample;
320}
321
322static int vol_effect_process(effect_handle_t self,
323                              audio_buffer_t *in_buffer,
324                              audio_buffer_t *out_buffer)
325{
326    int status = 0;
327    ALOGV("%s Called ", __func__);
328
329    vol_listener_context_t *context = (vol_listener_context_t *)self;
330    pthread_mutex_lock(&vol_listner_init_lock);
331
332    if (context->state != VOL_LISTENER_STATE_ACTIVE) {
333        ALOGE("%s: state is not active .. return error", __func__);
334        status = -EINVAL;
335        goto exit;
336    }
337
338    // calculation based on channel count 2
339    if (in_buffer->raw != out_buffer->raw) {
340        if (context->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
341            size_t i;
342            for (i = 0; i < out_buffer->frameCount*2; i++) {
343                out_buffer->s16[i] = clamp16(out_buffer->s16[i] + in_buffer->s16[i]);
344            }
345        } else {
346            memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
347        }
348
349    }
350
351exit:
352    pthread_mutex_unlock(&vol_listner_init_lock);
353    return status;
354}
355
356
357static int vol_effect_command(effect_handle_t self,
358                              uint32_t cmd_code, uint32_t cmd_size,
359                              void *p_cmd_data, uint32_t *reply_size,
360                              void *p_reply_data)
361{
362    vol_listener_context_t *context = (vol_listener_context_t *)self;
363    int status = 0;
364
365    ALOGV("%s Called ", __func__);
366    pthread_mutex_lock(&vol_listner_init_lock);
367
368    if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) {
369        ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
370              "context" : "context->state");
371        status = -EINVAL;
372        goto exit;
373    }
374
375    switch (cmd_code) {
376    case EFFECT_CMD_INIT:
377        ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
378        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
379            ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
380                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
381                  "*reply_size != sizeof(int)");
382            return -EINVAL;
383        }
384        *(int *)p_reply_data = 0;
385        break;
386
387    case EFFECT_CMD_SET_CONFIG:
388        ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
389        if (p_cmd_data == NULL || cmd_size != sizeof(effect_config_t)
390                || p_reply_data == NULL || reply_size == NULL || *reply_size != sizeof(int)) {
391            return -EINVAL;
392        }
393        context->config = *(effect_config_t *)p_cmd_data;
394        *(int *)p_reply_data = 0;
395        break;
396
397    case EFFECT_CMD_GET_CONFIG:
398        ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
399        break;
400
401    case EFFECT_CMD_RESET:
402        ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
403        break;
404
405    case EFFECT_CMD_SET_AUDIO_MODE:
406        ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
407        break;
408
409    case EFFECT_CMD_OFFLOAD:
410        ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
411        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
412            ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
413                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
414                  "*reply_size != sizeof(int)");
415            return -EINVAL;
416        }
417        *(int *)p_reply_data = 0;
418        break;
419
420    case EFFECT_CMD_ENABLE:
421        ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
422        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
423            ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
424                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
425                   "*reply_size != sizeof(int)");
426            status = -EINVAL;
427            goto exit;
428        }
429
430        if (context->state != VOL_LISTENER_STATE_INITIALIZED) {
431            ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
432            status = -ENOSYS;
433            goto exit;
434        }
435
436        context->state = VOL_LISTENER_STATE_ACTIVE;
437        *(int *)p_reply_data = 0;
438
439        // After changing the state and if device is speaker
440        // recalculate gain dep cal level
441        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
442                check_and_set_gain_dep_cal();
443        }
444
445        break;
446
447    case EFFECT_CMD_DISABLE:
448        ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
449        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
450            ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
451                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
452                  "*reply_size != sizeof(int)");
453            status = -EINVAL;
454            goto exit;
455        }
456
457        if (context->state != VOL_LISTENER_STATE_ACTIVE) {
458            ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
459            status = -ENOSYS;
460            goto exit;
461        }
462
463        context->state = VOL_LISTENER_STATE_INITIALIZED;
464        *(int *)p_reply_data = 0;
465
466        // After changing the state and if device is speaker
467        // recalculate gain dep cal level
468        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
469            check_and_set_gain_dep_cal();
470        }
471
472        break;
473
474    case EFFECT_CMD_GET_PARAM:
475        ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
476        break;
477
478    case EFFECT_CMD_SET_PARAM:
479        ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
480        break;
481
482    case EFFECT_CMD_SET_DEVICE:
483    {
484        uint32_t new_device;
485        bool recompute_gain_dep_cal_Level = false;
486        ALOGV("cmd called EFFECT_CMD_SET_DEVICE ");
487
488        if (p_cmd_data == NULL) {
489            ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
490            status = -EINVAL;
491            goto exit;
492        }
493
494        new_device = *(uint32_t *)p_cmd_data;
495        ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
496               __func__, context->dev_id, new_device);
497
498        // check if old or new device is speaker
499        if ((context->dev_id ==  AUDIO_DEVICE_OUT_SPEAKER) ||
500            (new_device == AUDIO_DEVICE_OUT_SPEAKER)) {
501            recompute_gain_dep_cal_Level = true;
502        }
503
504        context->dev_id = new_device;
505
506        if (recompute_gain_dep_cal_Level) {
507            check_and_set_gain_dep_cal();
508        }
509    }
510    break;
511
512    case EFFECT_CMD_SET_VOLUME:
513    {
514        float left_vol = 0, right_vol = 0;
515        bool recompute_gain_dep_cal_Level = false;
516
517        ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
518        if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
519            ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
520                  "p_cmd_data is NULL" : "cmd_size issue");
521            status = -EINVAL;
522            goto exit;
523        }
524
525        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
526            recompute_gain_dep_cal_Level = true;
527        }
528
529        left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
530        right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
531        ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
532              context->right_vol, left_vol, right_vol);
533
534        context->left_vol = left_vol;
535        context->right_vol = right_vol;
536
537        // recompute gan dep cal level only if volume changed on speaker device
538        if (recompute_gain_dep_cal_Level) {
539            check_and_set_gain_dep_cal();
540        }
541    }
542    break;
543
544    default:
545        ALOGW("volume_listener_command invalid command %d", cmd_code);
546        status = -ENOSYS;
547        break;
548    }
549
550exit:
551    pthread_mutex_unlock(&vol_listner_init_lock);
552    return status;
553}
554
555/* Effect Control Interface Implementation: get_descriptor */
556static int vol_effect_get_descriptor(effect_handle_t   self,
557                                     effect_descriptor_t *descriptor)
558{
559    vol_listener_context_t *context = (vol_listener_context_t *)self;
560    ALOGV("%s Called ", __func__);
561
562    if (descriptor == NULL) {
563        ALOGE("%s: descriptor is NULL", __func__);
564        return -EINVAL;
565    }
566
567    *descriptor = *context->desc;
568    return 0;
569}
570
571static void init_once()
572{
573    int i = 0;
574    if (initialized) {
575        ALOGV("%s : already init .. do nothing", __func__);
576        return;
577    }
578
579    ALOGD("%s Called ", __func__);
580    pthread_mutex_init(&vol_listner_init_lock, NULL);
581
582    // get hal function pointer
583    if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
584        void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
585        if (hal_lib_pointer == NULL) {
586            ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
587            send_gain_dep_cal = NULL;
588        } else {
589            ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
590            send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
591            if (send_gain_dep_cal == NULL) {
592                ALOGE("Couldnt able to get the function symbol");
593            }
594        }
595    } else {
596        ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
597        send_gain_dep_cal = NULL;
598    }
599
600    // check system property to see if dumping is required
601    char check_dump_val[PROPERTY_VALUE_MAX];
602    property_get("audio.volume.listener.dump", check_dump_val, "0");
603    if (atoi(check_dump_val)) {
604        dumping_enabled = true;
605    }
606
607    init_status = 0;
608    list_init(&vol_effect_list);
609    initialized = true;
610}
611
612static int lib_init()
613{
614    pthread_once(&once, init_once);
615    ALOGV("%s Called ", __func__);
616    return init_status;
617}
618
619static int vol_prc_lib_create(const effect_uuid_t *uuid,
620                              int32_t session_id,
621                              int32_t io_id __unused,
622                              effect_handle_t *p_handle)
623{
624    int itt = 0;
625    vol_listener_context_t *context = NULL;
626
627    ALOGV("volume_prc_lib_create .. called ..");
628
629    if (lib_init() != 0) {
630        return init_status;
631    }
632
633    if (p_handle == NULL || uuid == NULL) {
634        ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
635        return -EINVAL;
636    }
637
638    context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));
639
640    if (context == NULL) {
641        ALOGE("%s: failed to allocate for context .. oops !!", __func__);
642        return -EINVAL;
643    }
644
645    // check if UUID is supported
646    for (itt = 0; descriptors[itt] != NULL; itt++) {
647        if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
648            // check if this correct .. very imp
649            context->desc = descriptors[itt];
650            context->stream_type = itt;
651            PRINT_STREAM_TYPE(itt)
652            break;
653        }
654    }
655
656    if (descriptors[itt] == NULL) {
657        ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
658        free(context);
659        return -EINVAL;
660    }
661
662    ALOGV("%s CREATED_CONTEXT %p", __func__, context);
663
664    context->itfe = &effect_interface;
665    context->state = VOL_LISTENER_STATE_INITIALIZED;
666    context->dev_id = AUDIO_DEVICE_NONE;
667    context->session_id = session_id;
668
669    // Add this to master list
670    pthread_mutex_lock(&vol_listner_init_lock);
671    list_add_tail(&vol_effect_list, &context->effect_list_node);
672
673    if (dumping_enabled) {
674        dump_list_l();
675    }
676
677    pthread_mutex_unlock(&vol_listner_init_lock);
678
679    *p_handle = (effect_handle_t)context;
680    return 0;
681}
682
683static int vol_prc_lib_release(effect_handle_t handle)
684{
685    struct listnode *node, *temp_node_next;
686    vol_listener_context_t *context = NULL;
687    vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle;
688    int status = -EINVAL;
689    bool recompute_flag = false;
690    int active_stream_count = 0;
691    uint32_t session_id;
692    uint32_t stream_type;
693    effect_uuid_t uuid;
694
695    ALOGV("%s context %p", __func__, handle);
696
697    if (recv_contex == NULL) {
698        return status;
699    }
700    pthread_mutex_lock(&vol_listner_init_lock);
701    session_id = recv_contex->session_id;
702    stream_type = recv_contex->stream_type;
703    uuid = recv_contex->desc->uuid;
704
705    // check if the handle/context provided is valid
706    list_for_each_safe(node, temp_node_next, &vol_effect_list) {
707        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
708        if ((memcmp(&(context->desc->uuid), &uuid, sizeof(effect_uuid_t)) == 0)
709            && (context->session_id == session_id)
710            && (context->stream_type == stream_type)) {
711            ALOGV("--- Found something to remove ---");
712            list_remove(node);
713            PRINT_STREAM_TYPE(context->stream_type);
714            if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
715                recompute_flag = true;
716            }
717            free(context);
718            status = 0;
719        } else {
720            ++active_stream_count;
721        }
722    }
723
724    if (status != 0) {
725        ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>");
726        pthread_mutex_unlock(&vol_listner_init_lock);
727        return status;
728    }
729
730    // if there are no active streams, reset cal and volume level
731    if (active_stream_count == 0) {
732        current_gain_dep_cal_level = -1;
733        current_vol = 0.0;
734    }
735
736    if (recompute_flag) {
737        check_and_set_gain_dep_cal();
738    }
739
740    if (dumping_enabled) {
741        dump_list_l();
742    }
743    pthread_mutex_unlock(&vol_listner_init_lock);
744    return status;
745}
746
747static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid,
748                                      effect_descriptor_t *descriptor)
749{
750    int i = 0;
751    ALOGV("%s Called ", __func__);
752    if (lib_init() != 0) {
753        return init_status;
754    }
755
756    if (descriptor == NULL || uuid == NULL) {
757        ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
758        return -EINVAL;
759    }
760
761    for (i = 0; descriptors[i] != NULL; i++) {
762        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
763            *descriptor = *descriptors[i];
764            return 0;
765        }
766    }
767
768    ALOGE("%s: couldnt found uuid passed, oops", __func__);
769    return  -EINVAL;
770}
771
772
773/* effect_handle_t interface implementation for volume listener effect */
774static const struct effect_interface_s effect_interface = {
775    vol_effect_process,
776    vol_effect_command,
777    vol_effect_get_descriptor,
778    NULL,
779};
780
781__attribute__((visibility("default")))
782audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
783    .tag = AUDIO_EFFECT_LIBRARY_TAG,
784    .version = EFFECT_LIBRARY_API_VERSION,
785    .name = "Volume Listener Effect Library",
786    .implementor = "Qualcomm Technologies Inc.",
787    .create_effect = vol_prc_lib_create,
788    .release_effect = vol_prc_lib_release,
789    .get_descriptor = vol_prc_lib_get_descriptor,
790};
791