1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/api/audio/audio_service.h" 6 7#include "base/callback.h" 8#include "base/memory/weak_ptr.h" 9#include "base/strings/string_number_conversions.h" 10#include "chromeos/audio/audio_device.h" 11#include "chromeos/audio/cras_audio_handler.h" 12#include "chromeos/dbus/audio_node.h" 13#include "chromeos/dbus/cras_audio_client.h" 14#include "chromeos/dbus/dbus_thread_manager.h" 15#include "content/public/browser/browser_thread.h" 16 17using content::BrowserThread; 18 19namespace extensions { 20 21using api::audio::OutputDeviceInfo; 22using api::audio::InputDeviceInfo; 23 24class AudioServiceImpl : public AudioService, 25 public chromeos::CrasAudioHandler::AudioObserver { 26 public: 27 AudioServiceImpl(); 28 virtual ~AudioServiceImpl(); 29 30 // Called by listeners to this service to add/remove themselves as observers. 31 virtual void AddObserver(AudioService::Observer* observer) OVERRIDE; 32 virtual void RemoveObserver(AudioService::Observer* observer) OVERRIDE; 33 34 // Start to query audio device information. 35 virtual void StartGetInfo(const GetInfoCallback& callback) OVERRIDE; 36 virtual void SetActiveDevices(const DeviceIdList& device_list) OVERRIDE; 37 virtual bool SetDeviceProperties(const std::string& device_id, 38 bool muted, 39 int volume, 40 int gain) OVERRIDE; 41 42 protected: 43 // chromeos::CrasAudioHandler::AudioObserver overrides. 44 virtual void OnOutputVolumeChanged() OVERRIDE; 45 virtual void OnInputGainChanged() OVERRIDE; 46 virtual void OnOutputMuteChanged() OVERRIDE; 47 virtual void OnInputMuteChanged() OVERRIDE; 48 virtual void OnAudioNodesChanged() OVERRIDE; 49 virtual void OnActiveOutputNodeChanged() OVERRIDE; 50 virtual void OnActiveInputNodeChanged() OVERRIDE; 51 52 private: 53 void NotifyDeviceChanged(); 54 55 // Callback for CrasAudioClient::GetNodes(). 56 void OnGetNodes(const GetInfoCallback& callback, 57 const chromeos::AudioNodeList& audio_nodes, 58 bool success); 59 60 bool FindDevice(uint64 id, chromeos::AudioDevice* device); 61 uint64 GetIdFromStr(const std::string& id_str); 62 63 // List of observers. 64 ObserverList<AudioService::Observer> observer_list_; 65 66 chromeos::CrasAudioClient* cras_audio_client_; 67 chromeos::CrasAudioHandler* cras_audio_handler_; 68 69 // Note: This should remain the last member so it'll be destroyed and 70 // invalidate the weak pointers before any other members are destroyed. 71 base::WeakPtrFactory<AudioServiceImpl> weak_ptr_factory_; 72 73 DISALLOW_COPY_AND_ASSIGN(AudioServiceImpl); 74}; 75 76AudioServiceImpl::AudioServiceImpl() 77 : cras_audio_client_(NULL), 78 cras_audio_handler_(NULL), 79 weak_ptr_factory_(this) { 80 if (chromeos::DBusThreadManager::IsInitialized() && 81 chromeos::DBusThreadManager::Get()) { 82 cras_audio_client_ = 83 chromeos::DBusThreadManager::Get()->GetCrasAudioClient(); 84 if (chromeos::CrasAudioHandler::IsInitialized()) { 85 cras_audio_handler_ = chromeos::CrasAudioHandler::Get(); 86 cras_audio_handler_->AddAudioObserver(this); 87 } 88 } 89} 90 91AudioServiceImpl::~AudioServiceImpl() { 92 if (cras_audio_handler_ && chromeos::CrasAudioHandler::IsInitialized()) { 93 cras_audio_handler_->RemoveAudioObserver(this); 94 } 95} 96 97void AudioServiceImpl::AddObserver(AudioService::Observer* observer) { 98 observer_list_.AddObserver(observer); 99} 100 101void AudioServiceImpl::RemoveObserver(AudioService::Observer* observer) { 102 observer_list_.RemoveObserver(observer); 103} 104 105void AudioServiceImpl::StartGetInfo(const GetInfoCallback& callback) { 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 107 DCHECK(cras_audio_client_); 108 if (cras_audio_client_) 109 cras_audio_client_->GetNodes(base::Bind(&AudioServiceImpl::OnGetNodes, 110 weak_ptr_factory_.GetWeakPtr(), 111 callback)); 112} 113 114void AudioServiceImpl::SetActiveDevices(const DeviceIdList& device_list) { 115 DCHECK(cras_audio_handler_); 116 if (!cras_audio_handler_) 117 return; 118 119 bool input_device_set = false; 120 bool output_device_set = false; 121 122 for (size_t i = 0; i < device_list.size(); ++i) { 123 chromeos::AudioDevice device; 124 bool found = FindDevice(GetIdFromStr(device_list[i]), &device); 125 if (found) { 126 if (device.is_input && !input_device_set) { 127 cras_audio_handler_->SwitchToDevice(device); 128 input_device_set = true; 129 } else if (!device.is_input && !output_device_set) { 130 cras_audio_handler_->SwitchToDevice(device); 131 output_device_set = true; 132 } 133 } 134 } 135} 136 137bool AudioServiceImpl::SetDeviceProperties(const std::string& device_id, 138 bool muted, 139 int volume, 140 int gain) { 141 DCHECK(cras_audio_handler_); 142 if (!cras_audio_handler_) 143 return false; 144 145 chromeos::AudioDevice device; 146 bool found = FindDevice(GetIdFromStr(device_id), &device); 147 if (!found) 148 return false; 149 150 cras_audio_handler_->SetMuteForDevice(device.id, muted); 151 152 if (!device.is_input && volume != -1) { 153 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, volume); 154 return true; 155 } else if (device.is_input && gain != -1) { 156 cras_audio_handler_->SetVolumeGainPercentForDevice(device.id, gain); 157 return true; 158 } 159 160 return false; 161} 162 163void AudioServiceImpl::OnGetNodes(const GetInfoCallback& callback, 164 const chromeos::AudioNodeList& audio_nodes, 165 bool success) { 166 OutputInfo output_info; 167 InputInfo input_info; 168 if (success) { 169 for (chromeos::AudioNodeList::const_iterator iter = audio_nodes.begin(); 170 iter != audio_nodes.end(); ++iter) { 171 if (!iter->is_input) { 172 linked_ptr<OutputDeviceInfo> info(new OutputDeviceInfo()); 173 info->id = base::Uint64ToString(iter->id); 174 info->name = iter->device_name + ": " + iter->name; 175 info->is_active = iter->active; 176 info->volume = cras_audio_handler_->GetOutputVolumePercentForDevice( 177 iter->id); 178 info->is_muted = cras_audio_handler_->IsOutputMutedForDevice(iter->id); 179 output_info.push_back(info); 180 } else { 181 linked_ptr<InputDeviceInfo> info(new InputDeviceInfo()); 182 info->id = base::Uint64ToString(iter->id); 183 info->name = iter->device_name + ": " + iter->name; 184 info->is_active = iter->active; 185 info->gain = cras_audio_handler_->GetInputGainPercentForDevice( 186 iter->id); 187 info->is_muted = cras_audio_handler_->IsInputMutedForDevice(iter->id); 188 input_info.push_back(info); 189 } 190 } 191 } 192 193 DCHECK(!callback.is_null()); 194 if (!callback.is_null()) 195 callback.Run(output_info, input_info, success); 196} 197 198bool AudioServiceImpl::FindDevice(uint64 id, chromeos::AudioDevice* device) { 199 chromeos::AudioDeviceList devices; 200 cras_audio_handler_->GetAudioDevices(&devices); 201 202 for (size_t i = 0; i < devices.size(); ++i) { 203 if (devices[i].id == id) { 204 *device = devices[i]; 205 return true; 206 } 207 } 208 return false; 209} 210 211uint64 AudioServiceImpl::GetIdFromStr(const std::string& id_str) { 212 uint64 device_id; 213 if (!base::StringToUint64(id_str, &device_id)) 214 return 0; 215 else 216 return device_id; 217} 218 219void AudioServiceImpl::OnOutputVolumeChanged() { 220 NotifyDeviceChanged(); 221} 222 223void AudioServiceImpl::OnOutputMuteChanged() { 224 NotifyDeviceChanged(); 225} 226 227void AudioServiceImpl::OnInputGainChanged() { 228 NotifyDeviceChanged(); 229} 230 231void AudioServiceImpl::OnInputMuteChanged() { 232 NotifyDeviceChanged(); 233} 234 235void AudioServiceImpl::OnAudioNodesChanged() { 236 NotifyDeviceChanged(); 237} 238 239void AudioServiceImpl::OnActiveOutputNodeChanged() { 240 NotifyDeviceChanged(); 241} 242 243void AudioServiceImpl::OnActiveInputNodeChanged() { 244 NotifyDeviceChanged(); 245} 246 247void AudioServiceImpl::NotifyDeviceChanged() { 248 FOR_EACH_OBSERVER(AudioService::Observer, observer_list_, OnDeviceChanged()); 249} 250 251AudioService* AudioService::CreateInstance() { 252 return new AudioServiceImpl; 253} 254 255} // namespace extensions 256