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

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

mercurial