Bug 832665 - Switching back and forth between the music app and FM radio (using home key) causes some unexpected behavior., r=mchen, a=blocking-b2g
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 25 Jan 2013 16:12:17 +0100
changeset 118342 9ba470b353d2
parent 118341 c99fb2843bdc
child 118343 3e673c17e680
push id365
push useramarchesini@mozilla.com
push dateFri, 25 Jan 2013 15:53:27 +0000
reviewersmchen, blocking-b2g
bugs832665
milestone18.0
Bug 832665 - Switching back and forth between the music app and FM radio (using home key) causes some unexpected behavior., r=mchen, a=blocking-b2g
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/tests/TestAudioChannelService.cpp
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -62,16 +62,17 @@ AudioChannelService::Shutdown()
     gAudioChannelService = nullptr;
   }
 }
 
 NS_IMPL_ISUPPORTS0(AudioChannelService)
 
 AudioChannelService::AudioChannelService()
 : mCurrentHigherChannel(AUDIO_CHANNEL_LAST)
+, mActiveContentChildIDsFrozen(false)
 {
   // Creation of the hash table.
   mAgents.Init();
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
@@ -164,28 +165,48 @@ AudioChannelService::GetMutedInternal(Au
   AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 
+  // If the audio content channel is visible, let's remember this ChildID.
+  if (newType == AUDIO_CHANNEL_INT_CONTENT &&
+      oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+      !mActiveContentChildIDs.Contains(aChildID)) {
+
+    if (mActiveContentChildIDsFrozen) {
+      mActiveContentChildIDsFrozen = false;
+      mActiveContentChildIDs.Clear();
+    }
+
+    mActiveContentChildIDs.AppendElement(aChildID);
+  }
+
+  // If nothing is visible, the list has to been frozen.
+  else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+           oldType == AUDIO_CHANNEL_INT_CONTENT &&
+           !mActiveContentChildIDsFrozen &&
+           mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
+    mActiveContentChildIDsFrozen = true;
+  }
+
   // Let play any visible audio channel.
   if (!aElementHidden) {
     return false;
   }
 
   bool muted = false;
 
   // We are not visible, maybe we have to mute.
   if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN ||
       (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-       (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
-        HasMoreThanOneContentChannelHidden()))) {
+       !mActiveContentChildIDs.Contains(aChildID))) {
     muted = true;
   }
 
   if (!muted) {
     MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
     muted = ChannelsActiveWithHigherPriorityThan(newType);
   }
 
@@ -194,35 +215,16 @@ AudioChannelService::GetMutedInternal(Au
 
 bool
 AudioChannelService::ContentChannelIsActive()
 {
   return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() ||
          !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty();
 }
 
-bool
-AudioChannelService::HasMoreThanOneContentChannelHidden()
-{
-  uint32_t childId = CONTENT_PARENT_UNKNOWN_CHILD_ID;
-  bool empty = true;
-  for (uint32_t i = 0;
-       i < mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Length();
-       ++i) {
-    if (empty) {
-      childId = mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN][i];
-      empty = false;
-    } else if (childId != mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN][i]) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 void
 AudioChannelService::SendAudioChannelChangedNotification()
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   // Calculating the most important active channel.
@@ -274,18 +276,17 @@ AudioChannelService::SendAudioChannelCha
     higher = AUDIO_CHANNEL_ALARM;
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
     higher = AUDIO_CHANNEL_NOTIFICATION;
   }
 
   // Content channels play in background if just one is active.
-  else if ((!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() &&
-            !HasMoreThanOneContentChannelHidden())) {
+  else if (!mActiveContentChildIDs.IsEmpty()) {
     higher = AUDIO_CHANNEL_CONTENT;
   }
 
   if (higher != mCurrentHigherChannel) {
     mCurrentHigherChannel = higher;
 
     nsString channelName;
     if (mCurrentHigherChannel != AUDIO_CHANNEL_LAST) {
@@ -386,16 +387,20 @@ AudioChannelService::Observe(nsISupports
   if (NS_SUCCEEDED(rv)) {
     for (int32_t type = AUDIO_CHANNEL_INT_NORMAL;
          type < AUDIO_CHANNEL_INT_LAST;
          ++type) {
       int32_t index;
       while ((index = mChannelCounters[type].IndexOf(childID)) != -1) {
         mChannelCounters[type].RemoveElementAt(index);
       }
+
+      if ((index = mActiveContentChildIDs.IndexOf(childID)) != -1) {
+        mActiveContentChildIDs.RemoveElementAt(index);
+      }
     }
 
     // We don't have to remove the agents from the mAgents hashtable because if
     // that table contains only agents running on the same process.
 
     SendAudioChannelChangedNotification();
     Notify();
   } else {
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -93,18 +93,16 @@ protected:
     AUDIO_CHANNEL_INT_RINGER_HIDDEN,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION,
     AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN,
     AUDIO_CHANNEL_INT_LAST
   };
 
   bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
 
-  bool HasMoreThanOneContentChannelHidden();
-
   const char* ChannelName(AudioChannelType aType);
 
   AudioChannelInternalType GetInternalType(AudioChannelType aType,
                                            bool aElementHidden);
 
   class AudioChannelAgentData {
   public:
     AudioChannelAgentData(AudioChannelType aType,
@@ -125,16 +123,19 @@ protected:
                    AudioChannelAgentData* aData, void *aUnused);
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
   AudioChannelType mCurrentHigherChannel;
 
+  nsTArray<uint64_t> mActiveContentChildIDs;
+  bool mActiveContentChildIDsFrozen;
+
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
   friend class ContentParent;
   friend class ContentChild;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -140,16 +140,22 @@ TestContentChannels()
   Agent agent1(AUDIO_CHANNEL_CONTENT);
   nsresult rv = agent1.Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   Agent agent2(AUDIO_CHANNEL_CONTENT);
   rv = agent2.Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = agent1.mAgent->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = agent2.mAgent->SetVisibilityState(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   bool playing;
   rv = agent1.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing");
 
   rv = agent2.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
@@ -161,17 +167,17 @@ TestContentChannels()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent1.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing");
 
   rv = agent2.StartPlaying(&playing);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent2 should not be playing");
+  TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing");
 
   rv = agent1.mAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent2.mAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent1.StartPlaying(&playing);