dom/system/gonk/AudioManager.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:c44b775a2dd2
1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <android/log.h>
17 #include <cutils/properties.h>
18
19 #include "AudioChannelService.h"
20 #include "AudioManager.h"
21
22 #include "nsIObserverService.h"
23 #ifdef MOZ_B2G_RIL
24 #include "nsIRadioInterfaceLayer.h"
25 #endif
26 #include "nsISettingsService.h"
27 #include "nsPrintfCString.h"
28
29 #include "mozilla/Hal.h"
30 #include "mozilla/Services.h"
31 #include "base/message_loop.h"
32
33 #include "BluetoothCommon.h"
34 #include "BluetoothHfpManagerBase.h"
35
36 #include "nsJSUtils.h"
37 #include "nsCxPusher.h"
38 #include "nsThreadUtils.h"
39 #include "nsServiceManagerUtils.h"
40 #include "nsComponentManagerUtils.h"
41
42 using namespace mozilla::dom::gonk;
43 using namespace android;
44 using namespace mozilla::hal;
45 using namespace mozilla;
46 using namespace mozilla::dom::bluetooth;
47
48 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
49
50 #define HEADPHONES_STATUS_HEADSET MOZ_UTF16("headset")
51 #define HEADPHONES_STATUS_HEADPHONE MOZ_UTF16("headphone")
52 #define HEADPHONES_STATUS_OFF MOZ_UTF16("off")
53 #define HEADPHONES_STATUS_UNKNOWN MOZ_UTF16("unknown")
54 #define HEADPHONES_STATUS_CHANGED "headphones-status-changed"
55 #define MOZ_SETTINGS_CHANGE_ID "mozsettings-changed"
56
57 static void BinderDeadCallback(status_t aErr);
58 static void InternalSetAudioRoutes(SwitchState aState);
59 // Refer AudioService.java from Android
60 static int sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
61 5, // voice call
62 15, // system
63 15, // ring
64 15, // music
65 15, // alarm
66 15, // notification
67 15, // BT SCO
68 15, // enforced audible
69 15, // DTMF
70 15, // TTS
71 15, // FM
72 };
73 // A bitwise variable for recording what kind of headset is attached.
74 static int sHeadsetState;
75 static const int kBtSampleRate = 8000;
76 static bool sSwitchDone = true;
77
78 namespace mozilla {
79 namespace dom {
80 namespace gonk {
81 class RecoverTask : public nsRunnable
82 {
83 public:
84 RecoverTask() {}
85 NS_IMETHODIMP Run() {
86 nsCOMPtr<nsIAudioManager> amService = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
87 NS_ENSURE_TRUE(amService, NS_OK);
88 AudioManager *am = static_cast<AudioManager *>(amService.get());
89 for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) {
90 AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
91 sMaxStreamVolumeTbl[loop]);
92 int32_t index;
93 am->GetStreamVolumeIndex(loop, &index);
94 am->SetStreamVolumeIndex(loop, index);
95 }
96
97 if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET)
98 InternalSetAudioRoutes(SWITCH_STATE_HEADSET);
99 else if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE)
100 InternalSetAudioRoutes(SWITCH_STATE_HEADPHONE);
101 else
102 InternalSetAudioRoutes(SWITCH_STATE_OFF);
103
104 int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID;
105 am->GetPhoneState(&phoneState);
106 #if ANDROID_VERSION < 17
107 AudioSystem::setPhoneState(phoneState);
108 #else
109 AudioSystem::setPhoneState(static_cast<audio_mode_t>(phoneState));
110 #endif
111
112 AudioSystem::get_audio_flinger();
113 return NS_OK;
114 }
115 };
116
117 class AudioChannelVolInitCallback MOZ_FINAL : public nsISettingsServiceCallback
118 {
119 public:
120 NS_DECL_ISUPPORTS
121
122 AudioChannelVolInitCallback() {}
123
124 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
125 {
126 nsCOMPtr<nsIAudioManager> audioManager =
127 do_GetService(NS_AUDIOMANAGER_CONTRACTID);
128 NS_ENSURE_TRUE(JSVAL_IS_INT(aResult), NS_OK);
129
130 int32_t volIndex = JSVAL_TO_INT(aResult);
131 if (aName.EqualsLiteral("audio.volume.content")) {
132 audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content,
133 volIndex);
134 } else if (aName.EqualsLiteral("audio.volume.notification")) {
135 audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification,
136 volIndex);
137 } else if (aName.EqualsLiteral("audio.volume.alarm")) {
138 audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm,
139 volIndex);
140 } else if (aName.EqualsLiteral("audio.volume.telephony")) {
141 audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony,
142 volIndex);
143 } else if (aName.EqualsLiteral("audio.volume.bt_sco")) {
144 static_cast<AudioManager *>(audioManager.get())->SetStreamVolumeIndex(
145 AUDIO_STREAM_BLUETOOTH_SCO, volIndex);
146 } else {
147 MOZ_ASSUME_UNREACHABLE("unexpected audio channel for initializing "
148 "volume control");
149 }
150
151 return NS_OK;
152 }
153
154 NS_IMETHOD HandleError(const nsAString& aName)
155 {
156 LOG("AudioChannelVolInitCallback::HandleError: %s\n",
157 NS_ConvertUTF16toUTF8(aName).get());
158 return NS_OK;
159 }
160 };
161
162 NS_IMPL_ISUPPORTS(AudioChannelVolInitCallback, nsISettingsServiceCallback)
163 } /* namespace gonk */
164 } /* namespace dom */
165 } /* namespace mozilla */
166
167 static void
168 BinderDeadCallback(status_t aErr)
169 {
170 if (aErr == DEAD_OBJECT) {
171 NS_DispatchToMainThread(new RecoverTask());
172 }
173 }
174
175 static bool
176 IsDeviceOn(audio_devices_t device)
177 {
178 if (static_cast<
179 audio_policy_dev_state_t (*) (audio_devices_t, const char *)
180 >(AudioSystem::getDeviceConnectionState))
181 return AudioSystem::getDeviceConnectionState(device, "") ==
182 AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
183
184 return false;
185 }
186
187 static void ProcessDelayedAudioRoute(SwitchState aState)
188 {
189 if (sSwitchDone)
190 return;
191 InternalSetAudioRoutes(aState);
192 sSwitchDone = true;
193 }
194
195 NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver)
196
197 static void
198 InternalSetAudioRoutesICS(SwitchState aState)
199 {
200 if (aState == SWITCH_STATE_HEADSET) {
201 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
202 AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
203 sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET;
204 } else if (aState == SWITCH_STATE_HEADPHONE) {
205 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
206 AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
207 sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
208 } else if (aState == SWITCH_STATE_OFF) {
209 AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(sHeadsetState),
210 AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
211 sHeadsetState = 0;
212 }
213 }
214
215 static void
216 InternalSetAudioRoutes(SwitchState aState)
217 {
218 if (static_cast<
219 status_t (*)(audio_devices_t, audio_policy_dev_state_t, const char*)
220 >(AudioSystem::setDeviceConnectionState)) {
221 InternalSetAudioRoutesICS(aState);
222 } else {
223 NS_NOTREACHED("Doesn't support audio routing on GB version");
224 }
225 }
226
227 void
228 AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
229 const char* aTopic,
230 const nsCString aAddress)
231 {
232 #ifdef MOZ_B2G_BT
233 bool status;
234 if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
235 BluetoothHfpManagerBase* hfp =
236 static_cast<BluetoothHfpManagerBase*>(aSubject);
237 status = hfp->IsScoConnected();
238 } else {
239 BluetoothProfileManagerBase* profile =
240 static_cast<BluetoothProfileManagerBase*>(aSubject);
241 status = profile->IsConnected();
242 }
243
244 audio_policy_dev_state_t audioState = status ?
245 AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
246 AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
247
248 if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
249 if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
250 String8 cmd;
251 cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
252 AudioSystem::setParameters(0, cmd);
253 SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
254 } else {
255 int32_t force;
256 GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
257 if (force == nsIAudioManager::FORCE_BT_SCO)
258 SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
259 }
260 } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
261 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
262 audioState, aAddress.get());
263 if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
264 String8 cmd("bluetooth_enabled=true");
265 AudioSystem::setParameters(0, cmd);
266 cmd.setTo("A2dpSuspended=false");
267 AudioSystem::setParameters(0, cmd);
268 } else {
269 String8 cmd("bluetooth_enabled=false");
270 AudioSystem::setParameters(0, cmd);
271 cmd.setTo("A2dpSuspended=true");
272 AudioSystem::setParameters(0, cmd);
273 }
274 } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
275 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
276 audioState, aAddress.get());
277 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
278 audioState, aAddress.get());
279 }
280 #endif
281 }
282
283 nsresult
284 AudioManager::Observe(nsISupports* aSubject,
285 const char* aTopic,
286 const char16_t* aData)
287 {
288 if ((strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID) == 0) ||
289 (strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID) == 0) ||
290 (strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID) == 0)) {
291 nsCString address = NS_ConvertUTF16toUTF8(nsDependentString(aData));
292 if (address.IsEmpty()) {
293 NS_WARNING(nsPrintfCString("Invalid address of %s", aTopic).get());
294 return NS_ERROR_FAILURE;
295 }
296
297 HandleBluetoothStatusChanged(aSubject, aTopic, address);
298 return NS_OK;
299 }
300
301 // To process the volume control on each audio channel according to
302 // change of settings
303 else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) {
304 AutoSafeJSContext cx;
305 nsDependentString dataStr(aData);
306 JS::Rooted<JS::Value> val(cx);
307 if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
308 !val.isObject()) {
309 return NS_OK;
310 }
311
312 JS::Rooted<JSObject*> obj(cx, &val.toObject());
313 JS::Rooted<JS::Value> key(cx);
314 if (!JS_GetProperty(cx, obj, "key", &key) ||
315 !key.isString()) {
316 return NS_OK;
317 }
318
319 JS::Rooted<JSString*> jsKey(cx, JS::ToString(cx, key));
320 if (!jsKey) {
321 return NS_OK;
322 }
323 nsDependentJSString keyStr;
324 if (!keyStr.init(cx, jsKey) || !keyStr.EqualsLiteral("audio.volume.bt_sco")) {
325 return NS_OK;
326 }
327
328 JS::Rooted<JS::Value> value(cx);
329 if (!JS_GetProperty(cx, obj, "value", &value) || !value.isInt32()) {
330 return NS_OK;
331 }
332
333 int32_t index = value.toInt32();
334 SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, index);
335
336 return NS_OK;
337 }
338
339 NS_WARNING("Unexpected topic in AudioManager");
340 return NS_ERROR_FAILURE;
341 }
342
343 static void
344 NotifyHeadphonesStatus(SwitchState aState)
345 {
346 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
347 if (obs) {
348 if (aState == SWITCH_STATE_HEADSET) {
349 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADSET);
350 } else if (aState == SWITCH_STATE_HEADPHONE) {
351 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADPHONE);
352 } else if (aState == SWITCH_STATE_OFF) {
353 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_OFF);
354 } else {
355 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_UNKNOWN);
356 }
357 }
358 }
359
360 class HeadphoneSwitchObserver : public SwitchObserver
361 {
362 public:
363 void Notify(const SwitchEvent& aEvent) {
364 NotifyHeadphonesStatus(aEvent.status());
365 // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker.
366 if (aEvent.status() == SWITCH_STATE_OFF && sSwitchDone) {
367 MessageLoop::current()->PostDelayedTask(
368 FROM_HERE, NewRunnableFunction(&ProcessDelayedAudioRoute, SWITCH_STATE_OFF), 1000);
369 sSwitchDone = false;
370 } else if (aEvent.status() != SWITCH_STATE_OFF) {
371 InternalSetAudioRoutes(aEvent.status());
372 sSwitchDone = true;
373 }
374 }
375 };
376
377 AudioManager::AudioManager()
378 : mPhoneState(PHONE_STATE_CURRENT)
379 , mObserver(new HeadphoneSwitchObserver())
380 #ifdef MOZ_B2G_RIL
381 , mMuteCallToRIL(false)
382 #endif
383 {
384 RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
385
386 InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
387 NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES));
388
389 for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) {
390 AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
391 sMaxStreamVolumeTbl[loop]);
392 mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop];
393 }
394 // Force publicnotification to output at maximal volume
395 SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE,
396 sMaxStreamVolumeTbl[AUDIO_STREAM_ENFORCED_AUDIBLE]);
397
398 // Get the initial volume index from settings DB during boot up.
399 nsCOMPtr<nsISettingsService> settingsService =
400 do_GetService("@mozilla.org/settingsService;1");
401 NS_ENSURE_TRUE_VOID(settingsService);
402 nsCOMPtr<nsISettingsServiceLock> lock;
403 nsresult rv = settingsService->CreateLock(nullptr, getter_AddRefs(lock));
404 NS_ENSURE_SUCCESS_VOID(rv);
405 nsCOMPtr<nsISettingsServiceCallback> callback = new AudioChannelVolInitCallback();
406 NS_ENSURE_TRUE_VOID(callback);
407 lock->Get("audio.volume.content", callback);
408 lock->Get("audio.volume.notification", callback);
409 lock->Get("audio.volume.alarm", callback);
410 lock->Get("audio.volume.telephony", callback);
411 lock->Get("audio.volume.bt_sco", callback);
412
413 // Gecko only control stream volume not master so set to default value
414 // directly.
415 AudioSystem::setMasterVolume(1.0);
416 AudioSystem::setErrorCallback(BinderDeadCallback);
417
418 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
419 NS_ENSURE_TRUE_VOID(obs);
420 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID, false))) {
421 NS_WARNING("Failed to add bluetooth sco status changed observer!");
422 }
423 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) {
424 NS_WARNING("Failed to add bluetooth a2dp status changed observer!");
425 }
426 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID, false))) {
427 NS_WARNING("Failed to add bluetooth hfp status changed observer!");
428 }
429 if (NS_FAILED(obs->AddObserver(this, MOZ_SETTINGS_CHANGE_ID, false))) {
430 NS_WARNING("Failed to add mozsettings-changed observer!");
431 }
432
433 #ifdef MOZ_B2G_RIL
434 char value[PROPERTY_VALUE_MAX];
435 property_get("ro.moz.mute.call.to_ril", value, "false");
436 if (!strcmp(value, "true")) {
437 mMuteCallToRIL = true;
438 }
439 #endif
440 }
441
442 AudioManager::~AudioManager() {
443 UnregisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
444
445 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
446 NS_ENSURE_TRUE_VOID(obs);
447 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) {
448 NS_WARNING("Failed to remove bluetooth sco status changed observer!");
449 }
450 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) {
451 NS_WARNING("Failed to remove bluetooth a2dp status changed observer!");
452 }
453 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID))) {
454 NS_WARNING("Failed to remove bluetooth hfp status changed observer!");
455 }
456 if (NS_FAILED(obs->RemoveObserver(this, MOZ_SETTINGS_CHANGE_ID))) {
457 NS_WARNING("Failed to remove mozsettings-changed observer!");
458 }
459 }
460
461 NS_IMETHODIMP
462 AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
463 {
464 #ifdef MOZ_B2G_RIL
465 if (mMuteCallToRIL) {
466 // Simply return cached mIsMicMuted if mute call go via RIL.
467 *aMicrophoneMuted = mIsMicMuted;
468 return NS_OK;
469 }
470 #endif
471
472 if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) {
473 return NS_ERROR_FAILURE;
474 }
475 return NS_OK;
476 }
477
478 NS_IMETHODIMP
479 AudioManager::SetMicrophoneMuted(bool aMicrophoneMuted)
480 {
481 if (!AudioSystem::muteMicrophone(aMicrophoneMuted)) {
482 #ifdef MOZ_B2G_RIL
483 if (mMuteCallToRIL) {
484 // Extra mute request to RIL for specific platform.
485 nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
486 NS_ENSURE_TRUE(ril, NS_ERROR_FAILURE);
487 ril->SetMicrophoneMuted(aMicrophoneMuted);
488 mIsMicMuted = aMicrophoneMuted;
489 }
490 #endif
491 return NS_OK;
492 }
493 return NS_ERROR_FAILURE;
494 }
495
496 NS_IMETHODIMP
497 AudioManager::GetPhoneState(int32_t* aState)
498 {
499 *aState = mPhoneState;
500 return NS_OK;
501 }
502
503 NS_IMETHODIMP
504 AudioManager::SetPhoneState(int32_t aState)
505 {
506 if (mPhoneState == aState) {
507 return NS_OK;
508 }
509
510 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
511 if (obs) {
512 nsString state;
513 state.AppendInt(aState);
514 obs->NotifyObservers(nullptr, "phone-state-changed", state.get());
515 }
516
517 #if ANDROID_VERSION < 17
518 if (AudioSystem::setPhoneState(aState)) {
519 #else
520 if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) {
521 #endif
522 return NS_ERROR_FAILURE;
523 }
524
525 mPhoneState = aState;
526
527 if (mPhoneAudioAgent) {
528 mPhoneAudioAgent->StopPlaying();
529 mPhoneAudioAgent = nullptr;
530 }
531
532 if (aState == PHONE_STATE_IN_CALL || aState == PHONE_STATE_RINGTONE) {
533 mPhoneAudioAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
534 MOZ_ASSERT(mPhoneAudioAgent);
535 if (aState == PHONE_STATE_IN_CALL) {
536 // Telephony doesn't be paused by any other channels.
537 mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Telephony, nullptr);
538 } else {
539 mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Ringer, nullptr);
540 }
541
542 // Telephony can always play.
543 int32_t canPlay;
544 mPhoneAudioAgent->StartPlaying(&canPlay);
545 }
546
547 return NS_OK;
548 }
549
550 NS_IMETHODIMP
551 AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce)
552 {
553 if (static_cast<
554 status_t (*)(audio_policy_force_use_t, audio_policy_forced_cfg_t)
555 >(AudioSystem::setForceUse)) {
556 // Dynamically resolved the ICS signature.
557 status_t status = AudioSystem::setForceUse(
558 (audio_policy_force_use_t)aUsage,
559 (audio_policy_forced_cfg_t)aForce);
560 return status ? NS_ERROR_FAILURE : NS_OK;
561 }
562
563 NS_NOTREACHED("Doesn't support force routing on GB version");
564 return NS_ERROR_UNEXPECTED;
565 }
566
567 NS_IMETHODIMP
568 AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) {
569 if (static_cast<
570 audio_policy_forced_cfg_t (*)(audio_policy_force_use_t)
571 >(AudioSystem::getForceUse)) {
572 // Dynamically resolved the ICS signature.
573 *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage);
574 return NS_OK;
575 }
576
577 NS_NOTREACHED("Doesn't support force routing on GB version");
578 return NS_ERROR_UNEXPECTED;
579 }
580
581 NS_IMETHODIMP
582 AudioManager::GetFmRadioAudioEnabled(bool *aFmRadioAudioEnabled)
583 {
584 *aFmRadioAudioEnabled = IsDeviceOn(AUDIO_DEVICE_OUT_FM);
585 return NS_OK;
586 }
587
588 NS_IMETHODIMP
589 AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
590 {
591 if (static_cast<
592 status_t (*) (AudioSystem::audio_devices, AudioSystem::device_connection_state, const char *)
593 >(AudioSystem::setDeviceConnectionState)) {
594 AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM,
595 aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
596 AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
597 InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
598 // sync volume with music after powering on fm radio
599 if (aFmRadioAudioEnabled) {
600 int32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
601 SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex);
602 mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex;
603 }
604 return NS_OK;
605 } else {
606 return NS_ERROR_NOT_IMPLEMENTED;
607 }
608 }
609
610 NS_IMETHODIMP
611 AudioManager::SetAudioChannelVolume(int32_t aChannel, int32_t aIndex) {
612 nsresult status;
613
614 switch (static_cast<AudioChannel>(aChannel)) {
615 case AudioChannel::Content:
616 // sync FMRadio's volume with content channel.
617 if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) {
618 status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex);
619 NS_ENSURE_SUCCESS(status, status);
620 }
621 status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex);
622 NS_ENSURE_SUCCESS(status, status);
623 status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex);
624 break;
625 case AudioChannel::Notification:
626 status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
627 NS_ENSURE_SUCCESS(status, status);
628 status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
629 break;
630 case AudioChannel::Alarm:
631 status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex);
632 break;
633 case AudioChannel::Telephony:
634 status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex);
635 break;
636 default:
637 return NS_ERROR_INVALID_ARG;
638 }
639
640 return status;
641 }
642
643 NS_IMETHODIMP
644 AudioManager::GetAudioChannelVolume(int32_t aChannel, int32_t* aIndex) {
645 if (!aIndex) {
646 return NS_ERROR_NULL_POINTER;
647 }
648
649 switch (static_cast<AudioChannel>(aChannel)) {
650 case AudioChannel::Content:
651 MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
652 mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
653 *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
654 break;
655 case AudioChannel::Notification:
656 MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
657 mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]);
658 *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
659 break;
660 case AudioChannel::Alarm:
661 *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM];
662 break;
663 case AudioChannel::Telephony:
664 *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
665 break;
666 default:
667 return NS_ERROR_INVALID_ARG;
668 }
669
670 return NS_OK;
671 }
672
673 NS_IMETHODIMP
674 AudioManager::GetMaxAudioChannelVolume(int32_t aChannel, int32_t* aMaxIndex) {
675 if (!aMaxIndex) {
676 return NS_ERROR_NULL_POINTER;
677 }
678
679 int32_t stream;
680 switch (static_cast<AudioChannel>(aChannel)) {
681 case AudioChannel::Content:
682 MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
683 sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
684 stream = AUDIO_STREAM_MUSIC;
685 break;
686 case AudioChannel::Notification:
687 MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
688 sMaxStreamVolumeTbl[AUDIO_STREAM_RING]);
689 stream = AUDIO_STREAM_NOTIFICATION;
690 break;
691 case AudioChannel::Alarm:
692 stream = AUDIO_STREAM_ALARM;
693 break;
694 case AudioChannel::Telephony:
695 stream = AUDIO_STREAM_VOICE_CALL;
696 break;
697 default:
698 return NS_ERROR_INVALID_ARG;
699 }
700
701 *aMaxIndex = sMaxStreamVolumeTbl[stream];
702 return NS_OK;
703 }
704
705 nsresult
706 AudioManager::SetStreamVolumeIndex(int32_t aStream, int32_t aIndex) {
707 if (aIndex < 0 || aIndex > sMaxStreamVolumeTbl[aStream]) {
708 return NS_ERROR_INVALID_ARG;
709 }
710
711 mCurrentStreamVolumeTbl[aStream] = aIndex;
712 status_t status;
713 #if ANDROID_VERSION < 17
714 status = AudioSystem::setStreamVolumeIndex(
715 static_cast<audio_stream_type_t>(aStream),
716 aIndex);
717 return status ? NS_ERROR_FAILURE : NS_OK;
718 #else
719 int device = 0;
720
721 if (aStream == AUDIO_STREAM_BLUETOOTH_SCO) {
722 device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
723 } else if (aStream == AUDIO_STREAM_FM) {
724 device = AUDIO_DEVICE_OUT_FM;
725 }
726
727 if (device != 0) {
728 status = AudioSystem::setStreamVolumeIndex(
729 static_cast<audio_stream_type_t>(aStream),
730 aIndex,
731 device);
732 return status ? NS_ERROR_FAILURE : NS_OK;
733 }
734
735 status = AudioSystem::setStreamVolumeIndex(
736 static_cast<audio_stream_type_t>(aStream),
737 aIndex,
738 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
739 status += AudioSystem::setStreamVolumeIndex(
740 static_cast<audio_stream_type_t>(aStream),
741 aIndex,
742 AUDIO_DEVICE_OUT_SPEAKER);
743 status += AudioSystem::setStreamVolumeIndex(
744 static_cast<audio_stream_type_t>(aStream),
745 aIndex,
746 AUDIO_DEVICE_OUT_WIRED_HEADSET);
747 status += AudioSystem::setStreamVolumeIndex(
748 static_cast<audio_stream_type_t>(aStream),
749 aIndex,
750 AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
751 status += AudioSystem::setStreamVolumeIndex(
752 static_cast<audio_stream_type_t>(aStream),
753 aIndex,
754 AUDIO_DEVICE_OUT_EARPIECE);
755 return status ? NS_ERROR_FAILURE : NS_OK;
756 #endif
757 }
758
759 nsresult
760 AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) {
761 if (!aIndex) {
762 return NS_ERROR_INVALID_ARG;
763 }
764
765 if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
766 return NS_ERROR_INVALID_ARG;
767 }
768
769 *aIndex = mCurrentStreamVolumeTbl[aStream];
770
771 return NS_OK;
772 }

mercurial