Bug 1218629 - Save audio volume for each device to setting db r=alwu
authorSotaro Ikeda <sotaro.ikeda.g@gmail.com>
Wed, 23 Dec 2015 23:31:34 -0800
changeset 277618 0eed3c26935ae436ddc8ed094e8cff4c5a18b3b4
parent 277617 6c98d100b4ca9f4b667955441cb10b19f8c8381f
child 277619 9c4f5c1196b8b516a772f96463979b5b423eb913
push id16783
push userryanvm@gmail.com
push dateSat, 26 Dec 2015 01:50:11 +0000
treeherderfx-team@4a559a618d67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersalwu
bugs1218629
milestone46.0a1
Bug 1218629 - Save audio volume for each device to setting db r=alwu
dom/system/gonk/AudioManager.cpp
dom/system/gonk/AudioManager.h
dom/system/gonk/android_audio/AudioSystem.h
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -121,22 +121,32 @@ static const uint32_t sChannelStreamTbl[
   AUDIO_STREAM_NOTIFICATION,    // AudioChannel::Notification
   AUDIO_STREAM_ALARM,           // AudioChannel::Alarm
   AUDIO_STREAM_VOICE_CALL,      // AudioChannel::Telephony
   AUDIO_STREAM_RING,            // AudioChannel::Ringer
   AUDIO_STREAM_ENFORCED_AUDIBLE,// AudioChannel::Publicnotification
   AUDIO_STREAM_SYSTEM,          // AudioChannel::System
 };
 
-// Mappings AudioOutputProfiles to strings.
-static const nsAttrValue::EnumTable kAudioOutputProfilesTable[] = {
-  { "primary",   DEVICE_PRIMARY },
-  { "headset",   DEVICE_HEADSET },
-  { "bluetooth", DEVICE_BLUETOOTH },
-  { nullptr }
+
+struct AudioDeviceInfo {
+  /** The string the value maps to */
+  const char* tag;
+  /** The enum value that maps to this string */
+  uint32_t value;
+};
+
+// Mappings audio output devices to strings.
+static const AudioDeviceInfo kAudioDeviceInfos[] = {
+  { "earpiece",        AUDIO_DEVICE_OUT_EARPIECE },
+  { "speaker",         AUDIO_DEVICE_OUT_SPEAKER },
+  { "wired_headset",   AUDIO_DEVICE_OUT_WIRED_HEADSET },
+  { "wired_headphone", AUDIO_DEVICE_OUT_WIRED_HEADPHONE },
+  { "bt_scoheadset",   AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET },
+  { "bt_a2dp",         AUDIO_DEVICE_OUT_BLUETOOTH_A2DP },
 };
 
 static const int kBtSampleRate = 8000;
 
 typedef MozPromise<bool, const char*, true> VolumeInitPromise;
 
 namespace mozilla {
 namespace dom {
@@ -256,17 +266,17 @@ AudioManager::HandleAudioFlingerDied()
     mStreamStates[streamType]->RestoreVolumeIndexToAllDevices();
   }
 
   // Indicate the end of reconfiguration phase to audio HAL
   AudioSystem::setParameters(0, String8("restarting=true"));
 
   // Enable volume change notification
   mIsVolumeInited = true;
-  mAudioOutProfileUpdated = 0;
+  mAudioOutDevicesUpdated = 0;
   MaybeUpdateVolumeSettingToDatabase(true);
 }
 
 class VolumeInitCallback final : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
@@ -283,29 +293,30 @@ public:
 
   NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   {
     RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
     MOZ_ASSERT(audioManager);
     for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
       NS_ConvertASCIItoUTF16 volumeType(gVolumeData[idx].mChannelName);
       if (StringBeginsWith(aName, volumeType)) {
-        AudioOutputProfiles profile = GetProfileFromSettingName(aName);
-        MOZ_ASSERT(profile != DEVICE_ERROR);
-        int32_t stream = gVolumeData[idx].mStreamType;
-        uint32_t volIndex = aResult.isInt32() ?
-                  aResult.toInt32() : sDefaultStreamVolumeTbl[stream];
-        nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          mPromiseHolder.Reject("Error : invalid volume index.", __func__);
-          return rv;
+        uint32_t device = GetDeviceFromSettingName(aName);
+        MOZ_ASSERT(device != AUDIO_DEVICE_NONE);
+        if (aResult.isInt32()) {
+          int32_t stream = gVolumeData[idx].mStreamType;
+          uint32_t volIndex = aResult.toInt32();
+          nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            mPromiseHolder.Reject("Error : invalid volume index.", __func__);
+            return rv;
+          }
+          audioManager->SetStreamVolumeForDevice(stream, volIndex, device);
         }
 
-        audioManager->InitVolumeForProfile(profile, stream, volIndex);
-        if (++mInitCounter == DEVICE_TOTAL_NUMBER * MOZ_ARRAY_LENGTH(gVolumeData)) {
+        if (++mInitCounter == MOZ_ARRAY_LENGTH(kAudioDeviceInfos) * MOZ_ARRAY_LENGTH(gVolumeData)) {
           mPromiseHolder.Resolve(true, __func__);
         }
         return NS_OK;
       }
     }
     mPromiseHolder.Reject("Error : unexpected audio init event.", __func__);
     return NS_OK;
   }
@@ -314,25 +325,25 @@ public:
   {
     mPromiseHolder.Reject(NS_ConvertUTF16toUTF8(aName).get(), __func__);
     return NS_OK;
   }
 
 protected:
   ~VolumeInitCallback() {}
 
-  AudioOutputProfiles GetProfileFromSettingName(const nsAString& aName) const
+  uint32_t GetDeviceFromSettingName(const nsAString& aName) const
   {
-    for (uint32_t idx = 0; kAudioOutputProfilesTable[idx].tag; ++idx) {
-      NS_ConvertASCIItoUTF16 profile(kAudioOutputProfilesTable[idx].tag);
-      if (StringEndsWith(aName, profile)) {
-        return static_cast<AudioOutputProfiles>(kAudioOutputProfilesTable[idx].value);
+    for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) {
+      NS_ConvertASCIItoUTF16 device(kAudioDeviceInfos[idx].tag);
+      if (StringEndsWith(aName, device)) {
+        return kAudioDeviceInfos[idx].value;
       }
     }
-    return DEVICE_ERROR;
+    return AUDIO_DEVICE_NONE;
   }
 
   RefPtr<VolumeInitPromise> mPromise;
   MozPromiseHolder<VolumeInitPromise> mPromiseHolder;
   uint32_t mInitCounter;
 };
 
 NS_IMPL_ISUPPORTS(VolumeInitCallback, nsISettingsServiceCallback)
@@ -359,19 +370,20 @@ bool
 AudioManager::IsFmOutConnected()
 {
   return mConnectedDevices.Get(AUDIO_DEVICE_OUT_FM, nullptr);
 }
 
 NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver)
 
 void
-AudioManager::AudioOutProfileUpdated(AudioOutputProfiles aProfile)
+AudioManager::AudioOutDeviceUpdated(uint32_t aDevice)
 {
-  mAudioOutProfileUpdated |= (1 << aProfile);
+  MOZ_ASSERT(audio_is_output_device(aDevice));
+  mAudioOutDevicesUpdated |= aDevice;
 }
 
 void
 AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState)
 {
   bool headphoneConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
                                                   nullptr);
   bool headsetConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADSET,
@@ -667,41 +679,50 @@ AudioManager::HandleHeadphoneSwitchEvent
     SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
   }
 #endif
 }
 
 AudioManager::AudioManager()
   : mPhoneState(PHONE_STATE_CURRENT)
   , mIsVolumeInited(false)
-  , mAudioOutProfileUpdated(0)
+  , mAudioOutDevicesUpdated(0)
   , mSwitchDone(true)
 #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
   , mBluetoothA2dpEnabled(false)
 #endif
 #ifdef MOZ_B2G_BT
   , mA2dpSwitchDone(true)
 #endif
   , mObserver(new HeadphoneSwitchObserver())
 #ifdef MOZ_B2G_RIL
   , mMuteCallToRIL(false)
 #endif
 {
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx) {
+    mAudioDeviceTableIdMaps.Put(kAudioDeviceInfos[idx].value, idx);
+  }
+
   AudioSystem::setErrorCallback(BinderDeadCallback);
 #if ANDROID_VERSION >= 21
   android::sp<GonkAudioPortCallback> callback = new GonkAudioPortCallback();
   AudioSystem::setAudioPortCallback(callback);
 #endif
 
   // Create VolumeStreamStates
   for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
     VolumeStreamState* streamState =
       new VolumeStreamState(*this, static_cast<audio_stream_type_t>(loop));
     mStreamStates.AppendElement(streamState);
   }
+  // Initialize stream volumes with default values
+  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
+      uint32_t volIndex = sDefaultStreamVolumeTbl[streamType];
+      SetStreamVolumeForDevice(streamType, volIndex, AUDIO_DEVICE_OUT_DEFAULT);
+  }
   UpdateCachedActiveDevicesForStreams();
 
   RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
   // Initialize headhone/heaset status
   UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
   NotifyHeadphonesStatus(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
 
   // Get the initial volume index from settings DB during boot up.
@@ -973,41 +994,27 @@ AudioManager::ValidateVolumeIndex(int32_
   uint32_t maxIndex = mStreamStates[aStream]->GetMaxIndex();
   if (aIndex > maxIndex) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
-AudioManager::SetStreamVolumeForProfile(AudioOutputProfiles aProfile,
-                                        int32_t aStream,
-                                        uint32_t aIndex)
+AudioManager::SetStreamVolumeForDevice(int32_t aStream,
+                                       uint32_t aIndex,
+                                       uint32_t aDevice)
 {
   if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 
   int32_t streamAlias = sStreamVolumeAliasTbl[aStream];
-  VolumeStreamState* state = mStreamStates[streamAlias].get();
-  // Rescaling of index is not necessary.
-  switch (aProfile) {
-    case DEVICE_PRIMARY:
-      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
-      break;
-    case DEVICE_HEADSET:
-      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET);
-      break;
-    case DEVICE_BLUETOOTH:
-      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
-      break;
-    default:
-      break;
-  }
-  return NS_OK;
+  VolumeStreamState* streamState = mStreamStates[streamAlias].get();
+  return streamState->SetVolumeIndexToAliasStreams(aIndex, aDevice);
 }
 
 nsresult
 AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex)
 {
   if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -1051,27 +1058,25 @@ AudioManager::GetStreamVolumeIndex(int32
     return NS_ERROR_INVALID_ARG;
   }
 
   *aIndex = mStreamStates[aStream]->GetVolumeIndex();
   return NS_OK;
 }
 
 nsAutoCString
-AudioManager::AppendProfileToVolumeSetting(const char* aName, AudioOutputProfiles aProfile)
+AudioManager::AppendDeviceToVolumeSetting(const char* aName, uint32_t aDevice)
 {
   nsAutoCString topic;
   topic.Assign(aName);
-  for (uint32_t idx = 0; kAudioOutputProfilesTable[idx].tag; ++idx) {
-    if (kAudioOutputProfilesTable[idx].value == aProfile) {
-      topic.Append(".");
-      topic.Append(kAudioOutputProfilesTable[idx].tag);
-      break;
-    }
-  }
+  topic.Append(".");
+  uint32_t index = 0;
+  DebugOnly<bool> exist = mAudioDeviceTableIdMaps.Get(aDevice, &index);
+  MOZ_ASSERT(exist);
+  topic.Append(kAudioDeviceInfos[index].tag);
   return topic;
 }
 
 void
 AudioManager::InitVolumeFromDatabase()
 {
   nsresult rv;
   nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
@@ -1083,47 +1088,39 @@ AudioManager::InitVolumeFromDatabase()
   rv = service->CreateLock(nullptr, getter_AddRefs(lock));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   RefPtr<VolumeInitCallback> callback = new VolumeInitCallback();
   MOZ_ASSERT(callback);
   callback->GetPromise()->Then(AbstractThread::MainThread(), __func__, this,
-                               &AudioManager::InitProfileVolumeSucceeded,
-                               &AudioManager::InitProfileVolumeFailed);
+                               &AudioManager::InitDeviceVolumeSucceeded,
+                               &AudioManager::InitDeviceVolumeFailed);
 
   for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
-    for (uint32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) {
-      lock->Get(AppendProfileToVolumeSetting(gVolumeData[idx].mChannelName,
-                  static_cast<AudioOutputProfiles>(profile)).get(), callback);
+    for (uint32_t idx2 = 0; idx2 < MOZ_ARRAY_LENGTH(kAudioDeviceInfos); ++idx2) {
+      lock->Get(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName,
+                                            kAudioDeviceInfos[idx2].value).get(),
+                callback);
     }
   }
 }
 
 void
-AudioManager::InitProfileVolumeSucceeded()
+AudioManager::InitDeviceVolumeSucceeded()
 {
   mIsVolumeInited = true;
   MaybeUpdateVolumeSettingToDatabase(true);
 }
 
 void
-AudioManager::InitProfileVolumeFailed(const char* aError)
+AudioManager::InitDeviceVolumeFailed(const char* aError)
 {
-  // Initialize stream volumes with default values
-  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
-    for (int32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) {
-      int32_t stream = gVolumeData[idx].mStreamType;
-      uint32_t volIndex = sDefaultStreamVolumeTbl[stream];
-      InitVolumeForProfile(static_cast<AudioOutputProfiles>(profile),
-                           stream,
-                           volIndex);
-    }
-  }
+  // Default volume of AUDIO_DEVICE_OUT_DEFAULT is already set.
   mIsVolumeInited = true;
   MaybeUpdateVolumeSettingToDatabase(true);
   NS_WARNING(aError);
 }
 
 void
 AudioManager::MaybeUpdateVolumeSettingToDatabase(bool aForce)
 {
@@ -1148,75 +1145,54 @@ AudioManager::MaybeUpdateVolumeSettingTo
     }
     // Get volume index of active device.
     volume = streamState->GetVolumeIndex();
     value.setInt32(volume);
     lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr);
   }
 
   // For reducing the code dependency, Gaia doesn't need to know the
-  // profile volume, it only need to care about different volume categories.
+  // device volume, it only need to care about different volume categories.
   // However, we need to send the setting volume to the permanent database,
   // so that we can store the volume setting even if the phone reboots.
 
   for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
     int32_t  streamType = gVolumeData[idx].mStreamType;
     VolumeStreamState* streamState = mStreamStates[streamType].get();
 
     if(!streamState->IsVolumeIndexesChanged()) {
         continue;
     }
 
-    if (mAudioOutProfileUpdated & (1 << DEVICE_PRIMARY)) {
-      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_SPEAKER);
-      value.setInt32(volume);
-      lock->Set(AppendProfileToVolumeSetting(
-                  gVolumeData[idx].mChannelName,
-                  DEVICE_PRIMARY).get(),
-                  value, nullptr, nullptr);
-    }
-    if (mAudioOutProfileUpdated & (1 << DEVICE_HEADSET)) {
-      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_WIRED_HEADSET);
+    uint32_t remainingDevices = mAudioOutDevicesUpdated;
+    for (uint32_t i = 0; remainingDevices != 0; i++) {
+      uint32_t device = (1 << i);
+      if ((device & remainingDevices) == 0) {
+        continue;
+      }
+      remainingDevices &= ~device;
+      if (!mAudioDeviceTableIdMaps.Get(device, nullptr)) {
+        continue;
+      }
+      volume = streamState->GetVolumeIndex(device);
       value.setInt32(volume);
-      lock->Set(AppendProfileToVolumeSetting(
-                  gVolumeData[idx].mChannelName,
-                  DEVICE_HEADSET).get(),
-                  value, nullptr, nullptr);
-    }
-    if (mAudioOutProfileUpdated & (1 << DEVICE_BLUETOOTH)) {
-      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
-      value.setInt32(volume);
-      lock->Set(AppendProfileToVolumeSetting(
-                  gVolumeData[idx].mChannelName,
-                  DEVICE_BLUETOOTH).get(),
-                  value, nullptr, nullptr);
+      lock->Set(AppendDeviceToVolumeSetting(gVolumeData[idx].mChannelName,
+                                            device).get(),
+                value, nullptr, nullptr);
     }
   }
 
   // Clear changed flags
   for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
     int32_t  streamType = gVolumeData[idx].mStreamType;
     mStreamStates[streamType]->ClearDevicesChanged();
     mStreamStates[streamType]->ClearVolumeIndexesChanged();
   }
-  // Clear mAudioOutProfileUpdated
-  mAudioOutProfileUpdated = 0;
-}
-
-void
-AudioManager::InitVolumeForProfile(AudioOutputProfiles aProfile,
-                                   int32_t aStreamType,
-                                   uint32_t aIndex)
-{
-  // Set volume to streams of aStreamType and devices of Profile.
-  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
-    if (aStreamType == sStreamVolumeAliasTbl[streamType]) {
-      SetStreamVolumeForProfile(aProfile, streamType, aIndex);
-    }
-  }
+  // Clear mAudioOutDevicesUpdated
+  mAudioOutDevicesUpdated = 0;
 }
 
 void
 AudioManager::UpdateCachedActiveDevicesForStreams()
 {
   // This function updates cached active devices for streams.
   // It is used for optimization of GetDevicesForStream() since L.
   // AudioManager could know when active devices
@@ -1245,17 +1221,19 @@ AudioManager::GetDevicesForStream(int32_
 #endif
 
 #if ANDROID_VERSION >= 17
   audio_devices_t devices =
     AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(aStream));
 
   return static_cast<uint32_t>(devices);
 #else
-  return AUDIO_DEVICE_OUT_DEFAULT;
+  // Per audio out device volume is not supported.
+  // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB.
+  return AUDIO_DEVICE_OUT_SPEAKER;
 #endif
 }
 
 uint32_t
 AudioManager::GetDeviceForStream(int32_t aStream)
 {
   uint32_t devices =
     GetDevicesForStream(static_cast<audio_stream_type_t>(aStream));
@@ -1378,29 +1356,29 @@ AudioManager::VolumeStreamState::SetVolu
   bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex);
   if (exist && aIndex == oldVolumeIndex) {
     // No update
     return NS_OK;
   }
 
   // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active
   // devices for stream.
-  nsresult rv = SetVolumeIndexToAliasDevices(aIndex, device);
+  nsresult rv = SetVolumeIndex(aIndex, device);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Workaround to make audio volume control consisitent.
   // Active devices of AUDIO_STREAM_NOTIFICATION are affected by
   // AUDIO_STREAM_MUSIC's activity. It makes audio volume control inconsistent.
   // See Bug 1196724
   if (device != AUDIO_DEVICE_OUT_SPEAKER &&
       mStreamType == AUDIO_STREAM_NOTIFICATION) {
       // Rescaling of index is not necessary.
-      rv = SetVolumeIndexToAliasDevices(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
   }
 
   return NS_OK;
 }
 
@@ -1409,115 +1387,62 @@ AudioManager::VolumeStreamState::SetVolu
                                                               uint32_t aDevice)
 {
   uint32_t oldVolumeIndex = 0;
   bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex);
   if (exist && aIndex == oldVolumeIndex) {
     // No update
     return NS_OK;
   }
-  nsresult rv = SetVolumeIndexToAliasDevices(aIndex, aDevice);
+
+  nsresult rv = SetVolumeIndex(aIndex, aDevice);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
     if ((streamType != mStreamType) &&
          sStreamVolumeAliasTbl[streamType] == mStreamType) {
       // Rescaling of index is not necessary.
       rv = mManager.mStreamStates[streamType]->
         SetVolumeIndexToAliasStreams(aIndex, aDevice);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
-  return NS_OK;
-}
 
-nsresult
-AudioManager::VolumeStreamState::SetVolumeIndexToAliasDevices(uint32_t aIndex,
-                                                              uint32_t aDevice)
-{
-#if ANDROID_VERSION >= 17
-  nsresult rv = NS_ERROR_FAILURE;
-  switch (aDevice) {
-    case AUDIO_DEVICE_OUT_EARPIECE:
-    case AUDIO_DEVICE_OUT_SPEAKER:
-      // Apply volume index of DEVICE_PRIMARY devices
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      mManager.AudioOutProfileUpdated(DEVICE_PRIMARY);
-      break;
-    case AUDIO_DEVICE_OUT_WIRED_HEADSET:
-    case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
-      // Apply volume index of DEVICE_HEADSET devices
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      mManager.AudioOutProfileUpdated(DEVICE_HEADSET);
-      break;
-    case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
-    case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
-      // Apply volume index of DEVICE_BLUETOOTH devices
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      mManager.AudioOutProfileUpdated(DEVICE_BLUETOOTH);
-      break;
-    default:
-      rv = SetVolumeIndex(aIndex, aDevice);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      break;
-  }
-#else
-  SetVolumeIndex(aIndex, aDevice);
-#endif
   return NS_OK;
 }
 
 nsresult
 AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex,
                                                 uint32_t aDevice,
                                                 bool aUpdateCache)
 {
   status_t rv;
 #if ANDROID_VERSION >= 17
   if (aUpdateCache) {
     mVolumeIndexes.Put(aDevice, aIndex);
     mIsVolumeIndexesChanged = true;
+    mManager.AudioOutDeviceUpdated(aDevice);
   }
 
   rv = AudioSystem::setStreamVolumeIndex(
          static_cast<audio_stream_type_t>(mStreamType),
          aIndex,
          aDevice);
   return rv ? NS_ERROR_FAILURE : NS_OK;
 #else
   if (aUpdateCache) {
-    mVolumeIndexes.Put(AUDIO_DEVICE_OUT_DEFAULT, aIndex);
+    // Per audio out device volume is not supported.
+    // Use AUDIO_DEVICE_OUT_SPEAKER just to store audio volume to DB.
+    mVolumeIndexes.Put(AUDIO_DEVICE_OUT_SPEAKER, aIndex);
     mIsVolumeIndexesChanged = true;
+    mManager.AudioOutDeviceUpdated(AUDIO_DEVICE_OUT_SPEAKER);
   }
   rv = AudioSystem::setStreamVolumeIndex(
          static_cast<audio_stream_type_t>(mStreamType),
          aIndex);
   return rv ? NS_ERROR_FAILURE : NS_OK;
 #endif
 }
 
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -36,30 +36,16 @@ namespace mozilla {
 namespace hal {
 class SwitchEvent;
 typedef Observer<SwitchEvent> SwitchObserver;
 } // namespace hal
 
 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_ERROR        = -1,
-  DEVICE_PRIMARY      = 0,
-  DEVICE_HEADSET      = 1,
-  DEVICE_BLUETOOTH    = 2,
-  DEVICE_TOTAL_NUMBER = 3,
-};
-
 class VolumeInitCallback;
 
 class AudioManager final : public nsIAudioManager
                          , public nsIObserver
 {
 public:
   static already_AddRefed<AudioManager> GetInstance();
 
@@ -94,22 +80,18 @@ public:
     uint32_t GetMaxIndex();
     uint32_t GetDefaultIndex();
     uint32_t GetVolumeIndex();
     uint32_t GetVolumeIndex(uint32_t aDevice);
     void ClearCurrentVolumeUpdated();
     // Set volume index to all active devices.
     // Active devices are chosen by android AudioPolicyManager.
     nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex);
-    // Set volume index to all alias streams. Alias streams have same volume.
-    // It is used to update volume based on audio output profile data.
+    // Set volume index to all alias streams for device. Alias streams have same volume.
     nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice);
-    // Set volume index to all alias devices in audio output profile.
-    // Alias devices have same volume.
-    nsresult SetVolumeIndexToAliasDevices(uint32_t aIndex, uint32_t aDevice);
     nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true);
     // Restore volume index to all devices. Called when AudioFlinger is restarted.
     void RestoreVolumeIndexToAllDevices();
   private:
     AudioManager& mManager;
     const int32_t mStreamType;
     uint32_t mLastDevices;
     bool mIsDevicesChanged;
@@ -117,40 +99,43 @@ public:
     nsDataHashtable<nsUint32HashKey, uint32_t> mVolumeIndexes;
   };
 
 protected:
   int32_t mPhoneState;
 
   bool mIsVolumeInited;
 
-  // A bitwise variable for volume update of audio output profiles
-  uint32_t mAudioOutProfileUpdated;
+  // A bitwise variable for volume update of audio output devices,
+  // clear it after store the value into database.
+  uint32_t mAudioOutDevicesUpdated;
 
   // Connected devices that are controlled by setDeviceConnectionState()
   nsDataHashtable<nsUint32HashKey, nsCString> mConnectedDevices;
 
+  nsDataHashtable<nsUint32HashKey, uint32_t> mAudioDeviceTableIdMaps;
+
   bool mSwitchDone;
 
 #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
   bool mBluetoothA2dpEnabled;
 #endif
 #ifdef MOZ_B2G_BT
   bool mA2dpSwitchDone;
 #endif
   nsTArray<UniquePtr<VolumeStreamState> > mStreamStates;
   uint32_t mLastChannelVolume[AUDIO_STREAM_CNT];
 
   bool IsFmOutConnected();
 
-  nsresult SetStreamVolumeForProfile(AudioOutputProfiles aProfile,
-                                     int32_t aStream,
-                                     uint32_t aIndex);
+  nsresult SetStreamVolumeForDevice(int32_t aStream,
+                                    uint32_t aIndex,
+                                    uint32_t aDevice);
   nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
-  nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex);
+  nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t* aIndex);
 
   void UpdateCachedActiveDevicesForStreams();
   uint32_t GetDevicesForStream(int32_t aStream, bool aFromCache = true);
   uint32_t GetDeviceForStream(int32_t aStream);
   // Choose one device as representative of active devices.
   static uint32_t SelectDeviceFromDevices(uint32_t aOutDevices);
 
 private:
@@ -161,34 +146,29 @@ private:
   bool                                    mIsMicMuted;
 #endif
 
   void HandleBluetoothStatusChanged(nsISupports* aSubject,
                                     const char* aTopic,
                                     const nsCString aAddress);
   void HandleAudioChannelProcessChanged();
 
-  // Initialize volume index for audio output profile
-  void InitVolumeForProfile(AudioOutputProfiles aProfile,
-                            int32_t aStreamType,
-                            uint32_t aIndex);
-
-  // Append the audio output profile to the volume setting string.
-  nsAutoCString AppendProfileToVolumeSetting(const char* aName,
-                                             AudioOutputProfiles aProfile);
+  // Append the audio output device to the volume setting string.
+  nsAutoCString AppendDeviceToVolumeSetting(const char* aName,
+                                            uint32_t aDevice);
 
   // We store the volume setting in the database, these are related functions.
   void InitVolumeFromDatabase();
   void MaybeUpdateVolumeSettingToDatabase(bool aForce = false);
 
   // Promise functions.
-  void InitProfileVolumeSucceeded();
-  void InitProfileVolumeFailed(const char* aError);
+  void InitDeviceVolumeSucceeded();
+  void InitDeviceVolumeFailed(const char* aError);
 
-  void AudioOutProfileUpdated(AudioOutputProfiles aProfile);
+  void AudioOutDeviceUpdated(uint32_t aDevice);
 
   void UpdateHeadsetConnectionState(hal::SwitchState aState);
   void UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName);
   void SetAllDeviceConnectionStates();
 
   AudioManager();
   ~AudioManager();
 
--- a/dom/system/gonk/android_audio/AudioSystem.h
+++ b/dom/system/gonk/android_audio/AudioSystem.h
@@ -236,16 +236,17 @@ typedef enum {
     AUDIO_MODE_CNT,
     AUDIO_MODE_MAX              = AUDIO_MODE_CNT - 1,
 } audio_mode_t;
 #endif
 #endif
 
 #if ANDROID_VERSION < 17
 typedef enum {
+    AUDIO_DEVICE_NONE                          = 0x0,
     /* output devices */
     AUDIO_DEVICE_OUT_EARPIECE                  = 0x1,
     AUDIO_DEVICE_OUT_SPEAKER                   = 0x2,
     AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4,
     AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8,
     AUDIO_DEVICE_OUT_BLUETOOTH_SCO             = 0x10,
     AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET     = 0x20,
     AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT      = 0x40,