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