Bug 862899 - AudioChannelAgent in the FMRadio API. r=mchen, r=khuey
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 04 Nov 2013 17:27:39 -0500
changeset 153515 319c8d1250f89779b58a420585994f4f19dee0ef
parent 153514 436820be86d2441cbbd37c8361d3d86310db435c
child 153516 fdc953ce073e1580dc596534b8ef5ab7d33a4a92
push id35816
push userkwierso@gmail.com
push dateTue, 05 Nov 2013 05:22:53 +0000
treeherdermozilla-inbound@442b47e9fb80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchen, khuey
bugs862899
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 862899 - AudioChannelAgent in the FMRadio API. r=mchen, r=khuey
dom/fmradio/FMRadio.cpp
dom/fmradio/FMRadio.h
dom/fmradio/FMRadioService.cpp
dom/fmradio/FMRadioService.h
dom/fmradio/ipc/FMRadioChild.cpp
dom/fmradio/ipc/FMRadioChild.h
dom/fmradio/ipc/FMRadioParent.cpp
dom/fmradio/ipc/FMRadioParent.h
dom/fmradio/ipc/PFMRadio.ipdl
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -9,16 +9,20 @@
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/FMRadioBinding.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/dom/FMRadioService.h"
 #include "DOMRequest.h"
+#include "nsDOMClassInfo.h"
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAudioManager.h"
 
 #undef LOG
 #define LOG(args...) FM_LOG("FMRadio", args)
 
 // The pref indicates if the device has an internal antenna.
 // If the pref is true, the antanna will be always available.
 #define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
 
@@ -105,27 +109,53 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
   mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
                                              /* default = */ false);
   if (mHasInternalAntenna) {
     LOG("We have an internal antenna.");
   } else {
     mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
     RegisterSwitchObserver(SWITCH_HEADPHONES, this);
   }
+
+  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+  NS_ENSURE_TRUE_VOID(target);
+  target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
+                                 /* useCapture = */ true,
+                                 /* wantsUntrusted = */ false);
+
+  mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
+  if (!mAudioChannelAgent) {
+    return;
+  }
+
+  mAudioChannelAgent->InitWithWeakCallback(nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT,
+                                           this);
+
+  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
+  if (docshell) {
+    bool isActive = false;
+    docshell->GetIsActive(&isActive);
+    mAudioChannelAgent->SetVisibilityState(isActive);
+  }
 }
 
 void
 FMRadio::Shutdown()
 {
   IFMRadioService::Singleton()->RemoveObserver(this);
 
   if (!mHasInternalAntenna) {
     UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
   }
 
+  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+  NS_ENSURE_TRUE_VOID(target);
+  target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
+                                    /* useCapture = */ true);
+
   mIsShutdown = true;
 }
 
 JSObject*
 FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return FMRadioBinding::Wrap(aCx, aScope, this);
 }
@@ -146,18 +176,24 @@ void
 FMRadio::Notify(const FMRadioEventType& aType)
 {
   switch (aType) {
     case FrequencyChanged:
       DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
       break;
     case EnabledChanged:
       if (Enabled()) {
+        int32_t playingState = 0;
+        mAudioChannelAgent->StartPlaying(&playingState);
+        SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
+
         DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
       } else {
+        mAudioChannelAgent->StopPlaying();
+
         DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
       }
       break;
     default:
       MOZ_CRASH();
   }
 }
 
@@ -279,17 +315,52 @@ FMRadio::CancelSeek()
   }
 
   nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
   IFMRadioService::Singleton()->CancelSeek(r);
 
   return r.forget();
 }
 
+NS_IMETHODIMP
+FMRadio::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsAutoString type;
+  aEvent->GetType(type);
+
+  if (!type.EqualsLiteral("visibilitychange")) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
+  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
+
+  bool isActive = false;
+  docshell->GetIsActive(&isActive);
+
+  mAudioChannelAgent->SetVisibilityState(isActive);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FMRadio::CanPlayChanged(int32_t aCanPlay)
+{
+  SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
+  return NS_OK;
+}
+
+void
+FMRadio::SetCanPlay(bool aCanPlay)
+{
+  IFMRadioService::Singleton()->EnableAudio(aCanPlay);
+}
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(FMRadio, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FMRadio, nsDOMEventTargetHelper)
 
 END_FMRADIO_NAMESPACE
 
--- a/dom/fmradio/FMRadio.h
+++ b/dom/fmradio/FMRadio.h
@@ -6,35 +6,40 @@
 #ifndef mozilla_dom_FMRadio_h
 #define mozilla_dom_FMRadio_h
 
 #include "FMRadioCommon.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/HalTypes.h"
 #include "nsWeakReference.h"
+#include "AudioChannelAgent.h"
 
 class nsPIDOMWindow;
 class nsIScriptContext;
 
 BEGIN_FMRADIO_NAMESPACE
 
 class DOMRequest;
 
 class FMRadio MOZ_FINAL : public nsDOMEventTargetHelper
                         , public hal::SwitchObserver
                         , public FMRadioEventObserver
                         , public nsSupportsWeakReference
+                        , public nsIAudioChannelAgentCallback
+                        , public nsIDOMEventListener
+
 {
   friend class FMRadioRequest;
 
 public:
   FMRadio();
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
 
   void Init(nsPIDOMWindow *aWindow);
   void Shutdown();
 
   /* hal::SwitchObserver */
   virtual void Notify(const hal::SwitchEvent& aEvent) MOZ_OVERRIDE;
@@ -73,20 +78,27 @@ public:
 
   already_AddRefed<DOMRequest> CancelSeek();
 
   IMPL_EVENT_HANDLER(enabled);
   IMPL_EVENT_HANDLER(disabled);
   IMPL_EVENT_HANDLER(antennaavailablechange);
   IMPL_EVENT_HANDLER(frequencychange);
 
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
 private:
   ~FMRadio();
 
+  void SetCanPlay(bool aCanPlay);
+
   hal::SwitchState mHeadphoneState;
   bool mHasInternalAntenna;
   bool mIsShutdown;
+
+  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 };
 
 END_FMRADIO_NAMESPACE
 
 #endif // mozilla_dom_FMRadio_h
 
--- a/dom/fmradio/FMRadioService.cpp
+++ b/dom/fmradio/FMRadioService.cpp
@@ -118,22 +118,18 @@ public:
   NS_IMETHOD Run()
   {
     FMRadioSettings info;
     info.upperLimit() = mUpperLimit;
     info.lowerLimit() = mLowerLimit;
     info.spaceType() = mSpaceType;
 
     EnableFMRadio(info);
+    IFMRadioService::Singleton()->EnableAudio(true);
 
-    nsCOMPtr<nsIAudioManager> audioManager =
-      do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-    audioManager->SetFmRadioAudioEnabled(true);
-
-    // TODO apply path from bug 862899: AudioChannelAgent per process
     return NS_OK;
   }
 
 private:
   int32_t mUpperLimit;
   int32_t mLowerLimit;
   int32_t mSpaceType;
 };
@@ -204,21 +200,17 @@ class DisableRunnable MOZ_FINAL : public
 public:
   DisableRunnable() { }
 
   NS_IMETHOD Run()
   {
     // Fix Bug 796733. DisableFMRadio should be called before
     // SetFmRadioAudioEnabled to prevent the annoying beep sound.
     DisableFMRadio();
-
-    nsCOMPtr<nsIAudioManager> audioManager =
-      do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-
-    audioManager->SetFmRadioAudioEnabled(false);
+    IFMRadioService::Singleton()->EnableAudio(false);
 
     return NS_OK;
   }
 };
 
 class SetFrequencyRunnable MOZ_FINAL : public nsRunnable
 {
 public:
@@ -294,16 +286,34 @@ FMRadioService::RemoveObserver(FMRadioEv
   {
     // Turning off the FM radio HW because observer list is empty.
     if (IsFMRadioOn()) {
       NS_DispatchToMainThread(new DisableRunnable());
     }
   }
 }
 
+void
+FMRadioService::EnableAudio(bool aAudioEnabled)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIAudioManager> audioManager =
+    do_GetService("@mozilla.org/telephony/audiomanager;1");
+  if (!audioManager) {
+    return;
+  }
+
+  bool AudioEnabled;
+  audioManager->GetFmRadioAudioEnabled(&AudioEnabled);
+  if (AudioEnabled != aAudioEnabled) {
+    audioManager->SetFmRadioAudioEnabled(aAudioEnabled);
+  }
+}
+
 /**
  * Round the frequency to match the range of frequency and the channel width. If
  * the given frequency is out of range, return 0. For example:
  *  - lower: 87500KHz, upper: 108000KHz, channel width: 200KHz
  *    87.6MHz is rounded to 87700KHz
  *    87.58MHz is rounded to 87500KHz
  *    87.49MHz is rounded to 87500KHz
  *    109MHz is not rounded, 0 will be returned
--- a/dom/fmradio/FMRadioService.h
+++ b/dom/fmradio/FMRadioService.h
@@ -112,16 +112,19 @@ public:
    *   - StateChangedEvent
    *   - FrequencyChangedEvent
    *
    * Called by FMRadio and FMRadioParent.
    */
   virtual void AddObserver(FMRadioEventObserver* aObserver) = 0;
   virtual void RemoveObserver(FMRadioEventObserver* aObserver) = 0;
 
+  // Enable/Disable FMRadio
+  virtual void EnableAudio(bool aAudioEnabled) = 0;
+
   /**
    * Static method to return the singleton instance. If it's in the child
    * process, we will get an object of FMRadioChild.
    */
   static IFMRadioService* Singleton();
 };
 
 enum FMRadioState
@@ -159,16 +162,18 @@ public:
                             FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
   virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
                     FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
   virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
 
   virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
   virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
 
+  virtual void EnableAudio(bool aAudioEnabled) MOZ_OVERRIDE;
+
   /* FMRadioObserver */
   void Notify(const hal::FMRadioOperationInformation& aInfo) MOZ_OVERRIDE;
 
   NS_DECL_NSIOBSERVER
 
 protected:
   FMRadioService();
 
--- a/dom/fmradio/ipc/FMRadioChild.cpp
+++ b/dom/fmradio/ipc/FMRadioChild.cpp
@@ -160,16 +160,22 @@ FMRadioChild::AllocPFMRadioRequestChild(
 
 bool
 FMRadioChild::DeallocPFMRadioRequestChild(PFMRadioRequestChild* aActor)
 {
   delete aActor;
   return true;
 }
 
+void
+FMRadioChild::EnableAudio(bool aAudioEnabled)
+{
+  SendEnableAudio(aAudioEnabled);
+}
+
 // static
 FMRadioChild*
 FMRadioChild::Singleton()
 {
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sFMRadioChild) {
--- a/dom/fmradio/ipc/FMRadioChild.h
+++ b/dom/fmradio/ipc/FMRadioChild.h
@@ -46,16 +46,18 @@ public:
                             FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
   virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
                     FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
   virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
 
   virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
   virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
 
+  virtual void EnableAudio(bool aAudioEnabled) MOZ_OVERRIDE;
+
   /* PFMRadioChild */
   virtual bool
   Recv__delete__() MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyFrequencyChanged(const double& aFrequency) MOZ_OVERRIDE;
 
   virtual bool
--- a/dom/fmradio/ipc/FMRadioParent.cpp
+++ b/dom/fmradio/ipc/FMRadioParent.cpp
@@ -92,10 +92,17 @@ FMRadioParent::Notify(const FMRadioEvent
         IFMRadioService::Singleton()->GetFrequency());
       break;
     default:
       NS_RUNTIMEABORT("not reached");
       break;
   }
 }
 
+bool
+FMRadioParent::RecvEnableAudio(const bool& aAudioEnabled)
+{
+  IFMRadioService::Singleton()->EnableAudio(aAudioEnabled);
+  return true;
+}
+
 END_FMRADIO_NAMESPACE
 
--- a/dom/fmradio/ipc/FMRadioParent.h
+++ b/dom/fmradio/ipc/FMRadioParent.h
@@ -28,14 +28,17 @@ public:
   virtual PFMRadioRequestParent*
   AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPFMRadioRequestParent(PFMRadioRequestParent* aActor) MOZ_OVERRIDE;
 
   /* FMRadioEventObserver */
   virtual void Notify(const FMRadioEventType& aType) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvEnableAudio(const bool& aAudioEnabled) MOZ_OVERRIDE;
 };
 
 END_FMRADIO_NAMESPACE
 
 #endif // mozilla_dom_fmradioparent_h__
 
--- a/dom/fmradio/ipc/PFMRadio.ipdl
+++ b/dom/fmradio/ipc/PFMRadio.ipdl
@@ -81,13 +81,18 @@ parent:
    *
    * We don't have separate Enable/SetFrequency/etc. methods instead here,
    * because we can leverage the IPC messaging mechanism to manage the mapping
    * of the asynchronous request and the DOMRequest we returned to the caller
    * on web content, otherwise, we have to do the mapping stuff manually which
    * is more error prone.
    */
   PFMRadioRequest(FMRadioRequestArgs requestType);
+
+  /**
+   * Enable/Disable audio
+   */
+  EnableAudio(bool audioEnabled);
 };
 
 } // namespace dom
 } // namespace mozilla