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