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 "extensions/browser/api/audio/audio_api.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/lazy_instance.h"
11 #include "base/values.h"
12 #include "components/prefs/pref_registry_simple.h"
13 #include "extensions/browser/api/audio/audio_device_id_calculator.h"
14 #include "extensions/browser/api/audio/pref_names.h"
15 #include "extensions/browser/event_router.h"
16 #include "extensions/common/api/audio.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/features/behavior_feature.h"
19 #include "extensions/common/features/feature_provider.h"
20 
21 namespace extensions {
22 
23 namespace audio = api::audio;
24 
25 namespace {
26 
CreateIdCalculator(content::BrowserContext * context)27 std::unique_ptr<AudioDeviceIdCalculator> CreateIdCalculator(
28     content::BrowserContext* context) {
29   return std::make_unique<AudioDeviceIdCalculator>(context);
30 }
31 
32 // Checks if an extension is whitelisted to use deprecated version of audio API.
33 // TODO(tbarzic): Retire this whitelist and remove the deprecated API methods,
34 //     properties and events. This is currently targeted for M-60
35 //     (http://crbug.com/697279).
CanUseDeprecatedAudioApi(const Extension * extension)36 bool CanUseDeprecatedAudioApi(const Extension* extension) {
37   if (!extension)
38     return false;
39 
40   const Feature* allow_deprecated_audio_api_feature =
41       FeatureProvider::GetBehaviorFeatures()->GetFeature(
42           behavior_feature::kAllowDeprecatedAudioApi);
43   if (!allow_deprecated_audio_api_feature)
44     return false;
45 
46   return allow_deprecated_audio_api_feature->IsAvailableToExtension(extension)
47       .is_available();
48 }
49 
CanReceiveDeprecatedAudioEvent(content::BrowserContext * browser_context,Feature::Context target_context,const Extension * extension,Event * event,const base::DictionaryValue * filter)50 bool CanReceiveDeprecatedAudioEvent(content::BrowserContext* browser_context,
51                                     Feature::Context target_context,
52                                     const Extension* extension,
53                                     Event* event,
54                                     const base::DictionaryValue* filter) {
55   return CanUseDeprecatedAudioApi(extension);
56 }
57 
58 }  // namespace
59 
60 static base::LazyInstance<
61     BrowserContextKeyedAPIFactory<AudioAPI>>::DestructorAtExit g_factory =
62     LAZY_INSTANCE_INITIALIZER;
63 
RegisterUserPrefs(PrefRegistrySimple * registry)64 void AudioAPI::RegisterUserPrefs(PrefRegistrySimple* registry) {
65   registry->RegisterListPref(kAudioApiStableDeviceIds);
66 }
67 
68 // static
GetFactoryInstance()69 BrowserContextKeyedAPIFactory<AudioAPI>* AudioAPI::GetFactoryInstance() {
70   return g_factory.Pointer();
71 }
72 
AudioAPI(content::BrowserContext * context)73 AudioAPI::AudioAPI(content::BrowserContext* context)
74     : browser_context_(context),
75       stable_id_calculator_(CreateIdCalculator(context)),
76       service_(AudioService::CreateInstance(stable_id_calculator_.get())),
77       audio_service_observer_(this) {
78   audio_service_observer_.Add(service_.get());
79 }
80 
~AudioAPI()81 AudioAPI::~AudioAPI() {}
82 
GetService() const83 AudioService* AudioAPI::GetService() const {
84   return service_.get();
85 }
86 
OnDeviceChanged()87 void AudioAPI::OnDeviceChanged() {
88   EventRouter* event_router = EventRouter::Get(browser_context_);
89   if (!event_router)
90     return;
91 
92   std::unique_ptr<Event> event(new Event(
93       events::AUDIO_ON_DEVICE_CHANGED, audio::OnDeviceChanged::kEventName,
94       std::unique_ptr<base::ListValue>(new base::ListValue())));
95   event->will_dispatch_callback =
96       base::BindRepeating(&CanReceiveDeprecatedAudioEvent);
97   event_router->BroadcastEvent(std::move(event));
98 }
99 
OnLevelChanged(const std::string & id,int level)100 void AudioAPI::OnLevelChanged(const std::string& id, int level) {
101   EventRouter* event_router = EventRouter::Get(browser_context_);
102   if (!event_router)
103     return;
104 
105   audio::LevelChangedEvent raw_event;
106   raw_event.device_id = id;
107   raw_event.level = level;
108 
109   std::unique_ptr<base::ListValue> event_args =
110       audio::OnLevelChanged::Create(raw_event);
111   std::unique_ptr<Event> event(new Event(events::AUDIO_ON_LEVEL_CHANGED,
112                                          audio::OnLevelChanged::kEventName,
113                                          std::move(event_args)));
114   event_router->BroadcastEvent(std::move(event));
115 }
116 
OnMuteChanged(bool is_input,bool is_muted)117 void AudioAPI::OnMuteChanged(bool is_input, bool is_muted) {
118   EventRouter* event_router = EventRouter::Get(browser_context_);
119   if (!event_router)
120     return;
121 
122   // Dispatch onMuteChanged event.
123   audio::MuteChangedEvent raw_event;
124   raw_event.stream_type =
125       is_input ? audio::STREAM_TYPE_INPUT : audio::STREAM_TYPE_OUTPUT;
126   raw_event.is_muted = is_muted;
127   std::unique_ptr<base::ListValue> event_args =
128       audio::OnMuteChanged::Create(raw_event);
129   std::unique_ptr<Event> event(new Event(events::AUDIO_ON_MUTE_CHANGED,
130                                          audio::OnMuteChanged::kEventName,
131                                          std::move(event_args)));
132   event_router->BroadcastEvent(std::move(event));
133 }
134 
OnDevicesChanged(const DeviceInfoList & devices)135 void AudioAPI::OnDevicesChanged(const DeviceInfoList& devices) {
136   EventRouter* event_router = EventRouter::Get(browser_context_);
137   if (!event_router)
138     return;
139 
140   std::unique_ptr<base::ListValue> args =
141       audio::OnDeviceListChanged::Create(devices);
142   std::unique_ptr<Event> event(new Event(events::AUDIO_ON_DEVICES_CHANGED,
143                                          audio::OnDeviceListChanged::kEventName,
144                                          std::move(args)));
145   event_router->BroadcastEvent(std::move(event));
146 }
147 
148 ///////////////////////////////////////////////////////////////////////////////
149 
Run()150 ExtensionFunction::ResponseAction AudioGetInfoFunction::Run() {
151   if (!CanUseDeprecatedAudioApi(extension())) {
152     return RespondNow(
153         Error("audio.getInfo is deprecated, use audio.getDevices instead."));
154   }
155 
156   AudioService* service =
157       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
158   DCHECK(service);
159   OutputInfo output_info;
160   InputInfo input_info;
161   if (!service->GetInfo(&output_info, &input_info)) {
162     return RespondNow(
163         Error("Error occurred when querying audio device information."));
164   }
165 
166   return RespondNow(
167       ArgumentList(audio::GetInfo::Results::Create(output_info, input_info)));
168 }
169 
170 ///////////////////////////////////////////////////////////////////////////////
171 
Run()172 ExtensionFunction::ResponseAction AudioGetDevicesFunction::Run() {
173   std::unique_ptr<audio::GetDevices::Params> params(
174       audio::GetDevices::Params::Create(*args_));
175   EXTENSION_FUNCTION_VALIDATE(params.get());
176 
177   AudioService* service =
178       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
179   DCHECK(service);
180 
181   std::vector<api::audio::AudioDeviceInfo> devices;
182   if (!service->GetDevices(params->filter.get(), &devices)) {
183     return RespondNow(
184         Error("Error occurred when querying audio device information."));
185   }
186 
187   return RespondNow(ArgumentList(audio::GetDevices::Results::Create(devices)));
188 }
189 
190 ///////////////////////////////////////////////////////////////////////////////
191 
Run()192 ExtensionFunction::ResponseAction AudioSetActiveDevicesFunction::Run() {
193   std::unique_ptr<audio::SetActiveDevices::Params> params(
194       audio::SetActiveDevices::Params::Create(*args_));
195   EXTENSION_FUNCTION_VALIDATE(params.get());
196 
197   AudioService* service =
198       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
199   DCHECK(service);
200 
201   if (params->ids.as_device_id_lists) {
202     if (!service->SetActiveDeviceLists(
203             params->ids.as_device_id_lists->input,
204             params->ids.as_device_id_lists->output)) {
205       return RespondNow(Error("Failed to set active devices."));
206     }
207   } else if (params->ids.as_strings) {
208     if (!CanUseDeprecatedAudioApi(extension())) {
209       return RespondNow(
210           Error("String list |ids| is deprecated, use DeviceIdLists type."));
211     }
212     service->SetActiveDevices(*params->ids.as_strings);
213   }
214   return RespondNow(NoArguments());
215 }
216 
217 ///////////////////////////////////////////////////////////////////////////////
218 
Run()219 ExtensionFunction::ResponseAction AudioSetPropertiesFunction::Run() {
220   std::unique_ptr<audio::SetProperties::Params> params(
221       audio::SetProperties::Params::Create(*args_));
222   EXTENSION_FUNCTION_VALIDATE(params.get());
223 
224   AudioService* service =
225       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
226   DCHECK(service);
227 
228   if (!CanUseDeprecatedAudioApi(extension())) {
229     if (params->properties.volume)
230       return RespondNow(Error("|volume| property is deprecated, use |level|."));
231 
232     if (params->properties.gain)
233       return RespondNow(Error("|gain| property is deprecated, use |level|."));
234 
235     if (params->properties.is_muted) {
236       return RespondNow(
237           Error("|isMuted| property is deprecated, use |audio.setMute|."));
238     }
239   }
240 
241   bool level_set = !!params->properties.level;
242   int level_value = level_set ? *params->properties.level : -1;
243 
244   int volume_value = params->properties.volume.get() ?
245       *params->properties.volume : -1;
246 
247   int gain_value = params->properties.gain.get() ?
248       *params->properties.gain : -1;
249 
250   // |volume_value| and |gain_value| are deprecated in favor of |level_value|;
251   // they are kept around only to ensure backward-compatibility and should be
252   // ignored if |level_value| is set.
253   if (!service->SetDeviceSoundLevel(params->id,
254                                     level_set ? level_value : volume_value,
255                                     level_set ? level_value : gain_value))
256     return RespondNow(Error("Could not set volume/gain properties"));
257 
258   if (params->properties.is_muted.get() &&
259       !service->SetMuteForDevice(params->id, *params->properties.is_muted)) {
260     return RespondNow(Error("Could not set mute property."));
261   }
262 
263   return RespondNow(NoArguments());
264 }
265 
266 ///////////////////////////////////////////////////////////////////////////////
267 
Run()268 ExtensionFunction::ResponseAction AudioSetMuteFunction::Run() {
269   std::unique_ptr<audio::SetMute::Params> params(
270       audio::SetMute::Params::Create(*args_));
271   EXTENSION_FUNCTION_VALIDATE(params.get());
272 
273   AudioService* service =
274       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
275   DCHECK(service);
276 
277   if (!service->SetMute(params->stream_type == audio::STREAM_TYPE_INPUT,
278                         params->is_muted)) {
279     return RespondNow(Error("Could not set mute state."));
280   }
281   return RespondNow(NoArguments());
282 }
283 
284 ///////////////////////////////////////////////////////////////////////////////
285 
Run()286 ExtensionFunction::ResponseAction AudioGetMuteFunction::Run() {
287   std::unique_ptr<audio::GetMute::Params> params(
288       audio::GetMute::Params::Create(*args_));
289   EXTENSION_FUNCTION_VALIDATE(params.get());
290 
291   AudioService* service =
292       AudioAPI::GetFactoryInstance()->Get(browser_context())->GetService();
293   DCHECK(service);
294 
295   bool value = false;
296   if (!service->GetMute(params->stream_type == audio::STREAM_TYPE_INPUT,
297                         &value)) {
298     return RespondNow(Error("Could not get mute state."));
299   }
300   return RespondNow(ArgumentList(audio::GetMute::Results::Create(value)));
301 }
302 
303 }  // namespace extensions
304