1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/FMRadio.h"
8 #include "nsContentUtils.h"
9 #include "mozilla/Hal.h"
10 #include "mozilla/HalTypes.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/dom/FMRadioBinding.h"
13 #include "mozilla/dom/ContentChild.h"
14 #include "mozilla/dom/PFMRadioChild.h"
15 #include "mozilla/dom/FMRadioService.h"
16 #include "DOMRequest.h"
17 #include "nsDOMClassInfo.h"
18 #include "nsIDocShell.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsIAudioManager.h"
23 #define LOG(args...) FM_LOG("FMRadio", args)
25 // The pref indicates if the device has an internal antenna.
26 // If the pref is true, the antanna will be always available.
27 #define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
29 using namespace mozilla::hal
;
30 using mozilla::Preferences
;
32 BEGIN_FMRADIO_NAMESPACE
34 class FMRadioRequest MOZ_FINAL
: public FMRadioReplyRunnable
38 NS_DECL_ISUPPORTS_INHERITED
40 FMRadioRequest(nsPIDOMWindow
* aWindow
, FMRadio
* aFMRadio
)
42 , mType(FMRadioRequestArgs::T__None
)
44 // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference|
45 // which both inherits from nsISupports, so |nsISupports| is an ambiguous
46 // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes.
47 mFMRadio
= do_GetWeakReference(static_cast<nsIDOMEventTarget
*>(aFMRadio
));
50 FMRadioRequest(nsPIDOMWindow
* aWindow
, FMRadio
* aFMRadio
,
51 FMRadioRequestArgs::Type aType
)
54 MOZ_ASSERT(aType
>= FMRadioRequestArgs::T__None
&&
55 aType
<= FMRadioRequestArgs::T__Last
,
56 "Wrong FMRadioRequestArgs in FMRadioRequest");
58 mFMRadio
= do_GetWeakReference(static_cast<nsIDOMEventTarget
*>(aFMRadio
));
67 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
69 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryReferent(mFMRadio
);
74 FMRadio
* fmRadio
= static_cast<FMRadio
*>(
75 static_cast<nsIDOMEventTarget
*>(target
));
77 if (fmRadio
->mIsShutdown
) {
81 switch (mResponseType
.type()) {
82 case FMRadioResponseType::TErrorResponse
:
83 FireError(mResponseType
.get_ErrorResponse().error());
85 case FMRadioResponseType::TSuccessResponse
:
86 if (mType
== FMRadioRequestArgs::TEnableRequestArgs
) {
87 fmRadio
->EnableAudioChannelAgent();
90 FireSuccess(JS::UndefinedHandleValue
);
100 FMRadioRequestArgs::Type mType
;
104 NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest
, DOMRequest
)
107 : mHeadphoneState(SWITCH_STATE_OFF
)
108 , mAudioChannelAgentEnabled(false)
109 , mHasInternalAntenna(false)
112 LOG("FMRadio is initialized.");
122 FMRadio::Init(nsPIDOMWindow
*aWindow
)
124 BindToOwner(aWindow
);
126 IFMRadioService::Singleton()->AddObserver(this);
128 mHasInternalAntenna
= Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF
,
129 /* default = */ false);
130 if (mHasInternalAntenna
) {
131 LOG("We have an internal antenna.");
133 mHeadphoneState
= GetCurrentSwitchState(SWITCH_HEADPHONES
);
134 RegisterSwitchObserver(SWITCH_HEADPHONES
, this);
137 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(GetOwner());
138 NS_ENSURE_TRUE_VOID(target
);
139 target
->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
140 /* useCapture = */ true,
141 /* wantsUntrusted = */ false);
144 // All of the codes below are for AudioChannel. We can directly return here
145 // if preferences doesn't enable AudioChannelService.
146 NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService"));
148 nsCOMPtr
<nsIAudioChannelAgent
> audioChannelAgent
=
149 do_CreateInstance("@mozilla.org/audiochannelagent;1");
150 NS_ENSURE_TRUE_VOID(audioChannelAgent
);
152 audioChannelAgent
->InitWithWeakCallback(
154 nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT
,
157 nsCOMPtr
<nsIDocShell
> docshell
= do_GetInterface(GetOwner());
158 NS_ENSURE_TRUE_VOID(docshell
);
160 bool isActive
= false;
161 docshell
->GetIsActive(&isActive
);
162 audioChannelAgent
->SetVisibilityState(isActive
);
164 // Once all necessary resources are got successfully, we just enabled
165 // mAudioChannelAgent.
166 mAudioChannelAgent
= audioChannelAgent
;
172 IFMRadioService::Singleton()->RemoveObserver(this);
174 if (!mHasInternalAntenna
) {
175 UnregisterSwitchObserver(SWITCH_HEADPHONES
, this);
178 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(GetOwner());
179 NS_ENSURE_TRUE_VOID(target
);
180 target
->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
181 /* useCapture = */ true);
187 FMRadio::WrapObject(JSContext
* aCx
)
189 return FMRadioBinding::Wrap(aCx
, this);
193 FMRadio::Notify(const SwitchEvent
& aEvent
)
195 MOZ_ASSERT(!mHasInternalAntenna
);
197 if (mHeadphoneState
!= aEvent
.status()) {
198 mHeadphoneState
= aEvent
.status();
200 DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
205 FMRadio::Notify(const FMRadioEventType
& aType
)
208 case FrequencyChanged
:
209 DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
213 DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
215 if (mAudioChannelAgentEnabled
) {
216 mAudioChannelAgent
->StopPlaying();
217 mAudioChannelAgentEnabled
= false;
220 DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
232 return IFMRadioService::Singleton()->IsEnabled();
236 FMRadio::AntennaAvailable() const
238 return mHasInternalAntenna
? true : (mHeadphoneState
!= SWITCH_STATE_OFF
) &&
239 (mHeadphoneState
!= SWITCH_STATE_UNKNOWN
);
243 FMRadio::GetFrequency() const
246 Nullable
<double>(IFMRadioService::Singleton()->GetFrequency()) :
251 FMRadio::FrequencyUpperBound() const
253 return IFMRadioService::Singleton()->GetFrequencyUpperBound();
257 FMRadio::FrequencyLowerBound() const
259 return IFMRadioService::Singleton()->GetFrequencyLowerBound();
263 FMRadio::ChannelWidth() const
265 return IFMRadioService::Singleton()->GetChannelWidth();
268 already_AddRefed
<DOMRequest
>
269 FMRadio::Enable(double aFrequency
)
271 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
276 nsRefPtr
<FMRadioRequest
> r
=
277 new FMRadioRequest(win
, this, FMRadioRequestArgs::TEnableRequestArgs
);
278 IFMRadioService::Singleton()->Enable(aFrequency
, r
);
283 already_AddRefed
<DOMRequest
>
286 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
291 nsRefPtr
<FMRadioRequest
> r
= new FMRadioRequest(win
, this);
292 IFMRadioService::Singleton()->Disable(r
);
297 already_AddRefed
<DOMRequest
>
298 FMRadio::SetFrequency(double aFrequency
)
300 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
305 nsRefPtr
<FMRadioRequest
> r
= new FMRadioRequest(win
, this);
306 IFMRadioService::Singleton()->SetFrequency(aFrequency
, r
);
311 already_AddRefed
<DOMRequest
>
314 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
319 nsRefPtr
<FMRadioRequest
> r
= new FMRadioRequest(win
, this);
320 IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP
, r
);
325 already_AddRefed
<DOMRequest
>
328 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
333 nsRefPtr
<FMRadioRequest
> r
= new FMRadioRequest(win
, this);
334 IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN
, r
);
339 already_AddRefed
<DOMRequest
>
340 FMRadio::CancelSeek()
342 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
347 nsRefPtr
<FMRadioRequest
> r
= new FMRadioRequest(win
, this);
348 IFMRadioService::Singleton()->CancelSeek(r
);
354 FMRadio::HandleEvent(nsIDOMEvent
* aEvent
)
357 aEvent
->GetType(type
);
359 if (!type
.EqualsLiteral("visibilitychange")) {
360 return NS_ERROR_FAILURE
;
363 nsCOMPtr
<nsIDocShell
> docshell
= do_GetInterface(GetOwner());
364 NS_ENSURE_TRUE(docshell
, NS_ERROR_FAILURE
);
366 bool isActive
= false;
367 docshell
->GetIsActive(&isActive
);
369 mAudioChannelAgent
->SetVisibilityState(isActive
);
374 FMRadio::EnableAudioChannelAgent()
376 NS_ENSURE_TRUE_VOID(mAudioChannelAgent
);
378 int32_t playingState
= 0;
379 mAudioChannelAgent
->StartPlaying(&playingState
);
380 SetCanPlay(playingState
== AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL
);
382 mAudioChannelAgentEnabled
= true;
386 FMRadio::CanPlayChanged(int32_t aCanPlay
)
388 SetCanPlay(aCanPlay
== AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL
);
393 FMRadio::WindowVolumeChanged()
395 return NS_ERROR_NOT_IMPLEMENTED
;
399 FMRadio::SetCanPlay(bool aCanPlay
)
401 IFMRadioService::Singleton()->EnableAudio(aCanPlay
);
404 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio
)
405 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
406 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback
)
407 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
408 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
410 NS_IMPL_ADDREF_INHERITED(FMRadio
, DOMEventTargetHelper
)
411 NS_IMPL_RELEASE_INHERITED(FMRadio
, DOMEventTargetHelper
)
413 END_FMRADIO_NAMESPACE