import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1183925 - Part 2: Clean up the AudioChannelService shutdown; r=baku (22c57eaadd)
- Bug 1170117 - Separate volume control. r=baku (ac7560f899)
- Bug 1185422 - Fix -Wtype-limits warnings-as-errors in gonk/AudioManager.cpp. r=alwu (54666877a6)
- Bug 1142933 - New audio channel type for system usages. r=baku (bdb1137600)
This commit is contained in:
2022-03-30 14:31:19 +08:00
parent c49d0c7995
commit 4b190713d2
12 changed files with 484 additions and 188 deletions
+6
View File
@@ -348,6 +348,12 @@ this.PermissionsTable = { geolocation: {
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"audio-channel-system": {
app: DENY_ACTION,
trusted: DENY_ACTION,
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"audio-channel-telephony": {
app: DENY_ACTION,
trusted: DENY_ACTION,
+1
View File
@@ -72,6 +72,7 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
int(AUDIO_AGENT_CHANNEL_ALARM) == int(AudioChannel::Alarm) &&
int(AUDIO_AGENT_CHANNEL_TELEPHONY) == int(AudioChannel::Telephony) &&
int(AUDIO_AGENT_CHANNEL_RINGER) == int(AudioChannel::Ringer) &&
int(AUDIO_AGENT_CHANNEL_SYSTEM) == int(AudioChannel::System) &&
int(AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION) == int(AudioChannel::Publicnotification),
"Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelBinding.h");
+4 -57
View File
@@ -27,9 +27,7 @@
#ifdef MOZ_WIDGET_GONK
#include "nsJSUtils.h"
#include "nsIAudioManager.h"
#include "SpeakerManagerService.h"
#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
#endif
#include "mozilla/Preferences.h"
@@ -205,8 +203,7 @@ NS_IMPL_ADDREF(AudioChannelService)
NS_IMPL_RELEASE(AudioChannelService)
AudioChannelService::AudioChannelService()
: mDisabled(false)
, mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
: mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
, mTelephonyChannel(false)
, mContentOrNormalChannel(false)
, mAnyChannel(false)
@@ -236,10 +233,6 @@ void
AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
AudioChannel aChannel)
{
if (mDisabled) {
return;
}
uint64_t windowID = aAgent->WindowID();
AudioChannelWindow* winData = GetWindowData(windowID);
if (!winData) {
@@ -270,10 +263,6 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
void
AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
{
if (mDisabled) {
return;
}
AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
if (!winData) {
return;
@@ -463,49 +452,9 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
mDisabled = true;
mWindows.Clear();
}
#ifdef MOZ_WIDGET_GONK
// To process the volume control on each audio channel according to
// change of settings
else if (!strcmp(aTopic, "mozsettings-changed")) {
RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
if (!WrappedJSToDictionary(aSubject, setting)) {
return NS_OK;
}
if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) {
return NS_OK;
}
if (!setting.mValue.isNumber()) {
return NS_OK;
}
nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
NS_ENSURE_TRUE(audioManager, NS_OK);
int32_t index = setting.mValue.toNumber();
if (setting.mKey.EqualsLiteral("audio.volume.content")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content, index);
} else if (setting.mKey.EqualsLiteral("audio.volume.notification")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification, index);
} else if (setting.mKey.EqualsLiteral("audio.volume.alarm")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm, index);
} else if (setting.mKey.EqualsLiteral("audio.volume.telephony")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony, index);
} else if (!setting.mKey.EqualsLiteral("audio.volume.bt_sco")) {
// bt_sco is not a valid audio channel so we manipulate it in
// AudioManager.cpp. And the others should not be used.
// We didn't use MOZ_CRASH or MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE here
// because any web content who has permission of mozSettings can set any
// names then it can be easy to crash the B2G.
NS_WARNING("unexpected audio channel for volume control");
}
}
#endif
else if (!strcmp(aTopic, "outer-window-destroyed")) {
Shutdown();
} else if (!strcmp(aTopic, "outer-window-destroyed")) {
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
@@ -544,9 +493,7 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
mSpeakerManager[i]->SetAudioChannelActive(active);
}
#endif
}
else if (!strcmp(aTopic, "ipc:content-shutdown")) {
} else if (!strcmp(aTopic, "ipc:content-shutdown")) {
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
if (!props) {
NS_WARNING("ipc:content-shutdown message without property bag as subject");
+5 -7
View File
@@ -43,11 +43,6 @@ public:
*/
static already_AddRefed<AudioChannelService> GetOrCreate();
/**
* Shutdown the singleton.
*/
static void Shutdown();
static bool IsAudioChannelMutedByDefault();
/**
@@ -144,6 +139,11 @@ private:
AudioChannelService();
~AudioChannelService();
/**
* Shutdown the singleton.
*/
static void Shutdown();
void MaybeSendStatusUpdate();
bool ContentOrNormalChannelIsActive();
@@ -212,8 +212,6 @@ private:
nsTArray<SpeakerManagerService*> mSpeakerManager;
#endif
bool mDisabled;
nsCOMPtr<nsIRunnable> mRunnable;
uint64_t mDefChannelChildID;
+2 -1
View File
@@ -34,7 +34,7 @@ interface nsIAudioChannelAgentCallback : nsISupports
* 1. Changes to the playable status of this channel.
*/
[uuid(e28e1569-2a44-4f71-9cd0-216874b05d57)]
[uuid(ee39a34b-a5c7-4b30-b1ac-cd64ceedef67)]
interface nsIAudioChannelAgent : nsISupports
{
const long AUDIO_AGENT_CHANNEL_NORMAL = 0;
@@ -44,6 +44,7 @@ interface nsIAudioChannelAgent : nsISupports
const long AUDIO_AGENT_CHANNEL_TELEPHONY = 4;
const long AUDIO_AGENT_CHANNEL_RINGER = 5;
const long AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION = 6;
const long AUDIO_AGENT_CHANNEL_SYSTEM = 7;
const long AUDIO_AGENT_CHANNEL_ERROR = 1000;
+2
View File
@@ -181,6 +181,8 @@ cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel)
return CUBEB_STREAM_TYPE_VOICE_CALL;
case dom::AudioChannel::Ringer:
return CUBEB_STREAM_TYPE_RING;
case dom::AudioChannel::System:
return CUBEB_STREAM_TYPE_SYSTEM;
case dom::AudioChannel::Publicnotification:
return CUBEB_STREAM_TYPE_SYSTEM_ENFORCED;
default:
+377 -110
View File
@@ -62,11 +62,12 @@ using namespace mozilla::dom::bluetooth;
#define MOZ_SETTINGS_CHANGE_ID "mozsettings-changed"
#define AUDIO_CHANNEL_PROCESS_CHANGED "audio-channel-process-changed"
#define AUDIO_POLICY_SERVICE_NAME "media.audio_policy"
#define SETTINGS_SERVICE "@mozilla.org/settingsService;1"
static void BinderDeadCallback(status_t aErr);
static void InternalSetAudioRoutes(SwitchState aState);
// Refer AudioService.java from Android
static int sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = {
5, // voice call
15, // system
15, // ring
@@ -89,6 +90,47 @@ static bool sA2dpSwitchDone = true;
namespace mozilla {
namespace dom {
namespace gonk {
static const VolumeData gVolumeData[VOLUME_TOTAL_NUMBER] = {
{"audio.volume.content", VOLUME_MEDIA},
{"audio.volume.notification", VOLUME_NOTIFICATION},
{"audio.volume.alarm", VOLUME_ALARM},
{"audio.volume.telephony", VOLUME_TELEPHONY},
{"audio.volume.bt_sco", VOLUME_BLUETOOTH_SCO}
};
class AudioProfileData final
{
public:
explicit AudioProfileData(AudioOutputProfiles aProfile)
: mProfile(aProfile)
, mActive(false)
{
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
mVolumeTable.AppendElement(0);
}
};
AudioOutputProfiles GetProfile() const
{
return mProfile;
}
void SetActive(bool aActive)
{
mActive = aActive;
}
bool GetActive() const
{
return mActive;
}
nsTArray<uint32_t> mVolumeTable;
private:
const AudioOutputProfiles mProfile;
bool mActive;
};
class RecoverTask : public nsRunnable
{
public:
@@ -98,7 +140,7 @@ public:
NS_ENSURE_TRUE(amService, NS_OK);
AudioManager *am = static_cast<AudioManager *>(amService.get());
int attempt;
uint32_t attempt;
for (attempt = 0; attempt < 50; attempt++) {
if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) {
break;
@@ -110,10 +152,10 @@ public:
MOZ_RELEASE_ASSERT(attempt < 50);
for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) {
for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
sMaxStreamVolumeTbl[loop]);
int32_t index;
uint32_t index;
am->GetStreamVolumeIndex(loop, &index);
am->SetStreamVolumeIndex(loop, index);
}
@@ -147,31 +189,22 @@ public:
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
{
nsCOMPtr<nsIAudioManager> audioManager =
do_GetService(NS_AUDIOMANAGER_CONTRACTID);
NS_ENSURE_TRUE(aResult.isInt32(), NS_OK);
int32_t volIndex = aResult.toInt32();
if (aName.EqualsLiteral("audio.volume.content")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content,
volIndex);
} else if (aName.EqualsLiteral("audio.volume.notification")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification,
volIndex);
} else if (aName.EqualsLiteral("audio.volume.alarm")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm,
volIndex);
} else if (aName.EqualsLiteral("audio.volume.telephony")) {
audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony,
volIndex);
} else if (aName.EqualsLiteral("audio.volume.bt_sco")) {
static_cast<AudioManager *>(audioManager.get())->SetStreamVolumeIndex(
AUDIO_STREAM_BLUETOOTH_SCO, volIndex);
} else {
MOZ_ASSERT_UNREACHABLE("unexpected audio channel for initializing "
"volume control");
nsRefPtr<AudioManager> audioManager = AudioManager::GetInstance();
MOZ_ASSERT(audioManager);
uint32_t volIndex = aResult.toInt32();
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
if (aName.EqualsASCII(gVolumeData[idx].mChannelName)) {
uint32_t category = gVolumeData[idx].mCategory;
nsresult rv = audioManager->ValidateVolumeIndex(category, volIndex);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
audioManager->InitProfilesVolume(gVolumeData[idx].mCategory, volIndex);
return NS_OK;
}
}
NS_WARNING("unexpected event name for initializing volume control");
return NS_OK;
}
@@ -294,17 +327,21 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
AudioSystem::setParameters(0, cmd);
SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
SwitchProfileData(DEVICE_BLUETOOTH, true);
} else {
int32_t force;
GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
if (force == nsIAudioManager::FORCE_BT_SCO)
if (force == nsIAudioManager::FORCE_BT_SCO) {
SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
}
SwitchProfileData(DEVICE_BLUETOOTH, false);
}
} else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && sA2dpSwitchDone) {
MessageLoop::current()->PostDelayedTask(
FROM_HERE, NewRunnableFunction(&ProcessDelayedA2dpRoute, audioState, aAddress), 1000);
sA2dpSwitchDone = false;
SwitchProfileData(DEVICE_BLUETOOTH, false);
} else {
AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
audioState, aAddress.get());
@@ -313,6 +350,7 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
cmd.setTo("A2dpSuspended=false");
AudioSystem::setParameters(0, cmd);
sA2dpSwitchDone = true;
SwitchProfileData(DEVICE_BLUETOOTH, true);
#if ANDROID_VERSION >= 17
if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
@@ -390,24 +428,27 @@ AudioManager::Observe(nsISupports* aSubject,
return NS_OK;
}
// To process the volume control on each audio channel according to
// To process the volume control on each volume categories according to
// change of settings
else if (!strcmp(aTopic, MOZ_SETTINGS_CHANGE_ID)) {
RootedDictionary<dom::SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
if (!WrappedJSToDictionary(aSubject, setting)) {
return NS_OK;
}
if (!setting.mKey.EqualsASCII("audio.volume.bt_sco")) {
if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) {
return NS_OK;
}
if (!setting.mValue.isNumber()) {
return NS_OK;
}
int32_t index = setting.mValue.toNumber();
SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, index);
return NS_OK;
uint32_t volIndex = setting.mValue.toNumber();
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
if (setting.mKey.EqualsASCII(gVolumeData[idx].mChannelName)) {
SetVolumeByCategory(gVolumeData[idx].mCategory, volIndex);
return NS_OK;
}
}
}
NS_WARNING("Unexpected topic in AudioManager");
@@ -442,9 +483,11 @@ public:
if (aEvent.status() == SWITCH_STATE_OFF && sSwitchDone) {
MessageLoop::current()->PostDelayedTask(
FROM_HERE, NewRunnableFunction(&ProcessDelayedAudioRoute, SWITCH_STATE_OFF), 1000);
mAudioManager->SwitchProfileData(DEVICE_HEADSET, false);
sSwitchDone = false;
} else if (aEvent.status() != SWITCH_STATE_OFF) {
InternalSetAudioRoutes(aEvent.status());
mAudioManager->SwitchProfileData(DEVICE_HEADSET, true);
sSwitchDone = true;
}
// Handle the coexistence of a2dp / headset device, latest one wins.
@@ -474,7 +517,7 @@ AudioManager::AudioManager()
InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES));
for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) {
for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
sMaxStreamVolumeTbl[loop]);
mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop];
@@ -482,6 +525,7 @@ AudioManager::AudioManager()
// Force publicnotification to output at maximal volume
SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE,
sMaxStreamVolumeTbl[AUDIO_STREAM_ENFORCED_AUDIBLE]);
CreateAudioProfilesData();
// Get the initial volume index from settings DB during boot up.
nsCOMPtr<nsISettingsService> settingsService =
@@ -492,11 +536,9 @@ AudioManager::AudioManager()
NS_ENSURE_SUCCESS_VOID(rv);
nsCOMPtr<nsISettingsServiceCallback> callback = new AudioChannelVolInitCallback();
NS_ENSURE_TRUE_VOID(callback);
lock->Get("audio.volume.content", callback);
lock->Get("audio.volume.notification", callback);
lock->Get("audio.volume.alarm", callback);
lock->Get("audio.volume.telephony", callback);
lock->Get("audio.volume.bt_sco", callback);
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
lock->Get(gVolumeData[idx].mChannelName, callback);
}
// Gecko only control stream volume not master so set to default value
// directly.
@@ -719,114 +761,207 @@ AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
// sync volume with music after powering on fm radio
if (aFmRadioAudioEnabled) {
int32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
uint32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex);
mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex;
}
return NS_OK;
}
NS_IMETHODIMP
AudioManager::SetAudioChannelVolume(int32_t aChannel, int32_t aIndex) {
nsresult status;
nsresult
AudioManager::ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const
{
uint32_t maxIndex = GetMaxVolumeByCategory(aCategory);
if (aIndex > maxIndex) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
switch (static_cast<AudioChannel>(aChannel)) {
case AudioChannel::Content:
nsresult
AudioManager::SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex)
{
nsresult status;
switch (static_cast<AudioVolumeCategories>(aCategory)) {
case VOLUME_MEDIA:
// sync FMRadio's volume with content channel.
if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) {
status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex);
NS_ENSURE_SUCCESS(status, status);
if (NS_WARN_IF(NS_FAILED(status))) {
return status;
}
}
status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex);
NS_ENSURE_SUCCESS(status, status);
break;
case VOLUME_NOTIFICATION:
status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
if (NS_WARN_IF(NS_FAILED(status))) {
return status;
}
status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
if (NS_WARN_IF(NS_FAILED(status))) {
return status;
}
status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex);
break;
case AudioChannel::Notification:
status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
NS_ENSURE_SUCCESS(status, status);
status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
break;
case AudioChannel::Alarm:
case VOLUME_ALARM:
status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex);
break;
case AudioChannel::Telephony:
case VOLUME_TELEPHONY:
status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex);
case VOLUME_BLUETOOTH_SCO:
status = SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, aIndex);
break;
default:
return NS_ERROR_INVALID_ARG;
}
return status;
}
uint32_t
AudioManager::GetVolumeByCategory(uint32_t aCategory) const
{
switch (static_cast<AudioVolumeCategories>(aCategory)) {
case VOLUME_MEDIA:
return mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
case VOLUME_NOTIFICATION:
MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]);
MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
return mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
case VOLUME_ALARM:
return mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM];
case VOLUME_TELEPHONY:
return mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
case VOLUME_BLUETOOTH_SCO:
return mCurrentStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO];
default:
NS_WARNING("Can't get volume from error volume category.");
return 0;
}
}
uint32_t
AudioManager::GetMaxVolumeByCategory(uint32_t aCategory) const
{
switch (static_cast<AudioVolumeCategories>(aCategory)) {
case VOLUME_MEDIA:
return sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC];
case VOLUME_NOTIFICATION:
MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
sMaxStreamVolumeTbl[AUDIO_STREAM_RING]);
MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
return sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
case VOLUME_ALARM:
return sMaxStreamVolumeTbl[AUDIO_STREAM_ALARM];
case VOLUME_TELEPHONY:
return sMaxStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
case VOLUME_BLUETOOTH_SCO:
return sMaxStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO];
default:
NS_WARNING("Can't get max volume from error volume category.");
return 0;
}
}
NS_IMETHODIMP
AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex)
{
nsresult status;
AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
switch (static_cast<AudioChannel>(aChannel)) {
case AudioChannel::Normal:
case AudioChannel::Content:
status = SetVolumeByCategory(VOLUME_MEDIA, aIndex);
break;
case AudioChannel::Notification:
case AudioChannel::Ringer:
case AudioChannel::Publicnotification:
case AudioChannel::System:
status = SetVolumeByCategory(VOLUME_NOTIFICATION, aIndex);
break;
case AudioChannel::Alarm:
status = SetVolumeByCategory(VOLUME_ALARM, aIndex);
break;
case AudioChannel::Telephony:
status = SetVolumeByCategory(category, aIndex);
break;
default:
return NS_ERROR_INVALID_ARG;
}
return status;
}
NS_IMETHODIMP
AudioManager::GetAudioChannelVolume(int32_t aChannel, int32_t* aIndex) {
AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex)
{
if (!aIndex) {
return NS_ERROR_NULL_POINTER;
}
AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
switch (static_cast<AudioChannel>(aChannel)) {
case AudioChannel::Normal:
case AudioChannel::Content:
MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
*aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
*aIndex = GetVolumeByCategory(VOLUME_MEDIA);
break;
case AudioChannel::Notification:
MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]);
*aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
case AudioChannel::Ringer:
case AudioChannel::Publicnotification:
case AudioChannel::System:
*aIndex = GetVolumeByCategory(VOLUME_NOTIFICATION);
break;
case AudioChannel::Alarm:
*aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM];
*aIndex = GetVolumeByCategory(VOLUME_ALARM);
break;
case AudioChannel::Telephony:
*aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
*aIndex = GetVolumeByCategory(category);
break;
default:
return NS_ERROR_INVALID_ARG;
}
return NS_OK;
}
NS_IMETHODIMP
AudioManager::GetMaxAudioChannelVolume(int32_t aChannel, int32_t* aMaxIndex) {
AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex)
{
if (!aMaxIndex) {
return NS_ERROR_NULL_POINTER;
}
int32_t stream;
AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
switch (static_cast<AudioChannel>(aChannel)) {
case AudioChannel::Normal:
case AudioChannel::Content:
MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
stream = AUDIO_STREAM_MUSIC;
*aMaxIndex = GetMaxVolumeByCategory(VOLUME_MEDIA);
break;
case AudioChannel::Notification:
MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
sMaxStreamVolumeTbl[AUDIO_STREAM_RING]);
stream = AUDIO_STREAM_NOTIFICATION;
case AudioChannel::Ringer:
case AudioChannel::Publicnotification:
case AudioChannel::System:
*aMaxIndex = GetMaxVolumeByCategory(VOLUME_NOTIFICATION);
break;
case AudioChannel::Alarm:
stream = AUDIO_STREAM_ALARM;
*aMaxIndex = GetMaxVolumeByCategory(VOLUME_ALARM);
break;
case AudioChannel::Telephony:
stream = AUDIO_STREAM_VOICE_CALL;
*aMaxIndex = GetMaxVolumeByCategory(category);
break;
default:
return NS_ERROR_INVALID_ARG;
}
*aMaxIndex = sMaxStreamVolumeTbl[stream];
return NS_OK;
return NS_OK;
}
nsresult
AudioManager::SetStreamVolumeIndex(int32_t aStream, int32_t aIndex) {
if (aIndex < 0 || aIndex > sMaxStreamVolumeTbl[aStream]) {
AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex) {
if (aIndex > sMaxStreamVolumeTbl[aStream]) {
return NS_ERROR_INVALID_ARG;
}
mCurrentStreamVolumeTbl[aStream] = aIndex;
status_t status;
#if ANDROID_VERSION < 17
@@ -835,53 +970,50 @@ AudioManager::SetStreamVolumeIndex(int32_t aStream, int32_t aIndex) {
aIndex);
return status ? NS_ERROR_FAILURE : NS_OK;
#else
int device = 0;
if (aStream == AUDIO_STREAM_BLUETOOTH_SCO) {
device = AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
} else if (aStream == AUDIO_STREAM_FM) {
device = AUDIO_DEVICE_OUT_FM;
}
if (device != 0) {
if (aStream == AUDIO_STREAM_FM) {
status = AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
device);
AUDIO_DEVICE_OUT_FM);
return status ? NS_ERROR_FAILURE : NS_OK;
}
status = AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_SPEAKER);
status += AudioSystem::setStreamVolumeIndex(
if (mPresentProfile == DEVICE_PRIMARY) {
status = AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_SPEAKER);
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_EARPIECE);
} else if (mPresentProfile == DEVICE_HEADSET) {
status = AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_WIRED_HEADSET);
status += AudioSystem::setStreamVolumeIndex(
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_EARPIECE);
status += AudioSystem::setStreamVolumeIndex(
} else if (mPresentProfile == DEVICE_BLUETOOTH) {
status = AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
status += AudioSystem::setStreamVolumeIndex(
static_cast<audio_stream_type_t>(aStream),
aIndex,
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
} else {
NS_WARNING("Can't set stream volume on error profile!");
}
return status ? NS_ERROR_FAILURE : NS_OK;
#endif
}
nsresult
AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) {
AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) {
if (!aIndex) {
return NS_ERROR_INVALID_ARG;
}
@@ -894,3 +1026,138 @@ AudioManager::GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex) {
return NS_OK;
}
AudioProfileData*
AudioManager::FindAudioProfileData(AudioOutputProfiles aProfile)
{
uint32_t profilesNum = mAudioProfiles.Length();
MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!");
for (uint32_t idx = 0; idx < profilesNum; ++idx) {
if (mAudioProfiles[idx]->GetProfile() == aProfile) {
return mAudioProfiles[idx];
}
}
NS_WARNING("Can't find audio profile data");
return nullptr;
}
void
AudioManager::SendVolumeChangeNotification(AudioProfileData* aProfileData)
{
MOZ_ASSERT(aProfileData);
nsresult rv;
nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsCOMPtr<nsISettingsServiceLock> lock;
rv = service->CreateLock(nullptr, getter_AddRefs(lock));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Send events to update the Gaia volume
mozilla::AutoSafeJSContext cx;
JS::Rooted<JS::Value> value(cx);
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
value.setInt32(aProfileData->mVolumeTable[gVolumeData[idx].mCategory]);
lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr);
}
}
void
AudioManager::CreateAudioProfilesData()
{
MOZ_ASSERT(mAudioProfiles.IsEmpty(), "mAudioProfiles should be empty!");
for (uint32_t idx = 0; idx < DEVICE_TOTAL_NUMBER; ++idx) {
AudioProfileData* profile = new AudioProfileData(static_cast<AudioOutputProfiles>(idx));
mAudioProfiles.AppendElement(profile);
}
UpdateProfileState(DEVICE_PRIMARY, true);
}
void
AudioManager::InitProfilesVolume(uint32_t aCategory, uint32_t aIndex)
{
uint32_t profilesNum = mAudioProfiles.Length();
MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!");
for (uint32_t idx = 0; idx < profilesNum; ++idx) {
mAudioProfiles[idx]->mVolumeTable[aCategory] = aIndex;
}
SetVolumeByCategory(aCategory, aIndex);
}
void
AudioManager::SwitchProfileData(AudioOutputProfiles aProfile,
bool aActive)
{
MOZ_ASSERT(DEVICE_PRIMARY <= aProfile &&
aProfile < DEVICE_TOTAL_NUMBER, "Error profile type!");
// Save the present profile volume data.
AudioOutputProfiles oldProfile = mPresentProfile;
AudioProfileData* profileData = FindAudioProfileData(oldProfile);
MOZ_ASSERT(profileData);
UpdateVolumeToProfile(profileData);
UpdateProfileState(aProfile, aActive);
AudioOutputProfiles newProfile = mPresentProfile;
if (oldProfile == newProfile) {
return;
}
// Update new profile volume data and send the changing event.
profileData = FindAudioProfileData(newProfile);
MOZ_ASSERT(profileData);
UpdateVolumeFromProfile(profileData);
SendVolumeChangeNotification(profileData);
}
void
AudioManager::UpdateProfileState(AudioOutputProfiles aProfile, bool aActive)
{
MOZ_ASSERT(DEVICE_PRIMARY <= aProfile && aProfile < DEVICE_TOTAL_NUMBER,
"Error profile type!");
if (aProfile == DEVICE_PRIMARY && !aActive) {
NS_WARNING("Can't turn off the primary profile!");
return;
}
mAudioProfiles[aProfile]->SetActive(aActive);
if (aActive) {
mPresentProfile = aProfile;
return;
}
// The primary profile has the lowest priority. We will check whether there
// are other profiles. The bluetooth and headset have the same priotity.
uint32_t profilesNum = mAudioProfiles.Length();
MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!");
for (int32_t idx = profilesNum - 1; idx >= 0; --idx) {
if (mAudioProfiles[idx]->GetActive()) {
mPresentProfile = static_cast<AudioOutputProfiles>(idx);
break;
}
}
}
void
AudioManager::UpdateVolumeToProfile(AudioProfileData* aProfileData)
{
MOZ_ASSERT(aProfileData);
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
uint32_t volume = GetVolumeByCategory(gVolumeData[idx].mCategory);
aProfileData->mVolumeTable[gVolumeData[idx].mCategory] = volume;
}
}
void
AudioManager::UpdateVolumeFromProfile(AudioProfileData* aProfileData)
{
MOZ_ASSERT(aProfileData);
for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
SetVolumeByCategory(gVolumeData[idx].mCategory,
aProfileData->mVolumeTable[gVolumeData[idx].mCategory]);
}
}
+74 -3
View File
@@ -36,8 +36,47 @@ typedef Observer<SwitchEvent> SwitchObserver;
namespace dom {
namespace gonk {
/**
* FxOS can remeber the separate volume settings on difference output profiles.
* (1) Primary : speaker, receiver
* (2) Headset : wired headphone/headset
* (3) Bluetooth : BT SCO/A2DP devices
**/
enum AudioOutputProfiles {
DEVICE_PRIMARY = 0,
DEVICE_HEADSET = 1,
DEVICE_BLUETOOTH = 2,
DEVICE_TOTAL_NUMBER = 3,
};
/**
* We have five sound volume settings from UX spec,
* You can see more informations in Bug1068219.
* (1) Media : music, video, FM ...
* (2) Notification : ringer, notification ...
* (3) Alarm : alarm
* (4) Telephony : GSM call, WebRTC call
* (5) Bluetooth SCO : SCO call
**/
enum AudioVolumeCategories {
VOLUME_MEDIA = 0,
VOLUME_NOTIFICATION = 1,
VOLUME_ALARM = 2,
VOLUME_TELEPHONY = 3,
VOLUME_BLUETOOTH_SCO = 4,
VOLUME_TOTAL_NUMBER = 5,
};
struct VolumeData {
const char* mChannelName;
uint32_t mCategory;
};
class RecoverTask;
class AudioChannelVolInitCallback;
class AudioProfileData;
class AudioManager final : public nsIAudioManager
, public nsIObserver
{
@@ -53,12 +92,18 @@ public:
friend class RecoverTask;
friend class AudioChannelVolInitCallback;
// Open or close the specific profile
void SwitchProfileData(AudioOutputProfiles aProfile, bool aActive);
// Validate whether the volume index is within the range
nsresult ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const;
protected:
int32_t mPhoneState;
int mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT];
uint32_t mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT];
nsresult SetStreamVolumeIndex(int32_t aStream, int32_t aIndex);
nsresult GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex);
nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex);
private:
nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
@@ -68,12 +113,38 @@ private:
// mIsMicMuted is only used for toggling mute call to RIL.
bool mIsMicMuted;
#endif
nsTArray<nsAutoPtr<AudioProfileData>> mAudioProfiles;
AudioOutputProfiles mPresentProfile;
void HandleBluetoothStatusChanged(nsISupports* aSubject,
const char* aTopic,
const nsCString aAddress);
void HandleAudioChannelProcessChanged();
void CreateAudioProfilesData();
// Init the volume setting from the init setting callback
void InitProfilesVolume(uint32_t aCatogory, uint32_t aIndex);
// Update volume data of profiles
void UpdateVolumeToProfile(AudioProfileData* aProfileData);
// Apply the volume data to device
void UpdateVolumeFromProfile(AudioProfileData* aProfileData);
// Send the volume changing event to Gaia
void SendVolumeChangeNotification(AudioProfileData* aProfileData);
// Update the mPresentProfile and profiles active status
void UpdateProfileState(AudioOutputProfiles aProfile, bool aActive);
// Volume control functions
nsresult SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex);
uint32_t GetVolumeByCategory(uint32_t aCategory) const;
uint32_t GetMaxVolumeByCategory(uint32_t aCategory) const;
AudioProfileData* FindAudioProfileData(AudioOutputProfiles aProfile);
AudioManager();
~AudioManager();
};
+3 -3
View File
@@ -239,7 +239,7 @@ typedef enum {
#if ANDROID_VERSION < 17
typedef enum {
/* output devices */
/* output devices */
AUDIO_DEVICE_OUT_EARPIECE = 0x1,
AUDIO_DEVICE_OUT_SPEAKER = 0x2,
AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4,
@@ -299,7 +299,7 @@ typedef enum {
AUDIO_DEVICE_IN_FM_RX = 0x20000000,
AUDIO_DEVICE_IN_FM_RX_A2DP = 0x40000000,
AUDIO_DEVICE_IN_DEFAULT = 0x80000000,
AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION |
AUDIO_DEVICE_IN_AMBIENT |
AUDIO_DEVICE_IN_BUILTIN_MIC |
@@ -550,7 +550,7 @@ typedef uint32_t audio_devices_t;
typedef enum {
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
AUDIO_POLICY_DEVICE_STATE_CNT,
AUDIO_POLICY_DEVICE_STATE_MAX = AUDIO_POLICY_DEVICE_STATE_CNT - 1,
} audio_policy_dev_state_t;
+9 -5
View File
@@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, builtinclass, uuid(60da41b4-cdc2-11e2-8a91-10bf48d64bd4)]
[scriptable, builtinclass, uuid(df31c280-1ef1-11e5-867f-0800200c9a66)]
interface nsIAudioManager : nsISupports
{
/**
@@ -52,8 +52,12 @@ interface nsIAudioManager : nsISupports
void setForceForUse(in long usage, in long force);
long getForceForUse(in long usage);
/* The range of volume index is from 0 to N. Ex: 0 ~ 15 */
void setAudioChannelVolume(in long channel, in long index);
long getAudioChannelVolume(in long channel);
long getMaxAudioChannelVolume(in long channel);
/**
* These functions would be used when we enable the new volume control API
* (mozAudioChannelManager). The range of volume index is from 0 to N.
* More details on : https://gist.github.com/evanxd/41d8e2d91c5201a42bfa
*/
void setAudioChannelVolume(in unsigned long channel, in unsigned long index);
unsigned long getAudioChannelVolume(in unsigned long channel);
unsigned long getMaxAudioChannelVolume(in unsigned long channel);
};
+1
View File
@@ -46,4 +46,5 @@ enum AudioChannel {
"telephony",
"ringer",
"publicnotification",
"system"
};
-2
View File
@@ -427,8 +427,6 @@ nsLayoutStatics::Shutdown()
nsHyphenationManager::Shutdown();
nsDOMMutationObserver::Shutdown();
AudioChannelService::Shutdown();
DataStoreService::Shutdown();
ContentParent::ShutDown();