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