diff options
Diffstat (limited to 'dom/system/gonk/AudioManager.cpp')
-rw-r--r-- | dom/system/gonk/AudioManager.cpp | 1412 |
1 files changed, 0 insertions, 1412 deletions
diff --git a/dom/system/gonk/AudioManager.cpp b/dom/system/gonk/AudioManager.cpp deleted file mode 100644 index 88dff13f7..000000000 --- a/dom/system/gonk/AudioManager.cpp +++ /dev/null @@ -1,1412 +0,0 @@ -/* Copyright 2012 Mozilla Foundation and Mozilla contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <android/log.h> -#include <cutils/properties.h> -#include <binder/IServiceManager.h> - -#include "AudioChannelService.h" -#include "AudioManager.h" - -#include "nsIObserverService.h" -#include "nsISettingsService.h" -#include "nsPrintfCString.h" - -#include "mozilla/Hal.h" -#include "mozilla/Services.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/MozPromise.h" -#include "mozilla/dom/ScriptSettings.h" -#include "base/message_loop.h" - -#include "BluetoothCommon.h" -#include "BluetoothHfpManagerBase.h" - -#include "nsJSUtils.h" -#include "nsThreadUtils.h" -#include "nsServiceManagerUtils.h" -#include "nsComponentManagerUtils.h" -#include "nsContentUtils.h" -#include "nsXULAppAPI.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/SettingChangeNotificationBinding.h" - -using namespace mozilla::dom; -using namespace mozilla::dom::gonk; -using namespace android; -using namespace mozilla; -using namespace mozilla::dom::bluetooth; - -#undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args) - -#define HEADPHONES_STATUS_HEADSET u"headset" -#define HEADPHONES_STATUS_HEADPHONE u"headphone" -#define HEADPHONES_STATUS_OFF u"off" -#define HEADPHONES_STATUS_UNKNOWN u"unknown" -#define HEADPHONES_STATUS_CHANGED "headphones-status-changed" -#define MOZ_SETTINGS_CHANGE_ID "mozsettings-changed" -#define AUDIO_CHANNEL_PROCESS_CHANGED "audio-channel-process-changed" -#define AUDIO_POLICY_SERVICE_NAME "media.audio_policy" -#define SETTINGS_SERVICE "@mozilla.org/settingsService;1" - -// Refer AudioService.java from Android -static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = { - 5, // voice call - 15, // system - 15, // ring - 15, // music - 15, // alarm - 15, // notification - 15, // BT SCO - 15, // enforced audible - 15, // DTMF - 15, // TTS -#if ANDROID_VERSION < 19 - 15, // FM -#endif -}; - -static const uint32_t sDefaultStreamVolumeTbl[AUDIO_STREAM_CNT] = { - 3, // voice call - 8, // system - 8, // ring - 8, // music - 8, // alarm - 8, // notification - 8, // BT SCO - 15, // enforced audible // XXX Handle as fixed maximum audio setting - 8, // DTMF - 8, // TTS -#if ANDROID_VERSION < 19 - 8, // FM -#endif -}; - -static const int32_t sStreamVolumeAliasTbl[AUDIO_STREAM_CNT] = { - AUDIO_STREAM_VOICE_CALL, // voice call - AUDIO_STREAM_NOTIFICATION, // system - AUDIO_STREAM_NOTIFICATION, // ring - AUDIO_STREAM_MUSIC, // music - AUDIO_STREAM_ALARM, // alarm - AUDIO_STREAM_NOTIFICATION, // notification - AUDIO_STREAM_BLUETOOTH_SCO, // BT SCO - AUDIO_STREAM_ENFORCED_AUDIBLE,// enforced audible - AUDIO_STREAM_DTMF, // DTMF - AUDIO_STREAM_TTS, // TTS -#if ANDROID_VERSION < 19 - AUDIO_STREAM_MUSIC, // FM -#endif -}; - -static const uint32_t sChannelStreamTbl[NUMBER_OF_AUDIO_CHANNELS] = { - AUDIO_STREAM_MUSIC, // AudioChannel::Normal - AUDIO_STREAM_MUSIC, // AudioChannel::Content - AUDIO_STREAM_NOTIFICATION, // AudioChannel::Notification - AUDIO_STREAM_ALARM, // AudioChannel::Alarm - AUDIO_STREAM_VOICE_CALL, // AudioChannel::Telephony - AUDIO_STREAM_RING, // AudioChannel::Ringer - AUDIO_STREAM_ENFORCED_AUDIBLE,// AudioChannel::Publicnotification - AUDIO_STREAM_SYSTEM, // AudioChannel::System -}; - - -struct AudioDeviceInfo { - /** The string the value maps to */ - const char* tag; - /** The enum value that maps to this string */ - uint32_t value; -}; - -// Mappings audio output devices to strings. -static const AudioDeviceInfo kAudioDeviceInfos[] = { - { "earpiece", AUDIO_DEVICE_OUT_EARPIECE }, - { "speaker", AUDIO_DEVICE_OUT_SPEAKER }, - { "wired_headset", AUDIO_DEVICE_OUT_WIRED_HEADSET }, - { "wired_headphone", AUDIO_DEVICE_OUT_WIRED_HEADPHONE }, - { "bt_scoheadset", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET }, - { "bt_a2dp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP }, -}; - -static const int kBtSampleRate = 8000; - -typedef MozPromise<bool, const char*, true> VolumeInitPromise; - -namespace mozilla { -namespace dom { -namespace gonk { - -/** - * We have five sound volume settings from UX spec, - * You can see more informations in Bug1068219. - * (1) Media : music, video, FM ... - * (2) Notification : ringer, notification ... - * (3) Alarm : alarm - * (4) Telephony : GSM call, WebRTC call - * (5) Bluetooth SCO : SCO call - **/ -struct VolumeData { - const char* mChannelName; - int32_t mStreamType; -}; - -static const VolumeData gVolumeData[] = { - {"audio.volume.content", AUDIO_STREAM_MUSIC}, - {"audio.volume.notification", AUDIO_STREAM_NOTIFICATION}, - {"audio.volume.alarm", AUDIO_STREAM_ALARM}, - {"audio.volume.telephony", AUDIO_STREAM_VOICE_CALL}, - {"audio.volume.bt_sco", AUDIO_STREAM_BLUETOOTH_SCO} -}; - -class RunnableCallTask : public Runnable -{ -public: - explicit RunnableCallTask(nsIRunnable* aRunnable) - : mRunnable(aRunnable) {} - - NS_IMETHOD Run() override - { - return mRunnable->Run(); - } -protected: - nsCOMPtr<nsIRunnable> mRunnable; -}; - -nsCOMPtr<nsISettingsServiceLock> -GetSettingServiceLock() -{ - nsresult rv; - nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - nsCOMPtr<nsISettingsServiceLock> lock; - rv = service->CreateLock(nullptr, getter_AddRefs(lock)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - return lock.forget(); -} - -#if ANDROID_VERSION >= 21 -class GonkAudioPortCallback : public AudioSystem::AudioPortCallback -{ -public: - virtual void onAudioPortListUpdate() - { - nsCOMPtr<nsIRunnable> runnable = - NS_NewRunnableFunction([]() { - MOZ_ASSERT(NS_IsMainThread()); - RefPtr<AudioManager> audioManager = AudioManager::GetInstance(); - NS_ENSURE_TRUE(audioManager.get(), ); - audioManager->UpdateCachedActiveDevicesForStreams(); - audioManager->MaybeUpdateVolumeSettingToDatabase(); - }); - NS_DispatchToMainThread(runnable); - } - virtual void onAudioPatchListUpdate() { } - virtual void onServiceDied() { } -}; -#endif - -void -AudioManager::HandleAudioFlingerDied() -{ - //Disable volume change notification - mIsVolumeInited = false; - - uint32_t attempt; - for (attempt = 0; attempt < 50; attempt++) { - if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) { - break; - } - LOG("AudioPolicyService is dead! attempt=%d", attempt); - usleep(1000 * 200); - } - - MOZ_RELEASE_ASSERT(attempt < 50); - - // Indicate to audio HAL that we start the reconfiguration phase after a media - // server crash - AudioSystem::setParameters(0, String8("restarting=true")); - - // Restore device connection states - SetAllDeviceConnectionStates(); - - // Restore call state -#if ANDROID_VERSION < 17 - AudioSystem::setPhoneState(mPhoneState); -#else - AudioSystem::setPhoneState(static_cast<audio_mode_t>(mPhoneState)); -#endif - - // Restore master volume - AudioSystem::setMasterVolume(1.0); - - // Restore stream volumes - for (uint32_t streamType = 0; streamType < AUDIO_STREAM_CNT; ++streamType) { - mStreamStates[streamType]->InitStreamVolume(); - mStreamStates[streamType]->RestoreVolumeIndexToAllDevices(); - } - - // Indicate the end of reconfiguration phase to audio HAL - AudioSystem::setParameters(0, String8("restarting=true")); - - // Enable volume change notification - mIsVolumeInited = true; - mAudioOutDevicesUpdated = 0; - MaybeUpdateVolumeSettingToDatabase(true); -} - -class VolumeInitCallback final : public nsISettingsServiceCallback -{ -public: - NS_DECL_ISUPPORTS - - VolumeInitCallback() - : mInitCounter(0) - { - mPromise = mPromiseHolder.Ensure(__func__); - } - - RefPtr<VolumeInitPromise> GetPromise() const - { - return mPromise; - } - - NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) - { - RefPtr<AudioManager> audioManager = AudioManager::GetInstance(); - MOZ_ASSERT(audioManager); - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - NS_ConvertASCIItoUTF16 volumeType(gVolumeData[idx].mChannelName); - if (StringBeginsWith(aName, volumeType)) { - uint32_t device = GetDeviceFromSettingName(aName); - MOZ_ASSERT(device != AUDIO_DEVICE_NONE); - if (aResult.isInt32()) { - int32_t stream = gVolumeData[idx].mStreamType; - uint32_t volIndex = aResult.toInt32(); - nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex); - if (NS_WARN_IF(NS_FAILED(rv))) { - mPromiseHolder.Reject("Error : invalid volume index.", __func__); - return rv; - } - audioManager->SetStreamVolumeForDevice(stream, volIndex, device); - } - - if (++mInitCounter == MOZ_ARRAY_LENGTH(kAudioDeviceInfos) * MOZ_ARRAY_LENGTH(gVolumeData)) { - mPromiseHolder.Resolve(true, __func__); - } - return NS_OK; - } - } - mPromiseHolder.Reject("Error : unexpected audio init event.", __func__); - return NS_OK; - } - - NS_IMETHOD HandleError(const nsAString& aName) - { - mPromiseHolder.Reject(NS_ConvertUTF16toUTF8(aName).get(), __func__); - return NS_OK; - } - -protected: - ~VolumeInitCallback() {} - - uint32_t GetDeviceFromSettingName(const nsAString& aName) const - { - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) { - NS_ConvertASCIItoUTF16 device(kAudioDeviceInfos[idx].tag); - if (StringEndsWith(aName, device)) { - return kAudioDeviceInfos[idx].value; - } - } - return AUDIO_DEVICE_NONE; - } - - RefPtr<VolumeInitPromise> mPromise; - MozPromiseHolder<VolumeInitPromise> mPromiseHolder; - uint32_t mInitCounter; -}; - -NS_IMPL_ISUPPORTS(VolumeInitCallback, nsISettingsServiceCallback) - -static void -BinderDeadCallback(status_t aErr) -{ - if (aErr != DEAD_OBJECT) { - return; - } - - nsCOMPtr<nsIRunnable> runnable = - NS_NewRunnableFunction([]() { - MOZ_ASSERT(NS_IsMainThread()); - RefPtr<AudioManager> audioManager = AudioManager::GetInstance(); - NS_ENSURE_TRUE(audioManager.get(), ); - audioManager->HandleAudioFlingerDied(); - }); - - NS_DispatchToMainThread(runnable); -} - -bool -AudioManager::IsFmOutConnected() -{ - return mConnectedDevices.Get(AUDIO_DEVICE_OUT_FM, nullptr); -} - -NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver) - -void -AudioManager::AudioOutDeviceUpdated(uint32_t aDevice) -{ - MOZ_ASSERT(audio_is_output_device(aDevice)); - mAudioOutDevicesUpdated |= aDevice; -} - -void -AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState) -{ - bool headphoneConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - nullptr); - bool headsetConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADSET, - nullptr); - if (aState == hal::SWITCH_STATE_HEADSET) { - UpdateDeviceConnectionState(true, - AUDIO_DEVICE_OUT_WIRED_HEADSET, - NS_LITERAL_CSTRING("")); - } else if (aState == hal::SWITCH_STATE_HEADPHONE) { - UpdateDeviceConnectionState(true, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - NS_LITERAL_CSTRING("")); - } else if (aState == hal::SWITCH_STATE_OFF) { - if (headsetConnected) { - UpdateDeviceConnectionState(false, - AUDIO_DEVICE_OUT_WIRED_HEADSET, - NS_LITERAL_CSTRING("")); - } - if (headphoneConnected) { - UpdateDeviceConnectionState(false, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - NS_LITERAL_CSTRING("")); - } - } -} - -void -AudioManager::UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName) -{ -#if ANDROID_VERSION >= 15 - bool isConnected = mConnectedDevices.Get(aDevice, nullptr); - if (isConnected && !aIsConnected) { - AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice), - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - aDeviceName.get()); - mConnectedDevices.Remove(aDevice); - } else if(!isConnected && aIsConnected) { - AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice), - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - aDeviceName.get()); - mConnectedDevices.Put(aDevice, aDeviceName); - } -#if ANDROID_VERSION < 21 - // Manually call it, since AudioPortCallback is not supported. - // Current volumes might be changed by updating active devices in android - // AudioPolicyManager. - MaybeUpdateVolumeSettingToDatabase(); -#endif -#else - NS_NOTREACHED("Doesn't support audio routing on GB version"); -#endif -} - -void -AudioManager::SetAllDeviceConnectionStates() -{ - for (auto iter = mConnectedDevices.Iter(); !iter.Done(); iter.Next()) { - const uint32_t& device = iter.Key(); - nsCString& deviceAddress = iter.Data(); - AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(device), - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - deviceAddress.get()); - } -#if ANDROID_VERSION < 21 - // Manually call it, since AudioPortCallback is not supported. - // Current volumes might be changed by updating active devices in android - // AudioPolicyManager. - MaybeUpdateVolumeSettingToDatabase(true); -#endif -} - -void -AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject, - const char* aTopic, - const nsCString aAddress) -{ -#ifdef MOZ_B2G_BT - bool isConnected = false; - if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { - BluetoothHfpManagerBase* hfp = - static_cast<BluetoothHfpManagerBase*>(aSubject); - isConnected = hfp->IsScoConnected(); - } else { - BluetoothProfileManagerBase* profile = - static_cast<BluetoothProfileManagerBase*>(aSubject); - isConnected = profile->IsConnected(); - } - - if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { - if (isConnected) { - String8 cmd; - cmd.appendFormat("bt_samplerate=%d", kBtSampleRate); - AudioSystem::setParameters(0, cmd); - SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO); - } else { - int32_t force; - GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force); - if (force == nsIAudioManager::FORCE_BT_SCO) { - SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE); - } - } - } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) { - if (!isConnected && mA2dpSwitchDone) { - RefPtr<AudioManager> self = this; - nsCOMPtr<nsIRunnable> runnable = - NS_NewRunnableFunction([self, isConnected, aAddress]() { - if (self->mA2dpSwitchDone) { - return; - } - self->UpdateDeviceConnectionState(isConnected, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - aAddress); - - String8 cmd("bluetooth_enabled=false"); - AudioSystem::setParameters(0, cmd); - cmd.setTo("A2dpSuspended=true"); - AudioSystem::setParameters(0, cmd); - self->mA2dpSwitchDone = true; - }); - MessageLoop::current()->PostDelayedTask( - MakeAndAddRef<RunnableCallTask>(runnable), 1000); - - mA2dpSwitchDone = false; - } else { - UpdateDeviceConnectionState(isConnected, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - aAddress); - String8 cmd("bluetooth_enabled=true"); - AudioSystem::setParameters(0, cmd); - cmd.setTo("A2dpSuspended=false"); - AudioSystem::setParameters(0, cmd); - mA2dpSwitchDone = true; -#if ANDROID_VERSION >= 17 - if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) { - SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE); - } -#endif - } - mBluetoothA2dpEnabled = isConnected; - } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) { - UpdateDeviceConnectionState(isConnected, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, - aAddress); - UpdateDeviceConnectionState(isConnected, - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, - aAddress); - } else if (!strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID)) { - String8 cmd; - BluetoothHfpManagerBase* hfp = - static_cast<BluetoothHfpManagerBase*>(aSubject); - if (hfp->IsNrecEnabled()) { - cmd.setTo("bt_headset_name=<unknown>;bt_headset_nrec=on"); - AudioSystem::setParameters(0, cmd); - } else { - cmd.setTo("bt_headset_name=<unknown>;bt_headset_nrec=off"); - AudioSystem::setParameters(0, cmd); - } - } -#endif -} - -void -AudioManager::HandleAudioChannelProcessChanged() -{ - // Note: If the user answers a VoIP call (e.g. WebRTC calls) during the - // telephony call (GSM/CDMA calls) the audio manager won't set the - // PHONE_STATE_IN_COMMUNICATION audio state. Once the telephony call finishes - // the RIL plumbing sets the PHONE_STATE_NORMAL audio state. This seems to be - // an issue for the VoIP call but it is not. Once the RIL plumbing sets the - // the PHONE_STATE_NORMAL audio state the AudioManager::mPhoneAudioAgent - // member will call the NotifyStoppedPlaying() method causing that this function will - // be called again and therefore the audio manager sets the - // PHONE_STATE_IN_COMMUNICATION audio state. - - if ((mPhoneState == PHONE_STATE_IN_CALL) || - (mPhoneState == PHONE_STATE_RINGTONE)) { - return; - } - - RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); - bool telephonyChannelIsActive = service && service->TelephonyChannelIsActive(); - telephonyChannelIsActive ? SetPhoneState(PHONE_STATE_IN_COMMUNICATION) : - SetPhoneState(PHONE_STATE_NORMAL); -} - -nsresult -AudioManager::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - if ((strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID) == 0) || - (strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID) == 0) || - (strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID) == 0) || - (strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID) == 0)) { - nsCString address = NS_ConvertUTF16toUTF8(nsDependentString(aData)); - if (address.IsEmpty()) { - NS_WARNING(nsPrintfCString("Invalid address of %s", aTopic).get()); - return NS_ERROR_FAILURE; - } - - HandleBluetoothStatusChanged(aSubject, aTopic, address); - return NS_OK; - } - - else if (!strcmp(aTopic, AUDIO_CHANNEL_PROCESS_CHANGED)) { - HandleAudioChannelProcessChanged(); - return NS_OK; - } - - // To process the volume control on each volume categories according to - // change of settings - else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) { - RootedDictionary<dom::SettingChangeNotification> setting(RootingCx()); - if (!WrappedJSToDictionary(aSubject, setting)) { - return NS_OK; - } - if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) { - return NS_OK; - } - if (!setting.mValue.isNumber()) { - return NS_OK; - } - - uint32_t volIndex = setting.mValue.toNumber(); - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - if (setting.mKey.EqualsASCII(gVolumeData[idx].mChannelName)) { - SetStreamVolumeIndex(gVolumeData[idx].mStreamType, volIndex); - return NS_OK; - } - } - } - - NS_WARNING("Unexpected topic in AudioManager"); - return NS_ERROR_FAILURE; -} - -static void -NotifyHeadphonesStatus(hal::SwitchState aState) -{ - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - if (aState == hal::SWITCH_STATE_HEADSET) { - obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADSET); - } else if (aState == hal::SWITCH_STATE_HEADPHONE) { - obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADPHONE); - } else if (aState == hal::SWITCH_STATE_OFF) { - obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_OFF); - } else { - obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_UNKNOWN); - } - } -} - -class HeadphoneSwitchObserver : public hal::SwitchObserver -{ -public: - void Notify(const hal::SwitchEvent& aEvent) { - RefPtr<AudioManager> audioManager = AudioManager::GetInstance(); - MOZ_ASSERT(audioManager); - audioManager->HandleHeadphoneSwitchEvent(aEvent); - } -}; - -void -AudioManager::HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent) -{ - NotifyHeadphonesStatus(aEvent.status()); - // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker. - if (aEvent.status() == hal::SWITCH_STATE_OFF && mSwitchDone) { - - RefPtr<AudioManager> self = this; - nsCOMPtr<nsIRunnable> runnable = - NS_NewRunnableFunction([self]() { - if (self->mSwitchDone) { - return; - } - self->UpdateHeadsetConnectionState(hal::SWITCH_STATE_OFF); - self->mSwitchDone = true; - }); - MessageLoop::current()->PostDelayedTask( - MakeAndAddRef<RunnableCallTask>(runnable), 1000); - mSwitchDone = false; - } else if (aEvent.status() != hal::SWITCH_STATE_OFF) { - UpdateHeadsetConnectionState(aEvent.status()); - mSwitchDone = true; - } - // Handle the coexistence of a2dp / headset device, latest one wins. -#if ANDROID_VERSION >= 17 - int32_t forceUse = 0; - GetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, &forceUse); - if (aEvent.status() != hal::SWITCH_STATE_OFF && mBluetoothA2dpEnabled) { - SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NO_BT_A2DP); - } else if (forceUse == AUDIO_POLICY_FORCE_NO_BT_A2DP) { - SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE); - } -#endif -} - -AudioManager::AudioManager() - : mPhoneState(PHONE_STATE_CURRENT) - , mIsVolumeInited(false) - , mAudioOutDevicesUpdated(0) - , mSwitchDone(true) -#if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17 - , mBluetoothA2dpEnabled(false) -#endif -#ifdef MOZ_B2G_BT - , mA2dpSwitchDone(true) -#endif - , mObserver(new HeadphoneSwitchObserver()) -{ - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) { - mAudioDeviceTableIdMaps.Put(kAudioDeviceInfos[idx].value, idx); - } - - AudioSystem::setErrorCallback(BinderDeadCallback); -#if ANDROID_VERSION >= 21 - android::sp<GonkAudioPortCallback> callback = new GonkAudioPortCallback(); - AudioSystem::setAudioPortCallback(callback); -#endif - - // Create VolumeStreamStates - for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) { - VolumeStreamState* streamState = - new VolumeStreamState(*this, static_cast<audio_stream_type_t>(loop)); - mStreamStates.AppendElement(streamState); - } - // Initialize stream volumes with default values - for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { - uint32_t volIndex = sDefaultStreamVolumeTbl[streamType]; - SetStreamVolumeForDevice(streamType, volIndex, AUDIO_DEVICE_OUT_DEFAULT); - } - UpdateCachedActiveDevicesForStreams(); - - RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver); - // Initialize headhone/heaset status - UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES)); - NotifyHeadphonesStatus(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES)); - - // Get the initial volume index from settings DB during boot up. - InitVolumeFromDatabase(); - - // Gecko only control stream volume not master so set to default value - // directly. - AudioSystem::setMasterVolume(1.0); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - NS_ENSURE_TRUE_VOID(obs); - if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID, false))) { - NS_WARNING("Failed to add bluetooth sco status changed observer!"); - } - if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) { - NS_WARNING("Failed to add bluetooth a2dp status changed observer!"); - } - if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID, false))) { - NS_WARNING("Failed to add bluetooth hfp status changed observer!"); - } - if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID, false))) { - NS_WARNING("Failed to add bluetooth hfp NREC status changed observer!"); - } - if (NS_FAILED(obs->AddObserver(this, MOZ_SETTINGS_CHANGE_ID, false))) { - NS_WARNING("Failed to add mozsettings-changed observer!"); - } - if (NS_FAILED(obs->AddObserver(this, AUDIO_CHANNEL_PROCESS_CHANGED, false))) { - NS_WARNING("Failed to add audio-channel-process-changed observer!"); - } - -} - -AudioManager::~AudioManager() { - AudioSystem::setErrorCallback(nullptr); -#if ANDROID_VERSION >= 21 - AudioSystem::setAudioPortCallback(nullptr); -#endif - hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - NS_ENSURE_TRUE_VOID(obs); - if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) { - NS_WARNING("Failed to remove bluetooth sco status changed observer!"); - } - if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) { - NS_WARNING("Failed to remove bluetooth a2dp status changed observer!"); - } - if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID))) { - NS_WARNING("Failed to remove bluetooth hfp status changed observer!"); - } - if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID))) { - NS_WARNING("Failed to remove bluetooth hfp NREC status changed observer!"); - } - if (NS_FAILED(obs->RemoveObserver(this, MOZ_SETTINGS_CHANGE_ID))) { - NS_WARNING("Failed to remove mozsettings-changed observer!"); - } - if (NS_FAILED(obs->RemoveObserver(this, AUDIO_CHANNEL_PROCESS_CHANGED))) { - NS_WARNING("Failed to remove audio-channel-process-changed!"); - } -} - -static StaticRefPtr<AudioManager> sAudioManager; - -already_AddRefed<AudioManager> -AudioManager::GetInstance() -{ - // Avoid createing AudioManager from content process. - if (!XRE_IsParentProcess()) { - MOZ_CRASH("Non-chrome processes should not get here."); - } - - // Avoid createing multiple AudioManager instance inside main process. - if (!sAudioManager) { - sAudioManager = new AudioManager(); - ClearOnShutdown(&sAudioManager); - } - - RefPtr<AudioManager> audioMgr = sAudioManager.get(); - return audioMgr.forget(); -} - -NS_IMETHODIMP -AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted) -{ - - if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -AudioManager::SetMicrophoneMuted(bool aMicrophoneMuted) -{ - if (!AudioSystem::muteMicrophone(aMicrophoneMuted)) { - return NS_OK; - } - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -AudioManager::GetPhoneState(int32_t* aState) -{ - *aState = mPhoneState; - return NS_OK; -} - -NS_IMETHODIMP -AudioManager::SetPhoneState(int32_t aState) -{ - if (mPhoneState == aState) { - return NS_OK; - } - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (obs) { - nsString state; - state.AppendInt(aState); - obs->NotifyObservers(nullptr, "phone-state-changed", state.get()); - } - -#if ANDROID_VERSION < 17 - if (AudioSystem::setPhoneState(aState)) { -#else - if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) { -#endif - return NS_ERROR_FAILURE; - } - -#if ANDROID_VERSION < 21 - // Manually call it, since AudioPortCallback is not supported. - // Current volumes might be changed by updating active devices in android - // AudioPolicyManager. - MaybeUpdateVolumeSettingToDatabase(); -#endif - mPhoneState = aState; - return NS_OK; -} - -NS_IMETHODIMP -AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce) -{ -#if ANDROID_VERSION >= 15 - status_t status = AudioSystem::setForceUse( - (audio_policy_force_use_t)aUsage, - (audio_policy_forced_cfg_t)aForce); -#if ANDROID_VERSION < 21 - // Manually call it, since AudioPortCallback is not supported. - // Current volumes might be changed by updating active devices in android - // AudioPolicyManager. - MaybeUpdateVolumeSettingToDatabase(); -#endif - return status ? NS_ERROR_FAILURE : NS_OK; -#else - NS_NOTREACHED("Doesn't support force routing on GB version"); - return NS_ERROR_UNEXPECTED; -#endif -} - -NS_IMETHODIMP -AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) { -#if ANDROID_VERSION >= 15 - *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage); - return NS_OK; -#else - NS_NOTREACHED("Doesn't support force routing on GB version"); - return NS_ERROR_UNEXPECTED; -#endif -} - -NS_IMETHODIMP -AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex) -{ - if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { - return NS_ERROR_INVALID_ARG; - } - - return SetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex); -} - -NS_IMETHODIMP -AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex) -{ - if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { - return NS_ERROR_INVALID_ARG; - } - - if (!aIndex) { - return NS_ERROR_NULL_POINTER; - } - - return GetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex); -} - -NS_IMETHODIMP -AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex) -{ - if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { - return NS_ERROR_INVALID_ARG; - } - - if (!aMaxIndex) { - return NS_ERROR_NULL_POINTER; - } - - *aMaxIndex = mStreamStates[sChannelStreamTbl[aChannel]]->GetMaxIndex(); - return NS_OK; -} - -nsresult -AudioManager::ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const -{ - if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { - return NS_ERROR_INVALID_ARG; - } - - uint32_t maxIndex = mStreamStates[aStream]->GetMaxIndex(); - if (aIndex > maxIndex) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -nsresult -AudioManager::SetStreamVolumeForDevice(int32_t aStream, - uint32_t aIndex, - uint32_t aDevice) -{ - if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { - return NS_ERROR_INVALID_ARG; - } - - int32_t streamAlias = sStreamVolumeAliasTbl[aStream]; - VolumeStreamState* streamState = mStreamStates[streamAlias].get(); - return streamState->SetVolumeIndexToAliasStreams(aIndex, aDevice); -} - -nsresult -AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex) -{ - if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { - return NS_ERROR_INVALID_ARG; - } - - int32_t streamAlias = sStreamVolumeAliasTbl[aStream]; - - nsresult rv; - for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { - if (streamAlias == sStreamVolumeAliasTbl[streamType]) { - rv = mStreamStates[streamType]->SetVolumeIndexToActiveDevices(aIndex); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - } - - // AUDIO_STREAM_FM is not used on recent gonk. - // AUDIO_STREAM_MUSIC is used for FM radio volume control. -#if ANDROID_VERSION < 19 - if (streamAlias == AUDIO_STREAM_MUSIC && IsFmOutConnected()) { - rv = mStreamStates[AUDIO_STREAM_FM]-> - SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_FM); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } -#endif - - MaybeUpdateVolumeSettingToDatabase(); - return NS_OK; -} - -nsresult -AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) -{ - if (!aIndex) { - return NS_ERROR_INVALID_ARG; - } - - if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { - return NS_ERROR_INVALID_ARG; - } - - *aIndex = mStreamStates[aStream]->GetVolumeIndex(); - return NS_OK; -} - -nsAutoCString -AudioManager::AppendDeviceToVolumeSetting(const char* aName, uint32_t aDevice) -{ - nsAutoCString topic; - topic.Assign(aName); - topic.Append("."); - uint32_t index = 0; - DebugOnly<bool> exist = mAudioDeviceTableIdMaps.Get(aDevice, &index); - MOZ_ASSERT(exist); - topic.Append(kAudioDeviceInfos[index].tag); - return topic; -} - -void -AudioManager::InitVolumeFromDatabase() -{ - nsresult rv; - nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - nsCOMPtr<nsISettingsServiceLock> lock; - rv = service->CreateLock(nullptr, getter_AddRefs(lock)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - RefPtr<VolumeInitCallback> callback = new VolumeInitCallback(); - MOZ_ASSERT(callback); - callback->GetPromise()->Then(AbstractThread::MainThread(), __func__, this, - &AudioManager::InitDeviceVolumeSucceeded, - &AudioManager::InitDeviceVolumeFailed); - - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - for (uint32_t idx2 = 0; idx2 < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx2) { - lock->Get(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName, - kAudioDeviceInfos[idx2].value).get(), - callback); - } - } -} - -void -AudioManager::InitDeviceVolumeSucceeded() -{ - mIsVolumeInited = true; - MaybeUpdateVolumeSettingToDatabase(true); -} - -void -AudioManager::InitDeviceVolumeFailed(const char* aError) -{ - // Default volume of AUDIO_DEVICE_OUT_DEFAULT is already set. - mIsVolumeInited = true; - MaybeUpdateVolumeSettingToDatabase(true); - NS_WARNING(aError); -} - -void -AudioManager::MaybeUpdateVolumeSettingToDatabase(bool aForce) -{ - if (!mIsVolumeInited) { - return; - } - - nsCOMPtr<nsISettingsServiceLock> lock = GetSettingServiceLock(); - if (NS_WARN_IF(!lock)) { - return; - } - - // Send events to update the Gaia volumes - JS::Rooted<JS::Value> value(RootingCx()); - uint32_t volume = 0; - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - int32_t streamType = gVolumeData[idx].mStreamType; - VolumeStreamState* streamState = mStreamStates[streamType].get(); - if(!aForce && !streamState->IsDevicesChanged()) { - continue; - } - // Get volume index of active device. - volume = streamState->GetVolumeIndex(); - value.setInt32(volume); - lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr); - } - - // For reducing the code dependency, Gaia doesn't need to know the - // device volume, it only need to care about different volume categories. - // However, we need to send the setting volume to the permanent database, - // so that we can store the volume setting even if the phone reboots. - - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - int32_t streamType = gVolumeData[idx].mStreamType; - VolumeStreamState* streamState = mStreamStates[streamType].get(); - - if(!streamState->IsVolumeIndexesChanged()) { - continue; - } - - uint32_t remainingDevices = mAudioOutDevicesUpdated; - for (uint32_t i = 0; remainingDevices != 0; i++) { - uint32_t device = (1 << i); - if ((device & remainingDevices) == 0) { - continue; - } - remainingDevices &= ~device; - if (!mAudioDeviceTableIdMaps.Get(device, nullptr)) { - continue; - } - volume = streamState->GetVolumeIndex(device); - value.setInt32(volume); - lock->Set(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName, - device).get(), - value, nullptr, nullptr); - } - } - - // Clear changed flags - for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { - int32_t streamType = gVolumeData[idx].mStreamType; - mStreamStates[streamType]->ClearDevicesChanged(); - mStreamStates[streamType]->ClearVolumeIndexesChanged(); - } - // Clear mAudioOutDevicesUpdated - mAudioOutDevicesUpdated = 0; -} - -void -AudioManager::UpdateCachedActiveDevicesForStreams() -{ - // This function updates cached active devices for streams. - // It is used for optimization of GetDevicesForStream() since L. - // AudioManager could know when active devices - // are changed in AudioPolicyManager by onAudioPortListUpdate(). - // Except it, AudioManager normally do not need to ask AuidoPolicyManager - // about current active devices of streams and could use cached values. - // Before L, onAudioPortListUpdate() does not exist and GetDevicesForStream() - // does not use the cache. Therefore this function do nothing. -#if ANDROID_VERSION >= 21 - for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { - // Update cached active devices of stream - mStreamStates[streamType]->IsDevicesChanged(false /* aFromCache */); - } -#endif -} - -uint32_t -AudioManager::GetDevicesForStream(int32_t aStream, bool aFromCache) -{ -#if ANDROID_VERSION >= 21 - // Since Lollipop, devices update could be notified by AudioPortCallback. - // Cached values can be used if there is no update. - if (aFromCache) { - return mStreamStates[aStream]->GetLastDevices(); - } -#endif - -#if ANDROID_VERSION >= 17 - audio_devices_t devices = - AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(aStream)); - - return static_cast<uint32_t>(devices); -#else - // Per audio out device volume is not supported. - // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB. - return AUDIO_DEVICE_OUT_SPEAKER; -#endif -} - -uint32_t -AudioManager::GetDeviceForStream(int32_t aStream) -{ - uint32_t devices = - GetDevicesForStream(static_cast<audio_stream_type_t>(aStream)); - uint32_t device = SelectDeviceFromDevices(devices); - return device; -} - -/* static */ uint32_t -AudioManager::SelectDeviceFromDevices(uint32_t aOutDevices) -{ - uint32_t device = aOutDevices; - - // See android AudioService.getDeviceForStream(). - // AudioPolicyManager expects it. - // See also android AudioPolicyManager::getDeviceForVolume(). - if ((device & (device - 1)) != 0) { - // Multiple device selection. - if ((device & AUDIO_DEVICE_OUT_SPEAKER) != 0) { - device = AUDIO_DEVICE_OUT_SPEAKER; -#if ANDROID_VERSION >= 21 - } else if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) != 0) { - device = AUDIO_DEVICE_OUT_HDMI_ARC; - } else if ((device & AUDIO_DEVICE_OUT_SPDIF) != 0) { - device = AUDIO_DEVICE_OUT_SPDIF; - } else if ((device & AUDIO_DEVICE_OUT_AUX_LINE) != 0) { - device = AUDIO_DEVICE_OUT_AUX_LINE; -#endif - } else { - device &= AUDIO_DEVICE_OUT_ALL_A2DP; - } - } - MOZ_ASSERT(audio_is_output_device(device)); - return device; -} -AudioManager::VolumeStreamState::VolumeStreamState(AudioManager& aManager, - int32_t aStreamType) - : mManager(aManager) - , mStreamType(aStreamType) - , mLastDevices(0) - , mIsDevicesChanged(true) - , mIsVolumeIndexesChanged(true) -{ - InitStreamVolume(); -} - -bool -AudioManager::VolumeStreamState::IsDevicesChanged(bool aFromCache) -{ - uint32_t devices = mManager.GetDevicesForStream(mStreamType, aFromCache); - if (devices != mLastDevices) { - mLastDevices = devices; - mIsDevicesChanged = true; - } - return mIsDevicesChanged; -} - -void -AudioManager::VolumeStreamState::ClearDevicesChanged() -{ - mIsDevicesChanged = false; -} - -bool -AudioManager::VolumeStreamState::IsVolumeIndexesChanged() -{ - return mIsVolumeIndexesChanged; -} - -void -AudioManager::VolumeStreamState::ClearVolumeIndexesChanged() -{ - mIsVolumeIndexesChanged = false; -} - -void -AudioManager::VolumeStreamState::InitStreamVolume() -{ - AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(mStreamType), - 0, - GetMaxIndex()); -} - -uint32_t -AudioManager::VolumeStreamState::GetMaxIndex() -{ - return sMaxStreamVolumeTbl[mStreamType]; -} - -uint32_t -AudioManager::VolumeStreamState::GetDefaultIndex() -{ - return sDefaultStreamVolumeTbl[mStreamType]; -} - -uint32_t -AudioManager::VolumeStreamState::GetVolumeIndex() -{ - uint32_t device = mManager.GetDeviceForStream(mStreamType); - return GetVolumeIndex(device); -} - -uint32_t -AudioManager::VolumeStreamState::GetVolumeIndex(uint32_t aDevice) -{ - uint32_t index = 0; - bool ret = mVolumeIndexes.Get(aDevice, &index); - if (!ret) { - index = mVolumeIndexes.Get(AUDIO_DEVICE_OUT_DEFAULT); - } - return index; -} - -nsresult -AudioManager::VolumeStreamState::SetVolumeIndexToActiveDevices(uint32_t aIndex) -{ - uint32_t device = mManager.GetDeviceForStream(mStreamType); - - // Update volume index for device - uint32_t oldVolumeIndex = 0; - bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex); - if (exist && aIndex == oldVolumeIndex) { - // No update - return NS_OK; - } - - // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active - // devices for stream. - nsresult rv; - rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, device); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -AudioManager::VolumeStreamState::SetVolumeIndexToAliasStreams(uint32_t aIndex, - uint32_t aDevice) -{ - uint32_t oldVolumeIndex = 0; - bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex); - if (exist && aIndex == oldVolumeIndex) { - // No update - return NS_OK; - } - - nsresult rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, aDevice); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { - if ((streamType != mStreamType) && - sStreamVolumeAliasTbl[streamType] == mStreamType) { - // Rescaling of index is not necessary. - rv = mManager.mStreamStates[streamType]-> - SetVolumeIndexToAliasStreams(aIndex, aDevice); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - } - - return NS_OK; -} - -nsresult -AudioManager::VolumeStreamState::SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice) -{ - nsresult rv; - if (aDevice == AUDIO_DEVICE_OUT_SPEAKER || aDevice == AUDIO_DEVICE_OUT_EARPIECE) { - // Set AUDIO_DEVICE_OUT_SPEAKER and AUDIO_DEVICE_OUT_EARPIECE to same volume. - rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - // No alias device - rv = SetVolumeIndex(aIndex, aDevice); - } - return rv; -} - -nsresult -AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex, - uint32_t aDevice, - bool aUpdateCache) -{ - status_t rv; -#if ANDROID_VERSION >= 17 - if (aUpdateCache) { - mVolumeIndexes.Put(aDevice, aIndex); - mIsVolumeIndexesChanged = true; - mManager.AudioOutDeviceUpdated(aDevice); - } - - rv = AudioSystem::setStreamVolumeIndex( - static_cast<audio_stream_type_t>(mStreamType), - aIndex, - aDevice); - return rv ? NS_ERROR_FAILURE : NS_OK; -#else - if (aUpdateCache) { - // Per audio out device volume is not supported. - // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB. - mVolumeIndexes.Put(AUDIO_DEVICE_OUT_SPEAKER, aIndex); - mIsVolumeIndexesChanged = true; - mManager.AudioOutDeviceUpdated(AUDIO_DEVICE_OUT_SPEAKER); - } - rv = AudioSystem::setStreamVolumeIndex( - static_cast<audio_stream_type_t>(mStreamType), - aIndex); - return rv ? NS_ERROR_FAILURE : NS_OK; -#endif -} - -void -AudioManager::VolumeStreamState::RestoreVolumeIndexToAllDevices() -{ - for (auto iter = mVolumeIndexes.Iter(); !iter.Done(); iter.Next()) { - const uint32_t& key = iter.Key(); - uint32_t& index = iter.Data(); - SetVolumeIndex(key, index, /* aUpdateCache */ false); - } -} - -} /* namespace gonk */ -} /* namespace dom */ -} /* namespace mozilla */ |