1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/AudioManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,772 @@ 1.4 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors 1.5 + * 1.6 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.7 + * you may not use this file except in compliance with the License. 1.8 + * You may obtain a copy of the License at 1.9 + * 1.10 + * http://www.apache.org/licenses/LICENSE-2.0 1.11 + * 1.12 + * Unless required by applicable law or agreed to in writing, software 1.13 + * distributed under the License is distributed on an "AS IS" BASIS, 1.14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.15 + * See the License for the specific language governing permissions and 1.16 + * limitations under the License. 1.17 + */ 1.18 + 1.19 +#include <android/log.h> 1.20 +#include <cutils/properties.h> 1.21 + 1.22 +#include "AudioChannelService.h" 1.23 +#include "AudioManager.h" 1.24 + 1.25 +#include "nsIObserverService.h" 1.26 +#ifdef MOZ_B2G_RIL 1.27 +#include "nsIRadioInterfaceLayer.h" 1.28 +#endif 1.29 +#include "nsISettingsService.h" 1.30 +#include "nsPrintfCString.h" 1.31 + 1.32 +#include "mozilla/Hal.h" 1.33 +#include "mozilla/Services.h" 1.34 +#include "base/message_loop.h" 1.35 + 1.36 +#include "BluetoothCommon.h" 1.37 +#include "BluetoothHfpManagerBase.h" 1.38 + 1.39 +#include "nsJSUtils.h" 1.40 +#include "nsCxPusher.h" 1.41 +#include "nsThreadUtils.h" 1.42 +#include "nsServiceManagerUtils.h" 1.43 +#include "nsComponentManagerUtils.h" 1.44 + 1.45 +using namespace mozilla::dom::gonk; 1.46 +using namespace android; 1.47 +using namespace mozilla::hal; 1.48 +using namespace mozilla; 1.49 +using namespace mozilla::dom::bluetooth; 1.50 + 1.51 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args) 1.52 + 1.53 +#define HEADPHONES_STATUS_HEADSET MOZ_UTF16("headset") 1.54 +#define HEADPHONES_STATUS_HEADPHONE MOZ_UTF16("headphone") 1.55 +#define HEADPHONES_STATUS_OFF MOZ_UTF16("off") 1.56 +#define HEADPHONES_STATUS_UNKNOWN MOZ_UTF16("unknown") 1.57 +#define HEADPHONES_STATUS_CHANGED "headphones-status-changed" 1.58 +#define MOZ_SETTINGS_CHANGE_ID "mozsettings-changed" 1.59 + 1.60 +static void BinderDeadCallback(status_t aErr); 1.61 +static void InternalSetAudioRoutes(SwitchState aState); 1.62 +// Refer AudioService.java from Android 1.63 +static int sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = { 1.64 + 5, // voice call 1.65 + 15, // system 1.66 + 15, // ring 1.67 + 15, // music 1.68 + 15, // alarm 1.69 + 15, // notification 1.70 + 15, // BT SCO 1.71 + 15, // enforced audible 1.72 + 15, // DTMF 1.73 + 15, // TTS 1.74 + 15, // FM 1.75 +}; 1.76 +// A bitwise variable for recording what kind of headset is attached. 1.77 +static int sHeadsetState; 1.78 +static const int kBtSampleRate = 8000; 1.79 +static bool sSwitchDone = true; 1.80 + 1.81 +namespace mozilla { 1.82 +namespace dom { 1.83 +namespace gonk { 1.84 +class RecoverTask : public nsRunnable 1.85 +{ 1.86 +public: 1.87 + RecoverTask() {} 1.88 + NS_IMETHODIMP Run() { 1.89 + nsCOMPtr<nsIAudioManager> amService = do_GetService(NS_AUDIOMANAGER_CONTRACTID); 1.90 + NS_ENSURE_TRUE(amService, NS_OK); 1.91 + AudioManager *am = static_cast<AudioManager *>(amService.get()); 1.92 + for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) { 1.93 + AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0, 1.94 + sMaxStreamVolumeTbl[loop]); 1.95 + int32_t index; 1.96 + am->GetStreamVolumeIndex(loop, &index); 1.97 + am->SetStreamVolumeIndex(loop, index); 1.98 + } 1.99 + 1.100 + if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) 1.101 + InternalSetAudioRoutes(SWITCH_STATE_HEADSET); 1.102 + else if (sHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) 1.103 + InternalSetAudioRoutes(SWITCH_STATE_HEADPHONE); 1.104 + else 1.105 + InternalSetAudioRoutes(SWITCH_STATE_OFF); 1.106 + 1.107 + int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID; 1.108 + am->GetPhoneState(&phoneState); 1.109 +#if ANDROID_VERSION < 17 1.110 + AudioSystem::setPhoneState(phoneState); 1.111 +#else 1.112 + AudioSystem::setPhoneState(static_cast<audio_mode_t>(phoneState)); 1.113 +#endif 1.114 + 1.115 + AudioSystem::get_audio_flinger(); 1.116 + return NS_OK; 1.117 + } 1.118 +}; 1.119 + 1.120 +class AudioChannelVolInitCallback MOZ_FINAL : public nsISettingsServiceCallback 1.121 +{ 1.122 +public: 1.123 + NS_DECL_ISUPPORTS 1.124 + 1.125 + AudioChannelVolInitCallback() {} 1.126 + 1.127 + NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) 1.128 + { 1.129 + nsCOMPtr<nsIAudioManager> audioManager = 1.130 + do_GetService(NS_AUDIOMANAGER_CONTRACTID); 1.131 + NS_ENSURE_TRUE(JSVAL_IS_INT(aResult), NS_OK); 1.132 + 1.133 + int32_t volIndex = JSVAL_TO_INT(aResult); 1.134 + if (aName.EqualsLiteral("audio.volume.content")) { 1.135 + audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content, 1.136 + volIndex); 1.137 + } else if (aName.EqualsLiteral("audio.volume.notification")) { 1.138 + audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification, 1.139 + volIndex); 1.140 + } else if (aName.EqualsLiteral("audio.volume.alarm")) { 1.141 + audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm, 1.142 + volIndex); 1.143 + } else if (aName.EqualsLiteral("audio.volume.telephony")) { 1.144 + audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony, 1.145 + volIndex); 1.146 + } else if (aName.EqualsLiteral("audio.volume.bt_sco")) { 1.147 + static_cast<AudioManager *>(audioManager.get())->SetStreamVolumeIndex( 1.148 + AUDIO_STREAM_BLUETOOTH_SCO, volIndex); 1.149 + } else { 1.150 + MOZ_ASSUME_UNREACHABLE("unexpected audio channel for initializing " 1.151 + "volume control"); 1.152 + } 1.153 + 1.154 + return NS_OK; 1.155 + } 1.156 + 1.157 + NS_IMETHOD HandleError(const nsAString& aName) 1.158 + { 1.159 + LOG("AudioChannelVolInitCallback::HandleError: %s\n", 1.160 + NS_ConvertUTF16toUTF8(aName).get()); 1.161 + return NS_OK; 1.162 + } 1.163 +}; 1.164 + 1.165 +NS_IMPL_ISUPPORTS(AudioChannelVolInitCallback, nsISettingsServiceCallback) 1.166 +} /* namespace gonk */ 1.167 +} /* namespace dom */ 1.168 +} /* namespace mozilla */ 1.169 + 1.170 +static void 1.171 +BinderDeadCallback(status_t aErr) 1.172 +{ 1.173 + if (aErr == DEAD_OBJECT) { 1.174 + NS_DispatchToMainThread(new RecoverTask()); 1.175 + } 1.176 +} 1.177 + 1.178 +static bool 1.179 +IsDeviceOn(audio_devices_t device) 1.180 +{ 1.181 + if (static_cast< 1.182 + audio_policy_dev_state_t (*) (audio_devices_t, const char *) 1.183 + >(AudioSystem::getDeviceConnectionState)) 1.184 + return AudioSystem::getDeviceConnectionState(device, "") == 1.185 + AUDIO_POLICY_DEVICE_STATE_AVAILABLE; 1.186 + 1.187 + return false; 1.188 +} 1.189 + 1.190 +static void ProcessDelayedAudioRoute(SwitchState aState) 1.191 +{ 1.192 + if (sSwitchDone) 1.193 + return; 1.194 + InternalSetAudioRoutes(aState); 1.195 + sSwitchDone = true; 1.196 +} 1.197 + 1.198 +NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver) 1.199 + 1.200 +static void 1.201 +InternalSetAudioRoutesICS(SwitchState aState) 1.202 +{ 1.203 + if (aState == SWITCH_STATE_HEADSET) { 1.204 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET, 1.205 + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, ""); 1.206 + sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET; 1.207 + } else if (aState == SWITCH_STATE_HEADPHONE) { 1.208 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 1.209 + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, ""); 1.210 + sHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE; 1.211 + } else if (aState == SWITCH_STATE_OFF) { 1.212 + AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(sHeadsetState), 1.213 + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, ""); 1.214 + sHeadsetState = 0; 1.215 + } 1.216 +} 1.217 + 1.218 +static void 1.219 +InternalSetAudioRoutes(SwitchState aState) 1.220 +{ 1.221 + if (static_cast< 1.222 + status_t (*)(audio_devices_t, audio_policy_dev_state_t, const char*) 1.223 + >(AudioSystem::setDeviceConnectionState)) { 1.224 + InternalSetAudioRoutesICS(aState); 1.225 + } else { 1.226 + NS_NOTREACHED("Doesn't support audio routing on GB version"); 1.227 + } 1.228 +} 1.229 + 1.230 +void 1.231 +AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject, 1.232 + const char* aTopic, 1.233 + const nsCString aAddress) 1.234 +{ 1.235 +#ifdef MOZ_B2G_BT 1.236 + bool status; 1.237 + if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { 1.238 + BluetoothHfpManagerBase* hfp = 1.239 + static_cast<BluetoothHfpManagerBase*>(aSubject); 1.240 + status = hfp->IsScoConnected(); 1.241 + } else { 1.242 + BluetoothProfileManagerBase* profile = 1.243 + static_cast<BluetoothProfileManagerBase*>(aSubject); 1.244 + status = profile->IsConnected(); 1.245 + } 1.246 + 1.247 + audio_policy_dev_state_t audioState = status ? 1.248 + AUDIO_POLICY_DEVICE_STATE_AVAILABLE : 1.249 + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; 1.250 + 1.251 + if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { 1.252 + if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { 1.253 + String8 cmd; 1.254 + cmd.appendFormat("bt_samplerate=%d", kBtSampleRate); 1.255 + AudioSystem::setParameters(0, cmd); 1.256 + SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO); 1.257 + } else { 1.258 + int32_t force; 1.259 + GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force); 1.260 + if (force == nsIAudioManager::FORCE_BT_SCO) 1.261 + SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE); 1.262 + } 1.263 + } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) { 1.264 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, 1.265 + audioState, aAddress.get()); 1.266 + if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { 1.267 + String8 cmd("bluetooth_enabled=true"); 1.268 + AudioSystem::setParameters(0, cmd); 1.269 + cmd.setTo("A2dpSuspended=false"); 1.270 + AudioSystem::setParameters(0, cmd); 1.271 + } else { 1.272 + String8 cmd("bluetooth_enabled=false"); 1.273 + AudioSystem::setParameters(0, cmd); 1.274 + cmd.setTo("A2dpSuspended=true"); 1.275 + AudioSystem::setParameters(0, cmd); 1.276 + } 1.277 + } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) { 1.278 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 1.279 + audioState, aAddress.get()); 1.280 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, 1.281 + audioState, aAddress.get()); 1.282 + } 1.283 +#endif 1.284 +} 1.285 + 1.286 +nsresult 1.287 +AudioManager::Observe(nsISupports* aSubject, 1.288 + const char* aTopic, 1.289 + const char16_t* aData) 1.290 +{ 1.291 + if ((strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID) == 0) || 1.292 + (strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID) == 0) || 1.293 + (strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID) == 0)) { 1.294 + nsCString address = NS_ConvertUTF16toUTF8(nsDependentString(aData)); 1.295 + if (address.IsEmpty()) { 1.296 + NS_WARNING(nsPrintfCString("Invalid address of %s", aTopic).get()); 1.297 + return NS_ERROR_FAILURE; 1.298 + } 1.299 + 1.300 + HandleBluetoothStatusChanged(aSubject, aTopic, address); 1.301 + return NS_OK; 1.302 + } 1.303 + 1.304 + // To process the volume control on each audio channel according to 1.305 + // change of settings 1.306 + else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) { 1.307 + AutoSafeJSContext cx; 1.308 + nsDependentString dataStr(aData); 1.309 + JS::Rooted<JS::Value> val(cx); 1.310 + if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || 1.311 + !val.isObject()) { 1.312 + return NS_OK; 1.313 + } 1.314 + 1.315 + JS::Rooted<JSObject*> obj(cx, &val.toObject()); 1.316 + JS::Rooted<JS::Value> key(cx); 1.317 + if (!JS_GetProperty(cx, obj, "key", &key) || 1.318 + !key.isString()) { 1.319 + return NS_OK; 1.320 + } 1.321 + 1.322 + JS::Rooted<JSString*> jsKey(cx, JS::ToString(cx, key)); 1.323 + if (!jsKey) { 1.324 + return NS_OK; 1.325 + } 1.326 + nsDependentJSString keyStr; 1.327 + if (!keyStr.init(cx, jsKey) || !keyStr.EqualsLiteral("audio.volume.bt_sco")) { 1.328 + return NS_OK; 1.329 + } 1.330 + 1.331 + JS::Rooted<JS::Value> value(cx); 1.332 + if (!JS_GetProperty(cx, obj, "value", &value) || !value.isInt32()) { 1.333 + return NS_OK; 1.334 + } 1.335 + 1.336 + int32_t index = value.toInt32(); 1.337 + SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, index); 1.338 + 1.339 + return NS_OK; 1.340 + } 1.341 + 1.342 + NS_WARNING("Unexpected topic in AudioManager"); 1.343 + return NS_ERROR_FAILURE; 1.344 +} 1.345 + 1.346 +static void 1.347 +NotifyHeadphonesStatus(SwitchState aState) 1.348 +{ 1.349 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.350 + if (obs) { 1.351 + if (aState == SWITCH_STATE_HEADSET) { 1.352 + obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADSET); 1.353 + } else if (aState == SWITCH_STATE_HEADPHONE) { 1.354 + obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_HEADPHONE); 1.355 + } else if (aState == SWITCH_STATE_OFF) { 1.356 + obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_OFF); 1.357 + } else { 1.358 + obs->NotifyObservers(nullptr, HEADPHONES_STATUS_CHANGED, HEADPHONES_STATUS_UNKNOWN); 1.359 + } 1.360 + } 1.361 +} 1.362 + 1.363 +class HeadphoneSwitchObserver : public SwitchObserver 1.364 +{ 1.365 +public: 1.366 + void Notify(const SwitchEvent& aEvent) { 1.367 + NotifyHeadphonesStatus(aEvent.status()); 1.368 + // When user pulled out the headset, a delay of routing here can avoid the leakage of audio from speaker. 1.369 + if (aEvent.status() == SWITCH_STATE_OFF && sSwitchDone) { 1.370 + MessageLoop::current()->PostDelayedTask( 1.371 + FROM_HERE, NewRunnableFunction(&ProcessDelayedAudioRoute, SWITCH_STATE_OFF), 1000); 1.372 + sSwitchDone = false; 1.373 + } else if (aEvent.status() != SWITCH_STATE_OFF) { 1.374 + InternalSetAudioRoutes(aEvent.status()); 1.375 + sSwitchDone = true; 1.376 + } 1.377 + } 1.378 +}; 1.379 + 1.380 +AudioManager::AudioManager() 1.381 + : mPhoneState(PHONE_STATE_CURRENT) 1.382 + , mObserver(new HeadphoneSwitchObserver()) 1.383 +#ifdef MOZ_B2G_RIL 1.384 + , mMuteCallToRIL(false) 1.385 +#endif 1.386 +{ 1.387 + RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver); 1.388 + 1.389 + InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES)); 1.390 + NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES)); 1.391 + 1.392 + for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) { 1.393 + AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0, 1.394 + sMaxStreamVolumeTbl[loop]); 1.395 + mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop]; 1.396 + } 1.397 + // Force publicnotification to output at maximal volume 1.398 + SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, 1.399 + sMaxStreamVolumeTbl[AUDIO_STREAM_ENFORCED_AUDIBLE]); 1.400 + 1.401 + // Get the initial volume index from settings DB during boot up. 1.402 + nsCOMPtr<nsISettingsService> settingsService = 1.403 + do_GetService("@mozilla.org/settingsService;1"); 1.404 + NS_ENSURE_TRUE_VOID(settingsService); 1.405 + nsCOMPtr<nsISettingsServiceLock> lock; 1.406 + nsresult rv = settingsService->CreateLock(nullptr, getter_AddRefs(lock)); 1.407 + NS_ENSURE_SUCCESS_VOID(rv); 1.408 + nsCOMPtr<nsISettingsServiceCallback> callback = new AudioChannelVolInitCallback(); 1.409 + NS_ENSURE_TRUE_VOID(callback); 1.410 + lock->Get("audio.volume.content", callback); 1.411 + lock->Get("audio.volume.notification", callback); 1.412 + lock->Get("audio.volume.alarm", callback); 1.413 + lock->Get("audio.volume.telephony", callback); 1.414 + lock->Get("audio.volume.bt_sco", callback); 1.415 + 1.416 + // Gecko only control stream volume not master so set to default value 1.417 + // directly. 1.418 + AudioSystem::setMasterVolume(1.0); 1.419 + AudioSystem::setErrorCallback(BinderDeadCallback); 1.420 + 1.421 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.422 + NS_ENSURE_TRUE_VOID(obs); 1.423 + if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID, false))) { 1.424 + NS_WARNING("Failed to add bluetooth sco status changed observer!"); 1.425 + } 1.426 + if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) { 1.427 + NS_WARNING("Failed to add bluetooth a2dp status changed observer!"); 1.428 + } 1.429 + if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID, false))) { 1.430 + NS_WARNING("Failed to add bluetooth hfp status changed observer!"); 1.431 + } 1.432 + if (NS_FAILED(obs->AddObserver(this, MOZ_SETTINGS_CHANGE_ID, false))) { 1.433 + NS_WARNING("Failed to add mozsettings-changed observer!"); 1.434 + } 1.435 + 1.436 +#ifdef MOZ_B2G_RIL 1.437 + char value[PROPERTY_VALUE_MAX]; 1.438 + property_get("ro.moz.mute.call.to_ril", value, "false"); 1.439 + if (!strcmp(value, "true")) { 1.440 + mMuteCallToRIL = true; 1.441 + } 1.442 +#endif 1.443 +} 1.444 + 1.445 +AudioManager::~AudioManager() { 1.446 + UnregisterSwitchObserver(SWITCH_HEADPHONES, mObserver); 1.447 + 1.448 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.449 + NS_ENSURE_TRUE_VOID(obs); 1.450 + if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) { 1.451 + NS_WARNING("Failed to remove bluetooth sco status changed observer!"); 1.452 + } 1.453 + if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) { 1.454 + NS_WARNING("Failed to remove bluetooth a2dp status changed observer!"); 1.455 + } 1.456 + if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID))) { 1.457 + NS_WARNING("Failed to remove bluetooth hfp status changed observer!"); 1.458 + } 1.459 + if (NS_FAILED(obs->RemoveObserver(this, MOZ_SETTINGS_CHANGE_ID))) { 1.460 + NS_WARNING("Failed to remove mozsettings-changed observer!"); 1.461 + } 1.462 +} 1.463 + 1.464 +NS_IMETHODIMP 1.465 +AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted) 1.466 +{ 1.467 +#ifdef MOZ_B2G_RIL 1.468 + if (mMuteCallToRIL) { 1.469 + // Simply return cached mIsMicMuted if mute call go via RIL. 1.470 + *aMicrophoneMuted = mIsMicMuted; 1.471 + return NS_OK; 1.472 + } 1.473 +#endif 1.474 + 1.475 + if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) { 1.476 + return NS_ERROR_FAILURE; 1.477 + } 1.478 + return NS_OK; 1.479 +} 1.480 + 1.481 +NS_IMETHODIMP 1.482 +AudioManager::SetMicrophoneMuted(bool aMicrophoneMuted) 1.483 +{ 1.484 + if (!AudioSystem::muteMicrophone(aMicrophoneMuted)) { 1.485 +#ifdef MOZ_B2G_RIL 1.486 + if (mMuteCallToRIL) { 1.487 + // Extra mute request to RIL for specific platform. 1.488 + nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1"); 1.489 + NS_ENSURE_TRUE(ril, NS_ERROR_FAILURE); 1.490 + ril->SetMicrophoneMuted(aMicrophoneMuted); 1.491 + mIsMicMuted = aMicrophoneMuted; 1.492 + } 1.493 +#endif 1.494 + return NS_OK; 1.495 + } 1.496 + return NS_ERROR_FAILURE; 1.497 +} 1.498 + 1.499 +NS_IMETHODIMP 1.500 +AudioManager::GetPhoneState(int32_t* aState) 1.501 +{ 1.502 + *aState = mPhoneState; 1.503 + return NS_OK; 1.504 +} 1.505 + 1.506 +NS_IMETHODIMP 1.507 +AudioManager::SetPhoneState(int32_t aState) 1.508 +{ 1.509 + if (mPhoneState == aState) { 1.510 + return NS_OK; 1.511 + } 1.512 + 1.513 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.514 + if (obs) { 1.515 + nsString state; 1.516 + state.AppendInt(aState); 1.517 + obs->NotifyObservers(nullptr, "phone-state-changed", state.get()); 1.518 + } 1.519 + 1.520 +#if ANDROID_VERSION < 17 1.521 + if (AudioSystem::setPhoneState(aState)) { 1.522 +#else 1.523 + if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) { 1.524 +#endif 1.525 + return NS_ERROR_FAILURE; 1.526 + } 1.527 + 1.528 + mPhoneState = aState; 1.529 + 1.530 + if (mPhoneAudioAgent) { 1.531 + mPhoneAudioAgent->StopPlaying(); 1.532 + mPhoneAudioAgent = nullptr; 1.533 + } 1.534 + 1.535 + if (aState == PHONE_STATE_IN_CALL || aState == PHONE_STATE_RINGTONE) { 1.536 + mPhoneAudioAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); 1.537 + MOZ_ASSERT(mPhoneAudioAgent); 1.538 + if (aState == PHONE_STATE_IN_CALL) { 1.539 + // Telephony doesn't be paused by any other channels. 1.540 + mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Telephony, nullptr); 1.541 + } else { 1.542 + mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Ringer, nullptr); 1.543 + } 1.544 + 1.545 + // Telephony can always play. 1.546 + int32_t canPlay; 1.547 + mPhoneAudioAgent->StartPlaying(&canPlay); 1.548 + } 1.549 + 1.550 + return NS_OK; 1.551 +} 1.552 + 1.553 +NS_IMETHODIMP 1.554 +AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce) 1.555 +{ 1.556 + if (static_cast< 1.557 + status_t (*)(audio_policy_force_use_t, audio_policy_forced_cfg_t) 1.558 + >(AudioSystem::setForceUse)) { 1.559 + // Dynamically resolved the ICS signature. 1.560 + status_t status = AudioSystem::setForceUse( 1.561 + (audio_policy_force_use_t)aUsage, 1.562 + (audio_policy_forced_cfg_t)aForce); 1.563 + return status ? NS_ERROR_FAILURE : NS_OK; 1.564 + } 1.565 + 1.566 + NS_NOTREACHED("Doesn't support force routing on GB version"); 1.567 + return NS_ERROR_UNEXPECTED; 1.568 +} 1.569 + 1.570 +NS_IMETHODIMP 1.571 +AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) { 1.572 + if (static_cast< 1.573 + audio_policy_forced_cfg_t (*)(audio_policy_force_use_t) 1.574 + >(AudioSystem::getForceUse)) { 1.575 + // Dynamically resolved the ICS signature. 1.576 + *aForce = AudioSystem::getForceUse((audio_policy_force_use_t)aUsage); 1.577 + return NS_OK; 1.578 + } 1.579 + 1.580 + NS_NOTREACHED("Doesn't support force routing on GB version"); 1.581 + return NS_ERROR_UNEXPECTED; 1.582 +} 1.583 + 1.584 +NS_IMETHODIMP 1.585 +AudioManager::GetFmRadioAudioEnabled(bool *aFmRadioAudioEnabled) 1.586 +{ 1.587 + *aFmRadioAudioEnabled = IsDeviceOn(AUDIO_DEVICE_OUT_FM); 1.588 + return NS_OK; 1.589 +} 1.590 + 1.591 +NS_IMETHODIMP 1.592 +AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled) 1.593 +{ 1.594 + if (static_cast< 1.595 + status_t (*) (AudioSystem::audio_devices, AudioSystem::device_connection_state, const char *) 1.596 + >(AudioSystem::setDeviceConnectionState)) { 1.597 + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM, 1.598 + aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : 1.599 + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, ""); 1.600 + InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES)); 1.601 + // sync volume with music after powering on fm radio 1.602 + if (aFmRadioAudioEnabled) { 1.603 + int32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC]; 1.604 + SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex); 1.605 + mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex; 1.606 + } 1.607 + return NS_OK; 1.608 + } else { 1.609 + return NS_ERROR_NOT_IMPLEMENTED; 1.610 + } 1.611 +} 1.612 + 1.613 +NS_IMETHODIMP 1.614 +AudioManager::SetAudioChannelVolume(int32_t aChannel, int32_t aIndex) { 1.615 + nsresult status; 1.616 + 1.617 + switch (static_cast<AudioChannel>(aChannel)) { 1.618 + case AudioChannel::Content: 1.619 + // sync FMRadio's volume with content channel. 1.620 + if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) { 1.621 + status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex); 1.622 + NS_ENSURE_SUCCESS(status, status); 1.623 + } 1.624 + status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex); 1.625 + NS_ENSURE_SUCCESS(status, status); 1.626 + status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex); 1.627 + break; 1.628 + case AudioChannel::Notification: 1.629 + status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex); 1.630 + NS_ENSURE_SUCCESS(status, status); 1.631 + status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex); 1.632 + break; 1.633 + case AudioChannel::Alarm: 1.634 + status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex); 1.635 + break; 1.636 + case AudioChannel::Telephony: 1.637 + status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex); 1.638 + break; 1.639 + default: 1.640 + return NS_ERROR_INVALID_ARG; 1.641 + } 1.642 + 1.643 + return status; 1.644 +} 1.645 + 1.646 +NS_IMETHODIMP 1.647 +AudioManager::GetAudioChannelVolume(int32_t aChannel, int32_t* aIndex) { 1.648 + if (!aIndex) { 1.649 + return NS_ERROR_NULL_POINTER; 1.650 + } 1.651 + 1.652 + switch (static_cast<AudioChannel>(aChannel)) { 1.653 + case AudioChannel::Content: 1.654 + MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC] == 1.655 + mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]); 1.656 + *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC]; 1.657 + break; 1.658 + case AudioChannel::Notification: 1.659 + MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == 1.660 + mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]); 1.661 + *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION]; 1.662 + break; 1.663 + case AudioChannel::Alarm: 1.664 + *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM]; 1.665 + break; 1.666 + case AudioChannel::Telephony: 1.667 + *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL]; 1.668 + break; 1.669 + default: 1.670 + return NS_ERROR_INVALID_ARG; 1.671 + } 1.672 + 1.673 + return NS_OK; 1.674 +} 1.675 + 1.676 +NS_IMETHODIMP 1.677 +AudioManager::GetMaxAudioChannelVolume(int32_t aChannel, int32_t* aMaxIndex) { 1.678 + if (!aMaxIndex) { 1.679 + return NS_ERROR_NULL_POINTER; 1.680 + } 1.681 + 1.682 + int32_t stream; 1.683 + switch (static_cast<AudioChannel>(aChannel)) { 1.684 + case AudioChannel::Content: 1.685 + MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC] == 1.686 + sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]); 1.687 + stream = AUDIO_STREAM_MUSIC; 1.688 + break; 1.689 + case AudioChannel::Notification: 1.690 + MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == 1.691 + sMaxStreamVolumeTbl[AUDIO_STREAM_RING]); 1.692 + stream = AUDIO_STREAM_NOTIFICATION; 1.693 + break; 1.694 + case AudioChannel::Alarm: 1.695 + stream = AUDIO_STREAM_ALARM; 1.696 + break; 1.697 + case AudioChannel::Telephony: 1.698 + stream = AUDIO_STREAM_VOICE_CALL; 1.699 + break; 1.700 + default: 1.701 + return NS_ERROR_INVALID_ARG; 1.702 + } 1.703 + 1.704 + *aMaxIndex = sMaxStreamVolumeTbl[stream]; 1.705 + return NS_OK; 1.706 +} 1.707 + 1.708 +nsresult 1.709 +AudioManager::SetStreamVolumeIndex(int32_t aStream, int32_t aIndex) { 1.710 + if (aIndex < 0 || aIndex > sMaxStreamVolumeTbl[aStream]) { 1.711 + return NS_ERROR_INVALID_ARG; 1.712 + } 1.713 + 1.714 + mCurrentStreamVolumeTbl[aStream] = aIndex; 1.715 + status_t status; 1.716 +#if ANDROID_VERSION < 17 1.717 + status = AudioSystem::setStreamVolumeIndex( 1.718 + static_cast<audio_stream_type_t>(aStream), 1.719 + aIndex); 1.720 + return status ? NS_ERROR_FAILURE : NS_OK; 1.721 +#else 1.722 + int device = 0; 1.723 + 1.724 + if (aStream == AUDIO_STREAM_BLUETOOTH_SCO) { 1.725 + device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 1.726 + } else if (aStream == AUDIO_STREAM_FM) { 1.727 + device = AUDIO_DEVICE_OUT_FM; 1.728 + } 1.729 + 1.730 + if (device != 0) { 1.731 + status = AudioSystem::setStreamVolumeIndex( 1.732 + static_cast<audio_stream_type_t>(aStream), 1.733 + aIndex, 1.734 + device); 1.735 + return status ? NS_ERROR_FAILURE : NS_OK; 1.736 + } 1.737 + 1.738 + status = AudioSystem::setStreamVolumeIndex( 1.739 + static_cast<audio_stream_type_t>(aStream), 1.740 + aIndex, 1.741 + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP); 1.742 + status += AudioSystem::setStreamVolumeIndex( 1.743 + static_cast<audio_stream_type_t>(aStream), 1.744 + aIndex, 1.745 + AUDIO_DEVICE_OUT_SPEAKER); 1.746 + status += AudioSystem::setStreamVolumeIndex( 1.747 + static_cast<audio_stream_type_t>(aStream), 1.748 + aIndex, 1.749 + AUDIO_DEVICE_OUT_WIRED_HEADSET); 1.750 + status += AudioSystem::setStreamVolumeIndex( 1.751 + static_cast<audio_stream_type_t>(aStream), 1.752 + aIndex, 1.753 + AUDIO_DEVICE_OUT_WIRED_HEADPHONE); 1.754 + status += AudioSystem::setStreamVolumeIndex( 1.755 + static_cast<audio_stream_type_t>(aStream), 1.756 + aIndex, 1.757 + AUDIO_DEVICE_OUT_EARPIECE); 1.758 + return status ? NS_ERROR_FAILURE : NS_OK; 1.759 +#endif 1.760 +} 1.761 + 1.762 +nsresult 1.763 +AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) { 1.764 + if (!aIndex) { 1.765 + return NS_ERROR_INVALID_ARG; 1.766 + } 1.767 + 1.768 + if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { 1.769 + return NS_ERROR_INVALID_ARG; 1.770 + } 1.771 + 1.772 + *aIndex = mCurrentStreamVolumeTbl[aStream]; 1.773 + 1.774 + return NS_OK; 1.775 +}