Merge "[Exif] remove unnecessary semicolon" into tizen_3.0
[platform/core/api/webapi-plugins.git] / src / sound / sound_manager.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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 #include "sound/sound_manager.h"
18
19 #include <map>
20 #include <string>
21
22 #include <tizen/tizen.h>
23 #include <vconf-keys.h>
24 #include <vconf.h>
25
26 #include "common/converter.h"
27 #include "common/logger.h"
28 #include "common/scope_exit.h"
29 #include "common/task-queue.h"
30 #include "common/tools.h"
31
32 // This constant was originally defined in vconf.h. However, in tizen 3, it
33 // appears, it is removed (or defined only in vconf-internals.h)
34 // It is not clear, if it is final solution, or not.
35 #ifndef VCONF_OK
36 #define VCONF_OK 0
37 #endif
38
39 #include "sound/sound_instance.h"
40
41 namespace extension {
42 namespace sound {
43
44 using namespace common;
45 using namespace common::tools;
46
47 const std::map<std::string, sound_type_e> SoundManager::platform_enum_map_ = {
48     {"SYSTEM", SOUND_TYPE_SYSTEM}, {"NOTIFICATION", SOUND_TYPE_NOTIFICATION},
49     {"ALARM", SOUND_TYPE_ALARM},   {"MEDIA", SOUND_TYPE_MEDIA},
50     {"VOICE", SOUND_TYPE_VOICE},   {"RINGTONE", SOUND_TYPE_RINGTONE}};
51
52 PlatformResult SoundManager::StrToPlatformEnum(const std::string& key, sound_type_e* sound_type) {
53   ScopeLogger();
54   if (platform_enum_map_.find(key) == platform_enum_map_.end()) {
55     std::string message = "Platform enum value not found for key " + key;
56     return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message);
57   }
58
59   *sound_type = platform_enum_map_.at(key);
60
61   return PlatformResult(ErrorCode::NO_ERROR);
62 }
63
64 PlatformResult SoundManager::PlatformEnumToStr(const sound_type_e value, std::string* sound_type) {
65   ScopeLogger();
66   for (auto& item : platform_enum_map_) {
67     if (item.second == value) {
68       *sound_type = item.first;
69
70       return PlatformResult(ErrorCode::NO_ERROR);
71     }
72   }
73
74   std::string message = "Platform enum value " + std::to_string(value) + " not found";
75
76   return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, message);
77 }
78
79 std::string SoundManager::SoundDeviceTypeToString(sound_device_type_e type) {
80   ScopeLogger();
81   switch (type) {
82     case SOUND_DEVICE_BUILTIN_SPEAKER:
83       return "SPEAKER";
84     case SOUND_DEVICE_BUILTIN_RECEIVER:
85       return "RECEIVER";
86     case SOUND_DEVICE_BUILTIN_MIC:
87       return "MIC";
88     case SOUND_DEVICE_AUDIO_JACK:
89       return "AUDIO_JACK";
90     case SOUND_DEVICE_BLUETOOTH:
91       return "BLUETOOTH";
92     case SOUND_DEVICE_HDMI:
93       return "HDMI";
94     case SOUND_DEVICE_MIRRORING:
95       return "MIRRORING";
96     case SOUND_DEVICE_USB_AUDIO:
97       return "USB_AUDIO";
98     default:
99       LoggerE("Invalid sound_device_type_e: %d", type);
100       return "";
101   }
102 }
103
104 std::string SoundManager::SoundIOTypeToString(sound_device_io_direction_e type) {
105   ScopeLogger();
106   switch (type) {
107     case SOUND_DEVICE_IO_DIRECTION_IN:
108       return "IN";
109     case SOUND_DEVICE_IO_DIRECTION_OUT:
110       return "OUT";
111     case SOUND_DEVICE_IO_DIRECTION_BOTH:
112       return "BOTH";
113     default:
114       LoggerE("Invalid sound_device_io_direction_e: %d", type);
115       return "";
116   }
117 }
118
119 SoundManager::SoundManager(SoundInstance& instance)
120     : is_volume_change_listener_(false),
121       volume_change_listener_id_(0),
122       soundModeChangeListening(false),
123       is_sound_device_change_listener_(false),
124       sound_device_connection_listener_id_(0),
125       sound_device_state_listener_id_(0),
126       instance_(instance),
127       soundModeListener(nullptr) {
128   ScopeLogger();
129   FillMaxVolumeMap();
130 }
131
132 SoundManager::~SoundManager() {
133   ScopeLogger();
134
135   UnsetSoundModeChangeListener();
136
137   if (is_sound_device_change_listener_) {
138     if (SOUND_MANAGER_ERROR_NONE !=
139         sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_)) {
140       LoggerE("Cannot unregister connection listener id == %d",
141               sound_device_connection_listener_id_);
142     }
143
144     if (SOUND_MANAGER_ERROR_NONE !=
145         sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_)) {
146       LoggerE("Cannot unregister state listener id == %d", sound_device_state_listener_id_);
147     }
148   }
149
150   if (is_volume_change_listener_) {
151     if (SOUND_MANAGER_ERROR_NONE !=
152         sound_manager_remove_volume_changed_cb(volume_change_listener_id_)) {
153       LoggerE("Cannot unregister volume listener id == %d", volume_change_listener_id_);
154     }
155   }
156 }
157
158 void SoundManager::FillMaxVolumeMap() {
159   ScopeLogger();
160   int max = 100;
161   int ret;
162
163   for (auto& item : platform_enum_map_) {
164     max = 100;
165
166     ret = sound_manager_get_max_volume(item.second, &max);
167     if (ret != SOUND_MANAGER_ERROR_NONE) {
168       LoggerE("SoundManagerGetMaxVolumeFailed : %d", ret);
169     }
170
171     LoggerD("maxVolume: %d - %d", item.second, max);
172
173     max_volume_map_[item.second] = max;
174   }
175 }
176
177 PlatformResult SoundManager::GetMaxVolume(sound_type_e type, int* max_volume) {
178   ScopeLogger();
179   auto it = max_volume_map_.find(type);
180   if (it == max_volume_map_.end()) {
181     std::string sound_type;
182     PlatformResult status = PlatformEnumToStr(type, &sound_type);
183     if (status.IsError()) return status;
184
185     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to find maxVolume",
186                               ("Failed to find maxVolume of type: %s", sound_type.c_str()));
187   }
188
189   *max_volume = it->second;
190
191   return PlatformResult(ErrorCode::NO_ERROR);
192 }
193
194 double SoundManager::ConvertToSystemVolume(int max_volume, int volume) {
195   ScopeLogger();
196   return round(static_cast<double>(volume) * 100 / max_volume) / 100;
197 }
198
199 void SoundManager::VolumeChangeCallback(sound_type_e type, unsigned int value) {
200   ScopeLogger("VolumeChangeCallback: type: %d, value: %d", type, value);
201
202   // Prepare response
203   picojson::value response = picojson::value(picojson::object());
204   picojson::object& response_obj = response.get<picojson::object>();
205
206   response_obj.insert(std::make_pair("listenerId", picojson::value("VolumeChangeListener")));
207
208   std::string sound_type;
209   PlatformResult status = PlatformEnumToStr(type, &sound_type);
210   if (status.IsError()) return;
211
212   response_obj.insert(std::make_pair("type", picojson::value(sound_type)));
213
214   int max_volume;
215   status = GetMaxVolume(type, &max_volume);
216   if (status.IsError()) return;
217
218   response_obj.insert(
219       std::make_pair("volume", picojson::value(ConvertToSystemVolume(max_volume, value))));
220
221   Instance::PostMessage(&instance_, response.serialize().c_str());
222 }
223
224 PlatformResult SoundManager::GetSoundMode(std::string* sound_mode_type) {
225   ScopeLogger();
226   int isEnableSound = 0;
227   int isEnableVibrate = 0;
228
229   *sound_mode_type = "MUTE";
230
231   int ret = vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &isEnableSound);
232   if (VCONF_OK != ret) {
233     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error: " + std::to_string(ret),
234                               ("vconf_get_bool error: %d (%s)", ret, get_error_message(ret)));
235   }
236
237   ret = vconf_get_bool(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL, &isEnableVibrate);
238   if (VCONF_OK != ret) {
239     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error: " + std::to_string(ret),
240                               ("vconf_get_bool error: %d (%s)", ret, get_error_message(ret)));
241   }
242
243   if (isEnableSound && isEnableVibrate) {
244     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Platform has wrong state.",
245                               ("Wrong state (sound && vibration)"));
246   }
247
248   if (isEnableSound) {
249     *sound_mode_type = "SOUND";
250   } else if (isEnableVibrate) {
251     *sound_mode_type = "VIBRATE";
252   }
253
254   return PlatformResult(ErrorCode::NO_ERROR);
255 }
256
257 PlatformResult SoundManager::SetVolume(const picojson::object& args) {
258   ScopeLogger();
259   const std::string& type = FromJson<std::string>(args, "type");
260   double volume = FromJson<double>(args, "volume");
261
262   LoggerD("SoundType: %s", type.c_str());
263   LoggerD("volume: %f", volume);
264
265   if (volume > 1.0 || volume < 0.0) {
266     return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR,
267                               "Volume should be the value between 0 and 1.");
268   }
269
270   sound_type_e sound_type;
271   PlatformResult status = SoundManager::StrToPlatformEnum(type, &sound_type);
272   if (status.IsError()) return status;
273
274   auto it = max_volume_map_.find(sound_type);
275   if (it == max_volume_map_.end()) {
276     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to find maxVolume",
277                               ("Failed to find maxVolume of type: %d", type.c_str()));
278   }
279
280   int max_volume = it->second;
281   int value = round(volume * max_volume);
282   LoggerD("volume: %lf, maxVolume: %d, value: %d", volume, max_volume, value);
283
284   int ret = sound_manager_set_volume(sound_type, value);
285   if (ret != SOUND_MANAGER_ERROR_NONE) {
286     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to set volume",
287                               ("Failed to set volume: %d (%s)", ret, get_error_message(ret)));
288   }
289
290   return PlatformResult(ErrorCode::NO_ERROR);
291 }
292
293 PlatformResult SoundManager::GetVolume(const picojson::object& args, double* volume) {
294   ScopeLogger();
295   const std::string& type = FromJson<std::string>(args, "type");
296   int value = 0;
297
298   sound_type_e type_enum;
299   PlatformResult status = SoundManager::StrToPlatformEnum(type, &type_enum);
300   if (status.IsError()) return status;
301
302   int max_volume;
303   status = GetMaxVolume(type_enum, &max_volume);
304   if (status.IsError()) return status;
305
306   int ret = sound_manager_get_volume(type_enum, &value);
307   if (ret != SOUND_MANAGER_ERROR_NONE) {
308     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to get volume",
309                               ("Failed to get volume: %d (%s)", ret, get_error_message(ret)));
310   }
311
312   *volume = ConvertToSystemVolume(max_volume, value);
313   LoggerD("volume: %lf, maxVolume: %d, value: %d", *volume, max_volume, value);
314
315   return PlatformResult(ErrorCode::NO_ERROR);
316 }
317
318 void SoundManager::soundModeChangedCb(keynode_t*, void* user_data) {
319   ScopeLogger();
320
321   if (nullptr == user_data) {
322     LoggerE("Invalid callback data!");
323     return;
324   }
325
326   auto self = static_cast<SoundManager*>(user_data);
327
328   if (self->soundModeListener) {
329     std::string soundModeType;
330     PlatformResult status = self->GetSoundMode(&soundModeType);
331
332     if (status) {
333       self->soundModeListener->OnSoundModeChange(soundModeType);
334     }
335   } else {
336     LoggerE("No SoundModeListener attached");
337   }
338 }
339
340 PlatformResult SoundManager::SetSoundModeChangeListener(
341     SoundManagerSoundModeChangedListener* listener) {
342   ScopeLogger();
343   soundModeListener = listener;
344   if (soundModeChangeListening) return PlatformResult(ErrorCode::NO_ERROR);
345
346   int status = vconf_notify_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
347                                         SoundManager::soundModeChangedCb, this);
348   if (VCONF_OK != status) {
349     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not set");
350   }
351
352   status = vconf_notify_key_changed(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL,
353                                     SoundManager::soundModeChangedCb, this);
354   if (VCONF_OK != status) {
355     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not set");
356   }
357
358   soundModeChangeListening = true;
359   return PlatformResult(ErrorCode::NO_ERROR);
360 }
361
362 PlatformResult SoundManager::UnsetSoundModeChangeListener() {
363   ScopeLogger();
364   soundModeListener = nullptr;
365   if (!soundModeChangeListening) {
366     return PlatformResult(ErrorCode::NO_ERROR);
367   }
368
369   int status = vconf_ignore_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
370                                         SoundManager::soundModeChangedCb);
371   if (VCONF_OK != status) {
372     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not unset");
373   }
374
375   status = vconf_ignore_key_changed(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL,
376                                     SoundManager::soundModeChangedCb);
377   if (VCONF_OK != status) {
378     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "SoundModeChangeListener not unset");
379   }
380
381   soundModeChangeListening = false;
382   return PlatformResult(ErrorCode::NO_ERROR);
383 }
384
385 PlatformResult SoundManager::SetVolumeChangeListener() {
386   ScopeLogger();
387   if (!is_volume_change_listener_) {
388     int ret = sound_manager_add_volume_changed_cb(
389         [](sound_type_e type, unsigned int value, void* ud) {
390           ScopeLogger(
391               "Entered into asynchronous function, sound_manager_add_volume_changed_cb's argument");
392           return static_cast<SoundManager*>(ud)->VolumeChangeCallback(type, value);
393         },
394         static_cast<void*>(this), &volume_change_listener_id_);
395
396     if (ret != SOUND_MANAGER_ERROR_NONE) {
397       return LogAndCreateResult(
398           ErrorCode::UNKNOWN_ERR, "Failed to set volume changed callback",
399           ("Failed to set volume changed callback: error: %d (%s)", ret, get_error_message(ret)));
400     }
401
402     is_volume_change_listener_ = true;
403   }
404
405   return PlatformResult(ErrorCode::NO_ERROR);
406 }
407
408 PlatformResult SoundManager::UnsetVolumeChangeListener() {
409   ScopeLogger();
410   if (!is_volume_change_listener_) {
411     return PlatformResult(ErrorCode::NO_ERROR);
412   }
413
414   int ret = sound_manager_remove_volume_changed_cb(volume_change_listener_id_);
415   if (ret != SOUND_MANAGER_ERROR_NONE) {
416     return LogAndCreateResult(
417         ErrorCode::UNKNOWN_ERR, "Failed to unset volume changed callback",
418         ("sound_manager_unset_volume_changed_cb error: %d (%s)", ret, get_error_message(ret)));
419   }
420
421   is_volume_change_listener_ = false;
422
423   return PlatformResult(ErrorCode::NO_ERROR);
424 }
425
426 void SoundManager::GetDeviceList(sound_device_mask_e mask, picojson::object& out) {
427   ScopeLogger();
428
429   sound_device_list_h device_list = nullptr;
430   sound_device_h device = nullptr;
431
432   picojson::value response = picojson::value(picojson::array());
433   picojson::array& response_array = response.get<picojson::array>();
434
435   SCOPE_EXIT {
436     if (device_list) {
437       sound_manager_free_device_list(device_list);
438     }
439   };
440
441   int ret = sound_manager_get_device_list(mask, &device_list);
442   if (SOUND_MANAGER_ERROR_NONE != ret && SOUND_MANAGER_ERROR_NO_DATA != ret) {
443     LogAndReportError(
444         PlatformResult(ErrorCode::UNKNOWN_ERR, "Getting device list failed"), &out,
445         ("sound_manager_get_device_list error: %d (%s)", ret, get_error_message(ret)));
446     return;
447   }
448
449   while (!(ret = sound_manager_get_next_device(device_list, &device))) {
450     picojson::value val = picojson::value(picojson::object());
451     picojson::object& obj = val.get<picojson::object>();
452     PlatformResult result = GetDeviceInfo(device, true, false, &obj);
453
454     if (result.IsError()) {
455       LogAndReportError(result, &out);
456       return;
457     }
458     response_array.push_back(val);
459   }
460
461   ReportSuccess(response, out);
462 }
463
464 PlatformResult SoundManager::GetDeviceInfo(sound_device_h device, bool is_connected,
465                                            bool check_connection, picojson::object* obj) {
466   ScopeLogger();
467
468   // get id
469   int id = 0;
470   int ret = sound_manager_get_device_id(device, &id);
471   if (SOUND_MANAGER_ERROR_NONE != ret) {
472     return LogAndCreateResult(
473         ErrorCode::UNKNOWN_ERR, "Getting device id failed",
474         ("sound_manager_get_device_id error: %d (%s)", ret, get_error_message(ret)));
475   }
476   obj->insert(std::make_pair("id", picojson::value(static_cast<double>(id))));
477
478   // get name
479   char* name = nullptr;
480   ret = sound_manager_get_device_name(device, &name);
481   if (SOUND_MANAGER_ERROR_NONE != ret) {
482     return LogAndCreateResult(
483         ErrorCode::UNKNOWN_ERR, "Getting device name failed",
484         ("sound_manager_get_device_name error: %d (%s)", ret, get_error_message(ret)));
485   }
486   obj->insert(std::make_pair("name", picojson::value(name)));
487
488   // get type
489   sound_device_type_e type = SOUND_DEVICE_BUILTIN_SPEAKER;
490   ret = sound_manager_get_device_type(device, &type);
491   if (SOUND_MANAGER_ERROR_NONE != ret) {
492     return LogAndCreateResult(
493         ErrorCode::UNKNOWN_ERR, "Getting device type failed",
494         ("sound_manager_get_device_type error: %d (%s)", ret, get_error_message(ret)));
495   }
496   obj->insert(std::make_pair("device", picojson::value(SoundDeviceTypeToString(type))));
497
498   // get direction
499   sound_device_io_direction_e direction = SOUND_DEVICE_IO_DIRECTION_IN;
500   ret = sound_manager_get_device_io_direction(device, &direction);
501   if (SOUND_MANAGER_ERROR_NONE != ret) {
502     return LogAndCreateResult(
503         ErrorCode::UNKNOWN_ERR, "Getting device direction failed",
504         ("sound_manager_get_device_io_direction error: %d (%s)", ret, get_error_message(ret)));
505   }
506   obj->insert(std::make_pair("direction", picojson::value(SoundIOTypeToString(direction))));
507
508   // get state
509   sound_device_state_e state = SOUND_DEVICE_STATE_DEACTIVATED;
510   ret = sound_manager_get_device_state(device, &state);
511   if (SOUND_MANAGER_ERROR_NONE != ret) {
512     return LogAndCreateResult(
513         ErrorCode::UNKNOWN_ERR, "Getting device state failed",
514         ("sound_manager_get_device_state error: %d (%s)", ret, get_error_message(ret)));
515   }
516   obj->insert(std::make_pair("isActivated", picojson::value(static_cast<bool>(state))));
517
518   // get connection
519   if (check_connection) {
520     return IsDeviceConnected(type, direction, obj);
521   }
522
523   obj->insert(std::make_pair("isConnected", picojson::value(is_connected)));
524   return PlatformResult(ErrorCode::NO_ERROR);
525 }
526
527 PlatformResult SoundManager::IsDeviceConnected(sound_device_type_e type,
528                                                sound_device_io_direction_e direction,
529                                                picojson::object* obj) {
530   ScopeLogger();
531
532   sound_device_mask_e mask = SOUND_DEVICE_ALL_MASK;
533   switch (direction) {
534     case SOUND_DEVICE_IO_DIRECTION_IN:
535       mask = SOUND_DEVICE_IO_DIRECTION_IN_MASK;
536       break;
537     case SOUND_DEVICE_IO_DIRECTION_OUT:
538       mask = SOUND_DEVICE_IO_DIRECTION_OUT_MASK;
539       break;
540     case SOUND_DEVICE_IO_DIRECTION_BOTH:
541       mask = SOUND_DEVICE_IO_DIRECTION_BOTH_MASK;
542       break;
543     default:
544       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Invalid IO type",
545                                 ("Invalid IOType (%d)", direction));
546   }
547
548   sound_device_list_h device_list = nullptr;
549   sound_device_h device = nullptr;
550   sound_device_type_e device_type = SOUND_DEVICE_BUILTIN_SPEAKER;
551
552   SCOPE_EXIT {
553     if (device_list) {
554       sound_manager_free_device_list(device_list);
555     }
556   };
557
558   int ret = sound_manager_get_device_list(mask, &device_list);
559   if (SOUND_MANAGER_ERROR_NONE != ret) {
560     return LogAndCreateResult(
561         ErrorCode::UNKNOWN_ERR, "Getting device list failed",
562         ("sound_manager_get_device_list error: %d (%s)", ret, get_error_message(ret)));
563   }
564
565   while (!(ret = sound_manager_get_next_device(device_list, &device))) {
566     ret = sound_manager_get_device_type(device, &device_type);
567     if (SOUND_MANAGER_ERROR_NONE != ret) {
568       return LogAndCreateResult(
569           ErrorCode::UNKNOWN_ERR, "Getting device type failed",
570           ("sound_manager_get_device_type error: %d (%s)", ret, get_error_message(ret)));
571     }
572
573     if (type == device_type) {
574       obj->insert(std::make_pair("isConnected", picojson::value(true)));
575       return PlatformResult(ErrorCode::NO_ERROR);
576     }
577   }
578
579   obj->insert(std::make_pair("isConnected", picojson::value(false)));
580   return PlatformResult(ErrorCode::NO_ERROR);
581 }
582
583 void SoundManager::DeviceChangeCB(sound_device_h device, bool is_connected, bool check_connection) {
584   ScopeLogger();
585
586   picojson::value response = picojson::value(picojson::object());
587   picojson::object& response_obj = response.get<picojson::object>();
588
589   PlatformResult result = GetDeviceInfo(device, is_connected, check_connection, &response_obj);
590
591   if (result.IsSuccess()) {
592     response_obj.insert(
593         std::make_pair("listenerId", picojson::value("SoundDeviceStateChangeCallback")));
594
595     auto call_response = [this, response]() -> void {
596       ScopeLogger("Entered into asynchronous function, call_response");
597       Instance::PostMessage(&instance_, response.serialize().c_str());
598     };
599
600     TaskQueue::GetInstance().Async(call_response);
601   }
602 }
603
604 void DeviceConnectionChangedCB(sound_device_h device, bool is_connected, void* user_data) {
605   ScopeLogger();
606
607   SoundManager* h = static_cast<SoundManager*>(user_data);
608   h->DeviceChangeCB(device, is_connected, false);
609 }
610
611 void DeviceStateChangedCB(sound_device_h device, sound_device_state_e unused, void* user_data) {
612   ScopeLogger();
613
614   SoundManager* h = static_cast<SoundManager*>(user_data);
615   h->DeviceChangeCB(device, false, true);
616 }
617
618 PlatformResult SoundManager::AddDeviceStateChangeListener() {
619   ScopeLogger();
620
621   int ret = SOUND_MANAGER_ERROR_NONE;
622   sound_device_mask_e mask = SOUND_DEVICE_ALL_MASK;
623
624   if (!is_sound_device_change_listener_) {
625     ret = sound_manager_add_device_connection_changed_cb(mask, DeviceConnectionChangedCB, this,
626                                                          &sound_device_connection_listener_id_);
627     if (SOUND_MANAGER_ERROR_NONE != ret) {
628       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Setting connection listener failed",
629                                 ("sound_manager_add_device_connection_changed_cb error: %d (%s)",
630                                  ret, get_error_message(ret)));
631     }
632
633     ret = sound_manager_add_device_state_changed_cb(mask, DeviceStateChangedCB, this,
634                                                     &sound_device_state_listener_id_);
635     if (SOUND_MANAGER_ERROR_NONE != ret) {
636       // silently cleanup connection changed callback registered in previous step
637       sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_);
638       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Setting state listener failed",
639                                 ("sound_manager_add_device_state_changed_cb error: %d (%s)", ret,
640                                  get_error_message(ret)));
641     }
642
643     is_sound_device_change_listener_ = true;
644   }
645
646   return PlatformResult(ErrorCode::NO_ERROR);
647 }
648
649 PlatformResult SoundManager::RemoveDeviceStateChangeListener() {
650   ScopeLogger();
651
652   if (is_sound_device_change_listener_) {
653     int ret =
654         sound_manager_remove_device_connection_changed_cb(sound_device_connection_listener_id_);
655     if (SOUND_MANAGER_ERROR_NONE != ret) {
656       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removing connection listener failed",
657                                 ("sound_manager_remove_device_connection_changed_cb error: %d (%s)",
658                                  ret, get_error_message(ret)));
659     }
660
661     ret = sound_manager_remove_device_state_changed_cb(sound_device_state_listener_id_);
662     if (SOUND_MANAGER_ERROR_NONE != ret) {
663       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removing state listener failed",
664                                 ("sound_manager_remove_device_state_changed_cb error: %d (%s)", ret,
665                                  get_error_message(ret)));
666     }
667
668     is_sound_device_change_listener_ = false;
669   }
670
671   return PlatformResult(ErrorCode::NO_ERROR);
672 }
673
674 }  // namespace sound
675 }  // namespace extension