dom/system/gonk/AudioManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial