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 #include <binder/IServiceManager.h>
19
20 #include "AudioChannelService.h"
21 #include "AudioManager.h"
22
23 #include "nsIObserverService.h"
24 #include "nsISettingsService.h"
25 #include "nsPrintfCString.h"
26
27 #include "mozilla/Hal.h"
28 #include "mozilla/Services.h"
29 #include "mozilla/StaticPtr.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/MozPromise.h"
32 #include "mozilla/dom/ScriptSettings.h"
33 #include "base/message_loop.h"
34
35 #include "BluetoothCommon.h"
36 #include "BluetoothHfpManagerBase.h"
37
38 #include "nsJSUtils.h"
39 #include "nsThreadUtils.h"
40 #include "nsServiceManagerUtils.h"
41 #include "nsComponentManagerUtils.h"
42 #include "nsContentUtils.h"
43 #include "nsXULAppAPI.h"
44 #include "mozilla/dom/BindingUtils.h"
45 #include "mozilla/dom/SettingChangeNotificationBinding.h"
46
47 using namespace mozilla::dom;
48 using namespace mozilla::dom::gonk;
49 using namespace android;
50 using namespace mozilla;
51 using namespace mozilla::dom::bluetooth;
52
53 #undef LOG
54 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
55
56 #define HEADPHONES_STATUS_HEADSET u"headset"
57 #define HEADPHONES_STATUS_HEADPHONE u"headphone"
58 #define HEADPHONES_STATUS_OFF u"off"
59 #define HEADPHONES_STATUS_UNKNOWN u"unknown"
60 #define HEADPHONES_STATUS_CHANGED "headphones-status-changed"
61 #define MOZ_SETTINGS_CHANGE_ID "mozsettings-changed"
62 #define AUDIO_CHANNEL_PROCESS_CHANGED "audio-channel-process-changed"
63 #define AUDIO_POLICY_SERVICE_NAME "media.audio_policy"
64 #define SETTINGS_SERVICE "@mozilla.org/settingsService;1"
65
66 // Refer AudioService.java from Android
67 static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
68 5, // voice call
69 15, // system
70 15, // ring
71 15, // music
72 15, // alarm
73 15, // notification
74 15, // BT SCO
75 15, // enforced audible
76 15, // DTMF
77 15, // TTS
78 #if ANDROID_VERSION < 19
79 15, // FM
80 #endif
81 };
82
83 static const uint32_t sDefaultStreamVolumeTbl[AUDIO_STREAM_CNT] = {
84 3, // voice call
85 8, // system
86 8, // ring
87 8, // music
88 8, // alarm
89 8, // notification
90 8, // BT SCO
91 15, // enforced audible // XXX Handle as fixed maximum audio setting
92 8, // DTMF
93 8, // TTS
94 #if ANDROID_VERSION < 19
95 8, // FM
96 #endif
97 };
98
99 static const int32_t sStreamVolumeAliasTbl[AUDIO_STREAM_CNT] = {
100 AUDIO_STREAM_VOICE_CALL, // voice call
101 AUDIO_STREAM_NOTIFICATION, // system
102 AUDIO_STREAM_NOTIFICATION, // ring
103 AUDIO_STREAM_MUSIC, // music
104 AUDIO_STREAM_ALARM, // alarm
105 AUDIO_STREAM_NOTIFICATION, // notification
106 AUDIO_STREAM_BLUETOOTH_SCO, // BT SCO
107 AUDIO_STREAM_ENFORCED_AUDIBLE,// enforced audible
108 AUDIO_STREAM_DTMF, // DTMF
109 AUDIO_STREAM_TTS, // TTS
110 #if ANDROID_VERSION < 19
111 AUDIO_STREAM_MUSIC, // FM
112 #endif
113 };
114
115 static const uint32_t sChannelStreamTbl[NUMBER_OF_AUDIO_CHANNELS] = {
116 AUDIO_STREAM_MUSIC, // AudioChannel::Normal
117 AUDIO_STREAM_MUSIC, // AudioChannel::Content
118 AUDIO_STREAM_NOTIFICATION, // AudioChannel::Notification
119 AUDIO_STREAM_ALARM, // AudioChannel::Alarm
120 AUDIO_STREAM_VOICE_CALL, // AudioChannel::Telephony
121 AUDIO_STREAM_RING, // AudioChannel::Ringer
122 AUDIO_STREAM_ENFORCED_AUDIBLE,// AudioChannel::Publicnotification
123 AUDIO_STREAM_SYSTEM, // AudioChannel::System
124 };
125
126
127 struct AudioDeviceInfo {
128 /** The string the value maps to */
129 const char* tag;
130 /** The enum value that maps to this string */
131 uint32_t value;
132 };
133
134 // Mappings audio output devices to strings.
135 static const AudioDeviceInfo kAudioDeviceInfos[] = {
136 { "earpiece", AUDIO_DEVICE_OUT_EARPIECE },
137 { "speaker", AUDIO_DEVICE_OUT_SPEAKER },
138 { "wired_headset", AUDIO_DEVICE_OUT_WIRED_HEADSET },
139 { "wired_headphone", AUDIO_DEVICE_OUT_WIRED_HEADPHONE },
140 { "bt_scoheadset", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET },
141 { "bt_a2dp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP },
142 };
143
144 static const int kBtSampleRate = 8000;
145
146 typedef MozPromise<bool, const char*, true> VolumeInitPromise;
147
148 namespace mozilla {
149 namespace dom {
150 namespace gonk {
151
152 /**
153 * We have five sound volume settings from UX spec,
154 * You can see more informations in Bug1068219.
155 * (1) Media : music, video, FM ...
156 * (2) Notification : ringer, notification ...
157 * (3) Alarm : alarm
158 * (4) Telephony : GSM call, WebRTC call
159 * (5) Bluetooth SCO : SCO call
160 **/
161 struct VolumeData {
162 const char* mChannelName;
163 int32_t mStreamType;
164 };
165
166 static const VolumeData gVolumeData[] = {
167 {"audio.volume.content", AUDIO_STREAM_MUSIC},
168 {"audio.volume.notification", AUDIO_STREAM_NOTIFICATION},
169 {"audio.volume.alarm", AUDIO_STREAM_ALARM},
170 {"audio.volume.telephony", AUDIO_STREAM_VOICE_CALL},
171 {"audio.volume.bt_sco", AUDIO_STREAM_BLUETOOTH_SCO}
172 };
173
174 class RunnableCallTask : public Runnable
175 {
176 public:
RunnableCallTask(nsIRunnable * aRunnable)177 explicit RunnableCallTask(nsIRunnable* aRunnable)
178 : mRunnable(aRunnable) {}
179
Run()180 NS_IMETHOD Run() override
181 {
182 return mRunnable->Run();
183 }
184 protected:
185 nsCOMPtr<nsIRunnable> mRunnable;
186 };
187
188 nsCOMPtr<nsISettingsServiceLock>
GetSettingServiceLock()189 GetSettingServiceLock()
190 {
191 nsresult rv;
192 nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
193 if (NS_WARN_IF(NS_FAILED(rv))) {
194 return nullptr;
195 }
196
197 nsCOMPtr<nsISettingsServiceLock> lock;
198 rv = service->CreateLock(nullptr, getter_AddRefs(lock));
199 if (NS_WARN_IF(NS_FAILED(rv))) {
200 return nullptr;
201 }
202 return lock.forget();
203 }
204
205 #if ANDROID_VERSION >= 21
206 class GonkAudioPortCallback : public AudioSystem::AudioPortCallback
207 {
208 public:
onAudioPortListUpdate()209 virtual void onAudioPortListUpdate()
210 {
211 nsCOMPtr<nsIRunnable> runnable =
212 NS_NewRunnableFunction([]() {
213 MOZ_ASSERT(NS_IsMainThread());
214 RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
215 NS_ENSURE_TRUE(audioManager.get(), );
216 audioManager->UpdateCachedActiveDevicesForStreams();
217 audioManager->MaybeUpdateVolumeSettingToDatabase();
218 });
219 NS_DispatchToMainThread(runnable);
220 }
onAudioPatchListUpdate()221 virtual void onAudioPatchListUpdate() { }
onServiceDied()222 virtual void onServiceDied() { }
223 };
224 #endif
225
226 void
HandleAudioFlingerDied()227 AudioManager::HandleAudioFlingerDied()
228 {
229 //Disable volume change notification
230 mIsVolumeInited = false;
231
232 uint32_t attempt;
233 for (attempt = 0; attempt < 50; attempt++) {
234 if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) {
235 break;
236 }
237 LOG("AudioPolicyService is dead! attempt=%d", attempt);
238 usleep(1000 * 200);
239 }
240
241 MOZ_RELEASE_ASSERT(attempt < 50);
242
243 // Indicate to audio HAL that we start the reconfiguration phase after a media
244 // server crash
245 AudioSystem::setParameters(0, String8("restarting=true"));
246
247 // Restore device connection states
248 SetAllDeviceConnectionStates();
249
250 // Restore call state
251 #if ANDROID_VERSION < 17
252 AudioSystem::setPhoneState(mPhoneState);
253 #else
254 AudioSystem::setPhoneState(static_cast<audio_mode_t>(mPhoneState));
255 #endif
256
257 // Restore master volume
258 AudioSystem::setMasterVolume(1.0);
259
260 // Restore stream volumes
261 for (uint32_t streamType = 0; streamType < AUDIO_STREAM_CNT; ++streamType) {
262 mStreamStates[streamType]->InitStreamVolume();
263 mStreamStates[streamType]->RestoreVolumeIndexToAllDevices();
264 }
265
266 // Indicate the end of reconfiguration phase to audio HAL
267 AudioSystem::setParameters(0, String8("restarting=true"));
268
269 // Enable volume change notification
270 mIsVolumeInited = true;
271 mAudioOutDevicesUpdated = 0;
272 MaybeUpdateVolumeSettingToDatabase(true);
273 }
274
275 class VolumeInitCallback final : public nsISettingsServiceCallback
276 {
277 public:
278 NS_DECL_ISUPPORTS
279
VolumeInitCallback()280 VolumeInitCallback()
281 : mInitCounter(0)
282 {
283 mPromise = mPromiseHolder.Ensure(__func__);
284 }
285
GetPromise() const286 RefPtr<VolumeInitPromise> GetPromise() const
287 {
288 return mPromise;
289 }
290
Handle(const nsAString & aName,JS::Handle<JS::Value> aResult)291 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
292 {
293 RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
294 MOZ_ASSERT(audioManager);
295 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
296 NS_ConvertASCIItoUTF16 volumeType(gVolumeData[idx].mChannelName);
297 if (StringBeginsWith(aName, volumeType)) {
298 uint32_t device = GetDeviceFromSettingName(aName);
299 MOZ_ASSERT(device != AUDIO_DEVICE_NONE);
300 if (aResult.isInt32()) {
301 int32_t stream = gVolumeData[idx].mStreamType;
302 uint32_t volIndex = aResult.toInt32();
303 nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex);
304 if (NS_WARN_IF(NS_FAILED(rv))) {
305 mPromiseHolder.Reject("Error : invalid volume index.", __func__);
306 return rv;
307 }
308 audioManager->SetStreamVolumeForDevice(stream, volIndex, device);
309 }
310
311 if (++mInitCounter == MOZ_ARRAY_LENGTH(kAudioDeviceInfos) * MOZ_ARRAY_LENGTH(gVolumeData)) {
312 mPromiseHolder.Resolve(true, __func__);
313 }
314 return NS_OK;
315 }
316 }
317 mPromiseHolder.Reject("Error : unexpected audio init event.", __func__);
318 return NS_OK;
319 }
320
HandleError(const nsAString & aName)321 NS_IMETHOD HandleError(const nsAString& aName)
322 {
323 mPromiseHolder.Reject(NS_ConvertUTF16toUTF8(aName).get(), __func__);
324 return NS_OK;
325 }
326
327 protected:
~VolumeInitCallback()328 ~VolumeInitCallback() {}
329
GetDeviceFromSettingName(const nsAString & aName) const330 uint32_t GetDeviceFromSettingName(const nsAString& aName) const
331 {
332 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) {
333 NS_ConvertASCIItoUTF16 device(kAudioDeviceInfos[idx].tag);
334 if (StringEndsWith(aName, device)) {
335 return kAudioDeviceInfos[idx].value;
336 }
337 }
338 return AUDIO_DEVICE_NONE;
339 }
340
341 RefPtr<VolumeInitPromise> mPromise;
342 MozPromiseHolder<VolumeInitPromise> mPromiseHolder;
343 uint32_t mInitCounter;
344 };
345
NS_IMPL_ISUPPORTS(VolumeInitCallback,nsISettingsServiceCallback)346 NS_IMPL_ISUPPORTS(VolumeInitCallback, nsISettingsServiceCallback)
347
348 static void
349 BinderDeadCallback(status_t aErr)
350 {
351 if (aErr != DEAD_OBJECT) {
352 return;
353 }
354
355 nsCOMPtr<nsIRunnable> runnable =
356 NS_NewRunnableFunction([]() {
357 MOZ_ASSERT(NS_IsMainThread());
358 RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
359 NS_ENSURE_TRUE(audioManager.get(), );
360 audioManager->HandleAudioFlingerDied();
361 });
362
363 NS_DispatchToMainThread(runnable);
364 }
365
366 bool
IsFmOutConnected()367 AudioManager::IsFmOutConnected()
368 {
369 return mConnectedDevices.Get(AUDIO_DEVICE_OUT_FM, nullptr);
370 }
371
NS_IMPL_ISUPPORTS(AudioManager,nsIAudioManager,nsIObserver)372 NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver)
373
374 void
375 AudioManager::AudioOutDeviceUpdated(uint32_t aDevice)
376 {
377 MOZ_ASSERT(audio_is_output_device(aDevice));
378 mAudioOutDevicesUpdated |= aDevice;
379 }
380
381 void
UpdateHeadsetConnectionState(hal::SwitchState aState)382 AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState)
383 {
384 bool headphoneConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
385 nullptr);
386 bool headsetConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADSET,
387 nullptr);
388 if (aState == hal::SWITCH_STATE_HEADSET) {
389 UpdateDeviceConnectionState(true,
390 AUDIO_DEVICE_OUT_WIRED_HEADSET,
391 NS_LITERAL_CSTRING(""));
392 } else if (aState == hal::SWITCH_STATE_HEADPHONE) {
393 UpdateDeviceConnectionState(true,
394 AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
395 NS_LITERAL_CSTRING(""));
396 } else if (aState == hal::SWITCH_STATE_OFF) {
397 if (headsetConnected) {
398 UpdateDeviceConnectionState(false,
399 AUDIO_DEVICE_OUT_WIRED_HEADSET,
400 NS_LITERAL_CSTRING(""));
401 }
402 if (headphoneConnected) {
403 UpdateDeviceConnectionState(false,
404 AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
405 NS_LITERAL_CSTRING(""));
406 }
407 }
408 }
409
410 void
UpdateDeviceConnectionState(bool aIsConnected,uint32_t aDevice,const nsCString & aDeviceName)411 AudioManager::UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName)
412 {
413 #if ANDROID_VERSION >= 15
414 bool isConnected = mConnectedDevices.Get(aDevice, nullptr);
415 if (isConnected && !aIsConnected) {
416 AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice),
417 AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
418 aDeviceName.get());
419 mConnectedDevices.Remove(aDevice);
420 } else if(!isConnected && aIsConnected) {
421 AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice),
422 AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
423 aDeviceName.get());
424 mConnectedDevices.Put(aDevice, aDeviceName);
425 }
426 #if ANDROID_VERSION < 21
427 // Manually call it, since AudioPortCallback is not supported.
428 // Current volumes might be changed by updating active devices in android
429 // AudioPolicyManager.
430 MaybeUpdateVolumeSettingToDatabase();
431 #endif
432 #else
433 NS_NOTREACHED("Doesn't support audio routing on GB version");
434 #endif
435 }
436
437 void
SetAllDeviceConnectionStates()438 AudioManager::SetAllDeviceConnectionStates()
439 {
440 for (auto iter = mConnectedDevices.Iter(); !iter.Done(); iter.Next()) {
441 const uint32_t& device = iter.Key();
442 nsCString& deviceAddress = iter.Data();
443 AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(device),
444 AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
445 deviceAddress.get());
446 }
447 #if ANDROID_VERSION < 21
448 // Manually call it, since AudioPortCallback is not supported.
449 // Current volumes might be changed by updating active devices in android
450 // AudioPolicyManager.
451 MaybeUpdateVolumeSettingToDatabase(true);
452 #endif
453 }
454
455 void
HandleBluetoothStatusChanged(nsISupports * aSubject,const char * aTopic,const nsCString aAddress)456 AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
457 const char* aTopic,
458 const nsCString aAddress)
459 {
460 #ifdef MOZ_B2G_BT
461 bool isConnected = false;
462 if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
463 BluetoothHfpManagerBase* hfp =
464 static_cast<BluetoothHfpManagerBase*>(aSubject);
465 isConnected = hfp->IsScoConnected();
466 } else {
467 BluetoothProfileManagerBase* profile =
468 static_cast<BluetoothProfileManagerBase*>(aSubject);
469 isConnected = profile->IsConnected();
470 }
471
472 if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
473 if (isConnected) {
474 String8 cmd;
475 cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
476 AudioSystem::setParameters(0, cmd);
477 SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
478 } else {
479 int32_t force;
480 GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
481 if (force == nsIAudioManager::FORCE_BT_SCO) {
482 SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
483 }
484 }
485 } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
486 if (!isConnected && mA2dpSwitchDone) {
487 RefPtr<AudioManager> self = this;
488 nsCOMPtr<nsIRunnable> runnable =
489 NS_NewRunnableFunction([self, isConnected, aAddress]() {
490 if (self->mA2dpSwitchDone) {
491 return;
492 }
493 self->UpdateDeviceConnectionState(isConnected,
494 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
495 aAddress);
496
497 String8 cmd("bluetooth_enabled=false");
498 AudioSystem::setParameters(0, cmd);
499 cmd.setTo("A2dpSuspended=true");
500 AudioSystem::setParameters(0, cmd);
501 self->mA2dpSwitchDone = true;
502 });
503 MessageLoop::current()->PostDelayedTask(
504 MakeAndAddRef<RunnableCallTask>(runnable), 1000);
505
506 mA2dpSwitchDone = false;
507 } else {
508 UpdateDeviceConnectionState(isConnected,
509 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
510 aAddress);
511 String8 cmd("bluetooth_enabled=true");
512 AudioSystem::setParameters(0, cmd);
513 cmd.setTo("A2dpSuspended=false");
514 AudioSystem::setParameters(0, cmd);
515 mA2dpSwitchDone = true;
516 #if ANDROID_VERSION >= 17
517 if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
518 SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
519 }
520 #endif
521 }
522 mBluetoothA2dpEnabled = isConnected;
523 } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
524 UpdateDeviceConnectionState(isConnected,
525 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
526 aAddress);
527 UpdateDeviceConnectionState(isConnected,
528 AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
529 aAddress);
530 } else if (!strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID)) {
531 String8 cmd;
532 BluetoothHfpManagerBase* hfp =
533 static_cast<BluetoothHfpManagerBase*>(aSubject);
534 if (hfp->IsNrecEnabled()) {
535 cmd.setTo("bt_headset_name=<unknown>;bt_headset_nrec=on");
536 AudioSystem::setParameters(0, cmd);
537 } else {
538 cmd.setTo("bt_headset_name=<unknown>;bt_headset_nrec=off");
539 AudioSystem::setParameters(0, cmd);
540 }
541 }
542 #endif
543 }
544
545 void
HandleAudioChannelProcessChanged()546 AudioManager::HandleAudioChannelProcessChanged()
547 {
548 // Note: If the user answers a VoIP call (e.g. WebRTC calls) during the
549 // telephony call (GSM/CDMA calls) the audio manager won't set the
550 // PHONE_STATE_IN_COMMUNICATION audio state. Once the telephony call finishes
551 // the RIL plumbing sets the PHONE_STATE_NORMAL audio state. This seems to be
552 // an issue for the VoIP call but it is not. Once the RIL plumbing sets the
553 // the PHONE_STATE_NORMAL audio state the AudioManager::mPhoneAudioAgent
554 // member will call the NotifyStoppedPlaying() method causing that this function will
555 // be called again and therefore the audio manager sets the
556 // PHONE_STATE_IN_COMMUNICATION audio state.
557
558 if ((mPhoneState == PHONE_STATE_IN_CALL) ||
559 (mPhoneState == PHONE_STATE_RINGTONE)) {
560 return;
561 }
562
563 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
564 bool telephonyChannelIsActive = service && service->TelephonyChannelIsActive();
565 telephonyChannelIsActive ? SetPhoneState(PHONE_STATE_IN_COMMUNICATION) :
566 SetPhoneState(PHONE_STATE_NORMAL);
567 }
568
569 nsresult
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)570 AudioManager::Observe(nsISupports* aSubject,
571 const char* aTopic,
572 const char16_t* aData)
573 {
574 if ((strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID) == 0) ||
575 (strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID) == 0) ||
576 (strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID) == 0) ||
577 (strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID) == 0)) {
578 nsCString address = NS_ConvertUTF16toUTF8(nsDependentString(aData));
579 if (address.IsEmpty()) {
580 NS_WARNING(nsPrintfCString("Invalid address of %s", aTopic).get());
581 return NS_ERROR_FAILURE;
582 }
583
584 HandleBluetoothStatusChanged(aSubject, aTopic, address);
585 return NS_OK;
586 }
587
588 else if (!strcmp(aTopic, AUDIO_CHANNEL_PROCESS_CHANGED)) {
589 HandleAudioChannelProcessChanged();
590 return NS_OK;
591 }
592
593 // To process the volume control on each volume categories according to
594 // change of settings
595 else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) {
596 RootedDictionary<dom::SettingChangeNotification> setting(RootingCx());
597 if (!WrappedJSToDictionary(aSubject, setting)) {
598 return NS_OK;
599 }
600 if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) {
601 return NS_OK;
602 }
603 if (!setting.mValue.isNumber()) {
604 return NS_OK;
605 }
606
607 uint32_t volIndex = setting.mValue.toNumber();
608 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
609 if (setting.mKey.EqualsASCII(gVolumeData[idx].mChannelName)) {
610 SetStreamVolumeIndex(gVolumeData[idx].mStreamType, volIndex);
611 return NS_OK;
612 }
613 }
614 }
615
616 NS_WARNING("Unexpected topic in AudioManager");
617 return NS_ERROR_FAILURE;
618 }
619
620 static void
NotifyHeadphonesStatus(hal::SwitchState aState)621 NotifyHeadphonesStatus(hal::SwitchState aState)
622 {
623 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
624 if (obs) {
625 if (aState == hal::SWITCH_STATE_HEADSET) {
626 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADSET);
627 } else if (aState == hal::SWITCH_STATE_HEADPHONE) {
628 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADPHONE);
629 } else if (aState == hal::SWITCH_STATE_OFF) {
630 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_OFF);
631 } else {
632 obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_UNKNOWN);
633 }
634 }
635 }
636
637 class HeadphoneSwitchObserver : public hal::SwitchObserver
638 {
639 public:
Notify(const hal::SwitchEvent & aEvent)640 void Notify(const hal::SwitchEvent& aEvent) {
641 RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
642 MOZ_ASSERT(audioManager);
643 audioManager->HandleHeadphoneSwitchEvent(aEvent);
644 }
645 };
646
647 void
HandleHeadphoneSwitchEvent(const hal::SwitchEvent & aEvent)648 AudioManager::HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent)
649 {
650 NotifyHeadphonesStatus(aEvent.status());
651 // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker.
652 if (aEvent.status() == hal::SWITCH_STATE_OFF && mSwitchDone) {
653
654 RefPtr<AudioManager> self = this;
655 nsCOMPtr<nsIRunnable> runnable =
656 NS_NewRunnableFunction([self]() {
657 if (self->mSwitchDone) {
658 return;
659 }
660 self->UpdateHeadsetConnectionState(hal::SWITCH_STATE_OFF);
661 self->mSwitchDone = true;
662 });
663 MessageLoop::current()->PostDelayedTask(
664 MakeAndAddRef<RunnableCallTask>(runnable), 1000);
665 mSwitchDone = false;
666 } else if (aEvent.status() != hal::SWITCH_STATE_OFF) {
667 UpdateHeadsetConnectionState(aEvent.status());
668 mSwitchDone = true;
669 }
670 // Handle the coexistence of a2dp / headset device, latest one wins.
671 #if ANDROID_VERSION >= 17
672 int32_t forceUse = 0;
673 GetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, &forceUse);
674 if (aEvent.status() != hal::SWITCH_STATE_OFF && mBluetoothA2dpEnabled) {
675 SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NO_BT_A2DP);
676 } else if (forceUse == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
677 SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
678 }
679 #endif
680 }
681
AudioManager()682 AudioManager::AudioManager()
683 : mPhoneState(PHONE_STATE_CURRENT)
684 , mIsVolumeInited(false)
685 , mAudioOutDevicesUpdated(0)
686 , mSwitchDone(true)
687 #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
688 , mBluetoothA2dpEnabled(false)
689 #endif
690 #ifdef MOZ_B2G_BT
691 , mA2dpSwitchDone(true)
692 #endif
693 , mObserver(new HeadphoneSwitchObserver())
694 {
695 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) {
696 mAudioDeviceTableIdMaps.Put(kAudioDeviceInfos[idx].value, idx);
697 }
698
699 AudioSystem::setErrorCallback(BinderDeadCallback);
700 #if ANDROID_VERSION >= 21
701 android::sp<GonkAudioPortCallback> callback = new GonkAudioPortCallback();
702 AudioSystem::setAudioPortCallback(callback);
703 #endif
704
705 // Create VolumeStreamStates
706 for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
707 VolumeStreamState* streamState =
708 new VolumeStreamState(*this, static_cast<audio_stream_type_t>(loop));
709 mStreamStates.AppendElement(streamState);
710 }
711 // Initialize stream volumes with default values
712 for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
713 uint32_t volIndex = sDefaultStreamVolumeTbl[streamType];
714 SetStreamVolumeForDevice(streamType, volIndex, AUDIO_DEVICE_OUT_DEFAULT);
715 }
716 UpdateCachedActiveDevicesForStreams();
717
718 RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
719 // Initialize headhone/heaset status
720 UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
721 NotifyHeadphonesStatus(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
722
723 // Get the initial volume index from settings DB during boot up.
724 InitVolumeFromDatabase();
725
726 // Gecko only control stream volume not master so set to default value
727 // directly.
728 AudioSystem::setMasterVolume(1.0);
729
730 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
731 NS_ENSURE_TRUE_VOID(obs);
732 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID, false))) {
733 NS_WARNING("Failed to add bluetooth sco status changed observer!");
734 }
735 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) {
736 NS_WARNING("Failed to add bluetooth a2dp status changed observer!");
737 }
738 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID, false))) {
739 NS_WARNING("Failed to add bluetooth hfp status changed observer!");
740 }
741 if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID, false))) {
742 NS_WARNING("Failed to add bluetooth hfp NREC status changed observer!");
743 }
744 if (NS_FAILED(obs->AddObserver(this, MOZ_SETTINGS_CHANGE_ID, false))) {
745 NS_WARNING("Failed to add mozsettings-changed observer!");
746 }
747 if (NS_FAILED(obs->AddObserver(this, AUDIO_CHANNEL_PROCESS_CHANGED, false))) {
748 NS_WARNING("Failed to add audio-channel-process-changed observer!");
749 }
750
751 }
752
~AudioManager()753 AudioManager::~AudioManager() {
754 AudioSystem::setErrorCallback(nullptr);
755 #if ANDROID_VERSION >= 21
756 AudioSystem::setAudioPortCallback(nullptr);
757 #endif
758 hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
759
760 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
761 NS_ENSURE_TRUE_VOID(obs);
762 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) {
763 NS_WARNING("Failed to remove bluetooth sco status changed observer!");
764 }
765 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) {
766 NS_WARNING("Failed to remove bluetooth a2dp status changed observer!");
767 }
768 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID))) {
769 NS_WARNING("Failed to remove bluetooth hfp status changed observer!");
770 }
771 if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID))) {
772 NS_WARNING("Failed to remove bluetooth hfp NREC status changed observer!");
773 }
774 if (NS_FAILED(obs->RemoveObserver(this, MOZ_SETTINGS_CHANGE_ID))) {
775 NS_WARNING("Failed to remove mozsettings-changed observer!");
776 }
777 if (NS_FAILED(obs->RemoveObserver(this, AUDIO_CHANNEL_PROCESS_CHANGED))) {
778 NS_WARNING("Failed to remove audio-channel-process-changed!");
779 }
780 }
781
782 static StaticRefPtr<AudioManager> sAudioManager;
783
784 already_AddRefed<AudioManager>
GetInstance()785 AudioManager::GetInstance()
786 {
787 // Avoid createing AudioManager from content process.
788 if (!XRE_IsParentProcess()) {
789 MOZ_CRASH("Non-chrome processes should not get here.");
790 }
791
792 // Avoid createing multiple AudioManager instance inside main process.
793 if (!sAudioManager) {
794 sAudioManager = new AudioManager();
795 ClearOnShutdown(&sAudioManager);
796 }
797
798 RefPtr<AudioManager> audioMgr = sAudioManager.get();
799 return audioMgr.forget();
800 }
801
802 NS_IMETHODIMP
GetMicrophoneMuted(bool * aMicrophoneMuted)803 AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
804 {
805
806 if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) {
807 return NS_ERROR_FAILURE;
808 }
809 return NS_OK;
810 }
811
812 NS_IMETHODIMP
SetMicrophoneMuted(bool aMicrophoneMuted)813 AudioManager::SetMicrophoneMuted(bool aMicrophoneMuted)
814 {
815 if (!AudioSystem::muteMicrophone(aMicrophoneMuted)) {
816 return NS_OK;
817 }
818 return NS_ERROR_FAILURE;
819 }
820
821 NS_IMETHODIMP
GetPhoneState(int32_t * aState)822 AudioManager::GetPhoneState(int32_t* aState)
823 {
824 *aState = mPhoneState;
825 return NS_OK;
826 }
827
828 NS_IMETHODIMP
SetPhoneState(int32_t aState)829 AudioManager::SetPhoneState(int32_t aState)
830 {
831 if (mPhoneState == aState) {
832 return NS_OK;
833 }
834
835 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
836 if (obs) {
837 nsString state;
838 state.AppendInt(aState);
839 obs->NotifyObservers(nullptr, "phone-state-changed", state.get());
840 }
841
842 #if ANDROID_VERSION < 17
843 if (AudioSystem::setPhoneState(aState)) {
844 #else
845 if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) {
846 #endif
847 return NS_ERROR_FAILURE;
848 }
849
850 #if ANDROID_VERSION < 21
851 // Manually call it, since AudioPortCallback is not supported.
852 // Current volumes might be changed by updating active devices in android
853 // AudioPolicyManager.
854 MaybeUpdateVolumeSettingToDatabase();
855 #endif
856 mPhoneState = aState;
857 return NS_OK;
858 }
859
860 NS_IMETHODIMP
861 AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce)
862 {
863 #if ANDROID_VERSION >= 15
864 status_t status = AudioSystem::setForceUse(
865 (audio_policy_force_use_t)aUsage,
866 (audio_policy_forced_cfg_t)aForce);
867 #if ANDROID_VERSION < 21
868 // Manually call it, since AudioPortCallback is not supported.
869 // Current volumes might be changed by updating active devices in android
870 // AudioPolicyManager.
871 MaybeUpdateVolumeSettingToDatabase();
872 #endif
873 return status ? NS_ERROR_FAILURE : NS_OK;
874 #else
875 NS_NOTREACHED("Doesn't support force routing on GB version");
876 return NS_ERROR_UNEXPECTED;
877 #endif
878 }
879
880 NS_IMETHODIMP
881 AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) {
882 #if ANDROID_VERSION >= 15
883 *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage);
884 return NS_OK;
885 #else
886 NS_NOTREACHED("Doesn't support force routing on GB version");
887 return NS_ERROR_UNEXPECTED;
888 #endif
889 }
890
891 NS_IMETHODIMP
892 AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex)
893 {
894 if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
895 return NS_ERROR_INVALID_ARG;
896 }
897
898 return SetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex);
899 }
900
901 NS_IMETHODIMP
902 AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex)
903 {
904 if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
905 return NS_ERROR_INVALID_ARG;
906 }
907
908 if (!aIndex) {
909 return NS_ERROR_NULL_POINTER;
910 }
911
912 return GetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex);
913 }
914
915 NS_IMETHODIMP
916 AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex)
917 {
918 if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
919 return NS_ERROR_INVALID_ARG;
920 }
921
922 if (!aMaxIndex) {
923 return NS_ERROR_NULL_POINTER;
924 }
925
926 *aMaxIndex = mStreamStates[sChannelStreamTbl[aChannel]]->GetMaxIndex();
927 return NS_OK;
928 }
929
930 nsresult
931 AudioManager::ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const
932 {
933 if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
934 return NS_ERROR_INVALID_ARG;
935 }
936
937 uint32_t maxIndex = mStreamStates[aStream]->GetMaxIndex();
938 if (aIndex > maxIndex) {
939 return NS_ERROR_FAILURE;
940 }
941 return NS_OK;
942 }
943
944 nsresult
945 AudioManager::SetStreamVolumeForDevice(int32_t aStream,
946 uint32_t aIndex,
947 uint32_t aDevice)
948 {
949 if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
950 return NS_ERROR_INVALID_ARG;
951 }
952
953 int32_t streamAlias = sStreamVolumeAliasTbl[aStream];
954 VolumeStreamState* streamState = mStreamStates[streamAlias].get();
955 return streamState->SetVolumeIndexToAliasStreams(aIndex, aDevice);
956 }
957
958 nsresult
959 AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex)
960 {
961 if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
962 return NS_ERROR_INVALID_ARG;
963 }
964
965 int32_t streamAlias = sStreamVolumeAliasTbl[aStream];
966
967 nsresult rv;
968 for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
969 if (streamAlias == sStreamVolumeAliasTbl[streamType]) {
970 rv = mStreamStates[streamType]->SetVolumeIndexToActiveDevices(aIndex);
971 if (NS_WARN_IF(NS_FAILED(rv))) {
972 return rv;
973 }
974 }
975 }
976
977 // AUDIO_STREAM_FM is not used on recent gonk.
978 // AUDIO_STREAM_MUSIC is used for FM radio volume control.
979 #if ANDROID_VERSION < 19
980 if (streamAlias == AUDIO_STREAM_MUSIC && IsFmOutConnected()) {
981 rv = mStreamStates[AUDIO_STREAM_FM]->
982 SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_FM);
983 if (NS_WARN_IF(NS_FAILED(rv))) {
984 return rv;
985 }
986 }
987 #endif
988
989 MaybeUpdateVolumeSettingToDatabase();
990 return NS_OK;
991 }
992
993 nsresult
994 AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex)
995 {
996 if (!aIndex) {
997 return NS_ERROR_INVALID_ARG;
998 }
999
1000 if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
1001 return NS_ERROR_INVALID_ARG;
1002 }
1003
1004 *aIndex = mStreamStates[aStream]->GetVolumeIndex();
1005 return NS_OK;
1006 }
1007
1008 nsAutoCString
1009 AudioManager::AppendDeviceToVolumeSetting(const char* aName, uint32_t aDevice)
1010 {
1011 nsAutoCString topic;
1012 topic.Assign(aName);
1013 topic.Append(".");
1014 uint32_t index = 0;
1015 DebugOnly<bool> exist = mAudioDeviceTableIdMaps.Get(aDevice, &index);
1016 MOZ_ASSERT(exist);
1017 topic.Append(kAudioDeviceInfos[index].tag);
1018 return topic;
1019 }
1020
1021 void
1022 AudioManager::InitVolumeFromDatabase()
1023 {
1024 nsresult rv;
1025 nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
1026 if (NS_WARN_IF(NS_FAILED(rv))) {
1027 return;
1028 }
1029
1030 nsCOMPtr<nsISettingsServiceLock> lock;
1031 rv = service->CreateLock(nullptr, getter_AddRefs(lock));
1032 if (NS_WARN_IF(NS_FAILED(rv))) {
1033 return;
1034 }
1035
1036 RefPtr<VolumeInitCallback> callback = new VolumeInitCallback();
1037 MOZ_ASSERT(callback);
1038 callback->GetPromise()->Then(AbstractThread::MainThread(), __func__, this,
1039 &AudioManager::InitDeviceVolumeSucceeded,
1040 &AudioManager::InitDeviceVolumeFailed);
1041
1042 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
1043 for (uint32_t idx2 = 0; idx2 < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx2) {
1044 lock->Get(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName,
1045 kAudioDeviceInfos[idx2].value).get(),
1046 callback);
1047 }
1048 }
1049 }
1050
1051 void
1052 AudioManager::InitDeviceVolumeSucceeded()
1053 {
1054 mIsVolumeInited = true;
1055 MaybeUpdateVolumeSettingToDatabase(true);
1056 }
1057
1058 void
1059 AudioManager::InitDeviceVolumeFailed(const char* aError)
1060 {
1061 // Default volume of AUDIO_DEVICE_OUT_DEFAULT is already set.
1062 mIsVolumeInited = true;
1063 MaybeUpdateVolumeSettingToDatabase(true);
1064 NS_WARNING(aError);
1065 }
1066
1067 void
1068 AudioManager::MaybeUpdateVolumeSettingToDatabase(bool aForce)
1069 {
1070 if (!mIsVolumeInited) {
1071 return;
1072 }
1073
1074 nsCOMPtr<nsISettingsServiceLock> lock = GetSettingServiceLock();
1075 if (NS_WARN_IF(!lock)) {
1076 return;
1077 }
1078
1079 // Send events to update the Gaia volumes
1080 JS::Rooted<JS::Value> value(RootingCx());
1081 uint32_t volume = 0;
1082 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
1083 int32_t streamType = gVolumeData[idx].mStreamType;
1084 VolumeStreamState* streamState = mStreamStates[streamType].get();
1085 if(!aForce && !streamState->IsDevicesChanged()) {
1086 continue;
1087 }
1088 // Get volume index of active device.
1089 volume = streamState->GetVolumeIndex();
1090 value.setInt32(volume);
1091 lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr);
1092 }
1093
1094 // For reducing the code dependency, Gaia doesn't need to know the
1095 // device volume, it only need to care about different volume categories.
1096 // However, we need to send the setting volume to the permanent database,
1097 // so that we can store the volume setting even if the phone reboots.
1098
1099 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
1100 int32_t streamType = gVolumeData[idx].mStreamType;
1101 VolumeStreamState* streamState = mStreamStates[streamType].get();
1102
1103 if(!streamState->IsVolumeIndexesChanged()) {
1104 continue;
1105 }
1106
1107 uint32_t remainingDevices = mAudioOutDevicesUpdated;
1108 for (uint32_t i = 0; remainingDevices != 0; i++) {
1109 uint32_t device = (1 << i);
1110 if ((device & remainingDevices) == 0) {
1111 continue;
1112 }
1113 remainingDevices &= ~device;
1114 if (!mAudioDeviceTableIdMaps.Get(device, nullptr)) {
1115 continue;
1116 }
1117 volume = streamState->GetVolumeIndex(device);
1118 value.setInt32(volume);
1119 lock->Set(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName,
1120 device).get(),
1121 value, nullptr, nullptr);
1122 }
1123 }
1124
1125 // Clear changed flags
1126 for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
1127 int32_t streamType = gVolumeData[idx].mStreamType;
1128 mStreamStates[streamType]->ClearDevicesChanged();
1129 mStreamStates[streamType]->ClearVolumeIndexesChanged();
1130 }
1131 // Clear mAudioOutDevicesUpdated
1132 mAudioOutDevicesUpdated = 0;
1133 }
1134
1135 void
1136 AudioManager::UpdateCachedActiveDevicesForStreams()
1137 {
1138 // This function updates cached active devices for streams.
1139 // It is used for optimization of GetDevicesForStream() since L.
1140 // AudioManager could know when active devices
1141 // are changed in AudioPolicyManager by onAudioPortListUpdate().
1142 // Except it, AudioManager normally do not need to ask AuidoPolicyManager
1143 // about current active devices of streams and could use cached values.
1144 // Before L, onAudioPortListUpdate() does not exist and GetDevicesForStream()
1145 // does not use the cache. Therefore this function do nothing.
1146 #if ANDROID_VERSION >= 21
1147 for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
1148 // Update cached active devices of stream
1149 mStreamStates[streamType]->IsDevicesChanged(false /* aFromCache */);
1150 }
1151 #endif
1152 }
1153
1154 uint32_t
1155 AudioManager::GetDevicesForStream(int32_t aStream, bool aFromCache)
1156 {
1157 #if ANDROID_VERSION >= 21
1158 // Since Lollipop, devices update could be notified by AudioPortCallback.
1159 // Cached values can be used if there is no update.
1160 if (aFromCache) {
1161 return mStreamStates[aStream]->GetLastDevices();
1162 }
1163 #endif
1164
1165 #if ANDROID_VERSION >= 17
1166 audio_devices_t devices =
1167 AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(aStream));
1168
1169 return static_cast<uint32_t>(devices);
1170 #else
1171 // Per audio out device volume is not supported.
1172 // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB.
1173 return AUDIO_DEVICE_OUT_SPEAKER;
1174 #endif
1175 }
1176
1177 uint32_t
1178 AudioManager::GetDeviceForStream(int32_t aStream)
1179 {
1180 uint32_t devices =
1181 GetDevicesForStream(static_cast<audio_stream_type_t>(aStream));
1182 uint32_t device = SelectDeviceFromDevices(devices);
1183 return device;
1184 }
1185
1186 /* static */ uint32_t
1187 AudioManager::SelectDeviceFromDevices(uint32_t aOutDevices)
1188 {
1189 uint32_t device = aOutDevices;
1190
1191 // See android AudioService.getDeviceForStream().
1192 // AudioPolicyManager expects it.
1193 // See also android AudioPolicyManager::getDeviceForVolume().
1194 if ((device & (device - 1)) != 0) {
1195 // Multiple device selection.
1196 if ((device & AUDIO_DEVICE_OUT_SPEAKER) != 0) {
1197 device = AUDIO_DEVICE_OUT_SPEAKER;
1198 #if ANDROID_VERSION >= 21
1199 } else if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) != 0) {
1200 device = AUDIO_DEVICE_OUT_HDMI_ARC;
1201 } else if ((device & AUDIO_DEVICE_OUT_SPDIF) != 0) {
1202 device = AUDIO_DEVICE_OUT_SPDIF;
1203 } else if ((device & AUDIO_DEVICE_OUT_AUX_LINE) != 0) {
1204 device = AUDIO_DEVICE_OUT_AUX_LINE;
1205 #endif
1206 } else {
1207 device &= AUDIO_DEVICE_OUT_ALL_A2DP;
1208 }
1209 }
1210 MOZ_ASSERT(audio_is_output_device(device));
1211 return device;
1212 }
1213 AudioManager::VolumeStreamState::VolumeStreamState(AudioManager& aManager,
1214 int32_t aStreamType)
1215 : mManager(aManager)
1216 , mStreamType(aStreamType)
1217 , mLastDevices(0)
1218 , mIsDevicesChanged(true)
1219 , mIsVolumeIndexesChanged(true)
1220 {
1221 InitStreamVolume();
1222 }
1223
1224 bool
1225 AudioManager::VolumeStreamState::IsDevicesChanged(bool aFromCache)
1226 {
1227 uint32_t devices = mManager.GetDevicesForStream(mStreamType, aFromCache);
1228 if (devices != mLastDevices) {
1229 mLastDevices = devices;
1230 mIsDevicesChanged = true;
1231 }
1232 return mIsDevicesChanged;
1233 }
1234
1235 void
1236 AudioManager::VolumeStreamState::ClearDevicesChanged()
1237 {
1238 mIsDevicesChanged = false;
1239 }
1240
1241 bool
1242 AudioManager::VolumeStreamState::IsVolumeIndexesChanged()
1243 {
1244 return mIsVolumeIndexesChanged;
1245 }
1246
1247 void
1248 AudioManager::VolumeStreamState::ClearVolumeIndexesChanged()
1249 {
1250 mIsVolumeIndexesChanged = false;
1251 }
1252
1253 void
1254 AudioManager::VolumeStreamState::InitStreamVolume()
1255 {
1256 AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(mStreamType),
1257 0,
1258 GetMaxIndex());
1259 }
1260
1261 uint32_t
1262 AudioManager::VolumeStreamState::GetMaxIndex()
1263 {
1264 return sMaxStreamVolumeTbl[mStreamType];
1265 }
1266
1267 uint32_t
1268 AudioManager::VolumeStreamState::GetDefaultIndex()
1269 {
1270 return sDefaultStreamVolumeTbl[mStreamType];
1271 }
1272
1273 uint32_t
1274 AudioManager::VolumeStreamState::GetVolumeIndex()
1275 {
1276 uint32_t device = mManager.GetDeviceForStream(mStreamType);
1277 return GetVolumeIndex(device);
1278 }
1279
1280 uint32_t
1281 AudioManager::VolumeStreamState::GetVolumeIndex(uint32_t aDevice)
1282 {
1283 uint32_t index = 0;
1284 bool ret = mVolumeIndexes.Get(aDevice, &index);
1285 if (!ret) {
1286 index = mVolumeIndexes.Get(AUDIO_DEVICE_OUT_DEFAULT);
1287 }
1288 return index;
1289 }
1290
1291 nsresult
1292 AudioManager::VolumeStreamState::SetVolumeIndexToActiveDevices(uint32_t aIndex)
1293 {
1294 uint32_t device = mManager.GetDeviceForStream(mStreamType);
1295
1296 // Update volume index for device
1297 uint32_t oldVolumeIndex = 0;
1298 bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex);
1299 if (exist && aIndex == oldVolumeIndex) {
1300 // No update
1301 return NS_OK;
1302 }
1303
1304 // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active
1305 // devices for stream.
1306 nsresult rv;
1307 rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, device);
1308 if (NS_WARN_IF(NS_FAILED(rv))) {
1309 return rv;
1310 }
1311
1312 return NS_OK;
1313 }
1314
1315 nsresult
1316 AudioManager::VolumeStreamState::SetVolumeIndexToAliasStreams(uint32_t aIndex,
1317 uint32_t aDevice)
1318 {
1319 uint32_t oldVolumeIndex = 0;
1320 bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex);
1321 if (exist && aIndex == oldVolumeIndex) {
1322 // No update
1323 return NS_OK;
1324 }
1325
1326 nsresult rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, aDevice);
1327 if (NS_WARN_IF(NS_FAILED(rv))) {
1328 return rv;
1329 }
1330
1331 for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
1332 if ((streamType != mStreamType) &&
1333 sStreamVolumeAliasTbl[streamType] == mStreamType) {
1334 // Rescaling of index is not necessary.
1335 rv = mManager.mStreamStates[streamType]->
1336 SetVolumeIndexToAliasStreams(aIndex, aDevice);
1337 if (NS_WARN_IF(NS_FAILED(rv))) {
1338 return rv;
1339 }
1340 }
1341 }
1342
1343 return NS_OK;
1344 }
1345
1346 nsresult
1347 AudioManager::VolumeStreamState::SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice)
1348 {
1349 nsresult rv;
1350 if (aDevice == AUDIO_DEVICE_OUT_SPEAKER || aDevice == AUDIO_DEVICE_OUT_EARPIECE) {
1351 // Set AUDIO_DEVICE_OUT_SPEAKER and AUDIO_DEVICE_OUT_EARPIECE to same volume.
1352 rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
1353 if (NS_WARN_IF(NS_FAILED(rv))) {
1354 return rv;
1355 }
1356 rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE);
1357 if (NS_WARN_IF(NS_FAILED(rv))) {
1358 return rv;
1359 }
1360 } else {
1361 // No alias device
1362 rv = SetVolumeIndex(aIndex, aDevice);
1363 }
1364 return rv;
1365 }
1366
1367 nsresult
1368 AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex,
1369 uint32_t aDevice,
1370 bool aUpdateCache)
1371 {
1372 status_t rv;
1373 #if ANDROID_VERSION >= 17
1374 if (aUpdateCache) {
1375 mVolumeIndexes.Put(aDevice, aIndex);
1376 mIsVolumeIndexesChanged = true;
1377 mManager.AudioOutDeviceUpdated(aDevice);
1378 }
1379
1380 rv = AudioSystem::setStreamVolumeIndex(
1381 static_cast<audio_stream_type_t>(mStreamType),
1382 aIndex,
1383 aDevice);
1384 return rv ? NS_ERROR_FAILURE : NS_OK;
1385 #else
1386 if (aUpdateCache) {
1387 // Per audio out device volume is not supported.
1388 // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB.
1389 mVolumeIndexes.Put(AUDIO_DEVICE_OUT_SPEAKER, aIndex);
1390 mIsVolumeIndexesChanged = true;
1391 mManager.AudioOutDeviceUpdated(AUDIO_DEVICE_OUT_SPEAKER);
1392 }
1393 rv = AudioSystem::setStreamVolumeIndex(
1394 static_cast<audio_stream_type_t>(mStreamType),
1395 aIndex);
1396 return rv ? NS_ERROR_FAILURE : NS_OK;
1397 #endif
1398 }
1399
1400 void
1401 AudioManager::VolumeStreamState::RestoreVolumeIndexToAllDevices()
1402 {
1403 for (auto iter = mVolumeIndexes.Iter(); !iter.Done(); iter.Next()) {
1404 const uint32_t& key = iter.Key();
1405 uint32_t& index = iter.Data();
1406 SetVolumeIndex(key, index, /* aUpdateCache */ false);
1407 }
1408 }
1409
1410 } /* namespace gonk */
1411 } /* namespace dom */
1412 } /* namespace mozilla */
1413