dom/fmradio/FMRadioService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     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 file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "FMRadioService.h"
     8 #include "mozilla/Hal.h"
     9 #include "nsIAudioManager.h"
    10 #include "AudioManager.h"
    11 #include "nsDOMClassInfo.h"
    12 #include "mozilla/Preferences.h"
    13 #include "mozilla/dom/FMRadioChild.h"
    14 #include "nsIObserverService.h"
    15 #include "nsISettingsService.h"
    16 #include "nsJSUtils.h"
    17 #include "nsCxPusher.h"
    19 #define BAND_87500_108000_kHz 1
    20 #define BAND_76000_108000_kHz 2
    21 #define BAND_76000_90000_kHz  3
    23 #define CHANNEL_WIDTH_200KHZ 200
    24 #define CHANNEL_WIDTH_100KHZ 100
    25 #define CHANNEL_WIDTH_50KHZ  50
    27 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
    28 #define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
    30 using namespace mozilla::hal;
    31 using mozilla::Preferences;
    33 BEGIN_FMRADIO_NAMESPACE
    35 // static
    36 IFMRadioService*
    37 IFMRadioService::Singleton()
    38 {
    39   if (XRE_GetProcessType() != GeckoProcessType_Default) {
    40     return FMRadioChild::Singleton();
    41   } else {
    42     return FMRadioService::Singleton();
    43   }
    44 }
    46 StaticRefPtr<FMRadioService> FMRadioService::sFMRadioService;
    48 FMRadioService::FMRadioService()
    49   : mPendingFrequencyInKHz(0)
    50   , mState(Disabled)
    51   , mHasReadAirplaneModeSetting(false)
    52   , mAirplaneModeEnabled(false)
    53   , mPendingRequest(nullptr)
    54   , mObserverList(FMRadioEventObserverList())
    55 {
    57   // Read power state and frequency from Hal.
    58   mEnabled = IsFMRadioOn();
    59   if (mEnabled) {
    60     mPendingFrequencyInKHz = GetFMRadioFrequency();
    61     SetState(Enabled);
    62   }
    64   switch (Preferences::GetInt("dom.fmradio.band", BAND_87500_108000_kHz)) {
    65     case BAND_76000_90000_kHz:
    66       mUpperBoundInKHz = 90000;
    67       mLowerBoundInKHz = 76000;
    68       break;
    69     case BAND_76000_108000_kHz:
    70       mUpperBoundInKHz = 108000;
    71       mLowerBoundInKHz = 76000;
    72       break;
    73     case BAND_87500_108000_kHz:
    74     default:
    75       mUpperBoundInKHz = 108000;
    76       mLowerBoundInKHz = 87500;
    77       break;
    78   }
    80   switch (Preferences::GetInt("dom.fmradio.channelWidth",
    81                               CHANNEL_WIDTH_100KHZ)) {
    82     case CHANNEL_WIDTH_200KHZ:
    83       mChannelWidthInKHz = 200;
    84       break;
    85     case CHANNEL_WIDTH_50KHZ:
    86       mChannelWidthInKHz = 50;
    87       break;
    88     case CHANNEL_WIDTH_100KHZ:
    89     default:
    90       mChannelWidthInKHz = 100;
    91       break;
    92   }
    94   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    96   if (obs && NS_FAILED(obs->AddObserver(this,
    97                                         MOZSETTINGS_CHANGED_ID,
    98                                         /* useWeak */ false))) {
    99     NS_WARNING("Failed to add settings change observer!");
   100   }
   102   RegisterFMRadioObserver(this);
   103 }
   105 FMRadioService::~FMRadioService()
   106 {
   107   UnregisterFMRadioObserver(this);
   108 }
   110 class EnableRunnable MOZ_FINAL : public nsRunnable
   111 {
   112 public:
   113   EnableRunnable(int32_t aUpperLimit, int32_t aLowerLimit, int32_t aSpaceType)
   114     : mUpperLimit(aUpperLimit)
   115     , mLowerLimit(aLowerLimit)
   116     , mSpaceType(aSpaceType) { }
   118   NS_IMETHOD Run()
   119   {
   120     FMRadioSettings info;
   121     info.upperLimit() = mUpperLimit;
   122     info.lowerLimit() = mLowerLimit;
   123     info.spaceType() = mSpaceType;
   125     EnableFMRadio(info);
   127     return NS_OK;
   128   }
   130 private:
   131   int32_t mUpperLimit;
   132   int32_t mLowerLimit;
   133   int32_t mSpaceType;
   134 };
   136 /**
   137  * Read the airplane-mode setting, if the airplane-mode is not enabled, we
   138  * enable the FM radio.
   139  */
   140 class ReadAirplaneModeSettingTask MOZ_FINAL : public nsISettingsServiceCallback
   141 {
   142 public:
   143   NS_DECL_ISUPPORTS
   145   ReadAirplaneModeSettingTask(nsRefPtr<FMRadioReplyRunnable> aPendingRequest)
   146     : mPendingRequest(aPendingRequest) { }
   148   NS_IMETHOD
   149   Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   150   {
   151     FMRadioService* fmRadioService = FMRadioService::Singleton();
   152     MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
   154     fmRadioService->mHasReadAirplaneModeSetting = true;
   156     if (!aResult.isBoolean()) {
   157       // Failed to read the setting value, set the state back to Disabled.
   158       fmRadioService->TransitionState(
   159         ErrorResponse(NS_LITERAL_STRING("Unexpected error")), Disabled);
   160       return NS_OK;
   161     }
   163     fmRadioService->mAirplaneModeEnabled = aResult.toBoolean();
   164     if (!fmRadioService->mAirplaneModeEnabled) {
   165       EnableRunnable* runnable =
   166         new EnableRunnable(fmRadioService->mUpperBoundInKHz,
   167                            fmRadioService->mLowerBoundInKHz,
   168                            fmRadioService->mChannelWidthInKHz);
   169       NS_DispatchToMainThread(runnable);
   170     } else {
   171       // Airplane mode is enabled, set the state back to Disabled.
   172       fmRadioService->TransitionState(ErrorResponse(
   173         NS_LITERAL_STRING("Airplane mode currently enabled")), Disabled);
   174     }
   176     return NS_OK;
   177   }
   179   NS_IMETHOD
   180   HandleError(const nsAString& aName)
   181   {
   182     FMRadioService* fmRadioService = FMRadioService::Singleton();
   183     MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
   185     fmRadioService->TransitionState(ErrorResponse(
   186       NS_LITERAL_STRING("Unexpected error")), Disabled);
   188     return NS_OK;
   189   }
   191 private:
   192   nsRefPtr<FMRadioReplyRunnable> mPendingRequest;
   193 };
   195 NS_IMPL_ISUPPORTS(ReadAirplaneModeSettingTask, nsISettingsServiceCallback)
   197 class DisableRunnable MOZ_FINAL : public nsRunnable
   198 {
   199 public:
   200   DisableRunnable() { }
   202   NS_IMETHOD Run()
   203   {
   204     // Fix Bug 796733. DisableFMRadio should be called before
   205     // SetFmRadioAudioEnabled to prevent the annoying beep sound.
   206     DisableFMRadio();
   207     IFMRadioService::Singleton()->EnableAudio(false);
   209     return NS_OK;
   210   }
   211 };
   213 class SetFrequencyRunnable MOZ_FINAL : public nsRunnable
   214 {
   215 public:
   216   SetFrequencyRunnable(int32_t aFrequency)
   217     : mFrequency(aFrequency) { }
   219   NS_IMETHOD Run()
   220   {
   221     SetFMRadioFrequency(mFrequency);
   222     return NS_OK;
   223   }
   225 private:
   226   int32_t mFrequency;
   227 };
   229 class SeekRunnable MOZ_FINAL : public nsRunnable
   230 {
   231 public:
   232   SeekRunnable(FMRadioSeekDirection aDirection) : mDirection(aDirection) { }
   234   NS_IMETHOD Run()
   235   {
   236     switch (mDirection) {
   237       case FM_RADIO_SEEK_DIRECTION_UP:
   238       case FM_RADIO_SEEK_DIRECTION_DOWN:
   239         FMRadioSeek(mDirection);
   240         break;
   241       default:
   242         MOZ_CRASH();
   243     }
   245     return NS_OK;
   246   }
   248 private:
   249   FMRadioSeekDirection mDirection;
   250 };
   252 void
   253 FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
   254                                 FMRadioState aState)
   255 {
   256   if (mPendingRequest) {
   257     mPendingRequest->SetReply(aResponse);
   258     NS_DispatchToMainThread(mPendingRequest);
   259   }
   261   SetState(aState);
   262 }
   264 void
   265 FMRadioService::SetState(FMRadioState aState)
   266 {
   267   mState = aState;
   268   mPendingRequest = nullptr;
   269 }
   271 void
   272 FMRadioService::AddObserver(FMRadioEventObserver* aObserver)
   273 {
   274   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   275   mObserverList.AddObserver(aObserver);
   276 }
   278 void
   279 FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
   280 {
   281   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   282   mObserverList.RemoveObserver(aObserver);
   284   if (mObserverList.Length() == 0)
   285   {
   286     // Turning off the FM radio HW because observer list is empty.
   287     if (IsFMRadioOn()) {
   288       NS_DispatchToMainThread(new DisableRunnable());
   289     }
   290   }
   291 }
   293 void
   294 FMRadioService::EnableAudio(bool aAudioEnabled)
   295 {
   296   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   298   nsCOMPtr<nsIAudioManager> audioManager =
   299     do_GetService("@mozilla.org/telephony/audiomanager;1");
   300   if (!audioManager) {
   301     return;
   302   }
   304   bool audioEnabled;
   305   audioManager->GetFmRadioAudioEnabled(&audioEnabled);
   306   if (audioEnabled != aAudioEnabled) {
   307     audioManager->SetFmRadioAudioEnabled(aAudioEnabled);
   308   }
   309 }
   311 /**
   312  * Round the frequency to match the range of frequency and the channel width. If
   313  * the given frequency is out of range, return 0. For example:
   314  *  - lower: 87500KHz, upper: 108000KHz, channel width: 200KHz
   315  *    87.6MHz is rounded to 87700KHz
   316  *    87.58MHz is rounded to 87500KHz
   317  *    87.49MHz is rounded to 87500KHz
   318  *    109MHz is not rounded, 0 will be returned
   319  *
   320  * We take frequency in MHz to prevent precision losing, and return rounded
   321  * value in KHz for Gonk using.
   322  */
   323 int32_t
   324 FMRadioService::RoundFrequency(double aFrequencyInMHz)
   325 {
   326   double halfChannelWidthInMHz = mChannelWidthInKHz / 1000.0 / 2;
   328   // Make sure 87.49999MHz would be rounded to the lower bound when
   329   // the lower bound is 87500KHz.
   330   if (aFrequencyInMHz < mLowerBoundInKHz / 1000.0 - halfChannelWidthInMHz ||
   331       aFrequencyInMHz > mUpperBoundInKHz / 1000.0 + halfChannelWidthInMHz) {
   332     return 0;
   333   }
   335   int32_t partToBeRounded = round(aFrequencyInMHz * 1000) - mLowerBoundInKHz;
   336   int32_t roundedPart = round(partToBeRounded / (double)mChannelWidthInKHz) *
   337                         mChannelWidthInKHz;
   339   return mLowerBoundInKHz + roundedPart;
   340 }
   342 bool
   343 FMRadioService::IsEnabled() const
   344 {
   345   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   346   return IsFMRadioOn();
   347 }
   349 double
   350 FMRadioService::GetFrequency() const
   351 {
   352   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   353   if (IsEnabled()) {
   354     int32_t frequencyInKHz = GetFMRadioFrequency();
   355     return frequencyInKHz / 1000.0;
   356   }
   358   return 0;
   359 }
   361 double
   362 FMRadioService::GetFrequencyUpperBound() const
   363 {
   364   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   365   return mUpperBoundInKHz / 1000.0;
   366 }
   368 double
   369 FMRadioService::GetFrequencyLowerBound() const
   370 {
   371   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   372   return mLowerBoundInKHz / 1000.0;
   373 }
   375 double
   376 FMRadioService::GetChannelWidth() const
   377 {
   378   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   379   return mChannelWidthInKHz / 1000.0;
   380 }
   382 void
   383 FMRadioService::Enable(double aFrequencyInMHz,
   384                        FMRadioReplyRunnable* aReplyRunnable)
   385 {
   386   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   387   MOZ_ASSERT(aReplyRunnable);
   389   switch (mState) {
   390     case Seeking:
   391     case Enabled:
   392       aReplyRunnable->SetReply(
   393         ErrorResponse(NS_LITERAL_STRING("FM radio currently enabled")));
   394       NS_DispatchToMainThread(aReplyRunnable);
   395       return;
   396     case Disabling:
   397       aReplyRunnable->SetReply(
   398         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
   399       NS_DispatchToMainThread(aReplyRunnable);
   400       return;
   401     case Enabling:
   402       aReplyRunnable->SetReply(
   403         ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
   404       NS_DispatchToMainThread(aReplyRunnable);
   405       return;
   406     case Disabled:
   407       break;
   408   }
   410   int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
   412   if (!roundedFrequency) {
   413     aReplyRunnable->SetReply(ErrorResponse(
   414       NS_LITERAL_STRING("Frequency is out of range")));
   415     NS_DispatchToMainThread(aReplyRunnable);
   416     return;
   417   }
   419   if (mHasReadAirplaneModeSetting && mAirplaneModeEnabled) {
   420     aReplyRunnable->SetReply(ErrorResponse(
   421       NS_LITERAL_STRING("Airplane mode currently enabled")));
   422     NS_DispatchToMainThread(aReplyRunnable);
   423     return;
   424   }
   426   SetState(Enabling);
   427   // Cache the enable request just in case disable() is called
   428   // while the FM radio HW is being enabled.
   429   mPendingRequest = aReplyRunnable;
   431   // Cache the frequency value, and set it after the FM radio HW is enabled
   432   mPendingFrequencyInKHz = roundedFrequency;
   434   if (!mHasReadAirplaneModeSetting) {
   435     nsCOMPtr<nsISettingsService> settings =
   436       do_GetService("@mozilla.org/settingsService;1");
   438     nsCOMPtr<nsISettingsServiceLock> settingsLock;
   439     nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
   440     if (NS_FAILED(rv)) {
   441       TransitionState(ErrorResponse(
   442         NS_LITERAL_STRING("Can't create settings lock")), Disabled);
   443       return;
   444     }
   446     nsRefPtr<ReadAirplaneModeSettingTask> callback =
   447       new ReadAirplaneModeSettingTask(mPendingRequest);
   449     rv = settingsLock->Get(SETTING_KEY_AIRPLANEMODE_ENABLED, callback);
   450     if (NS_FAILED(rv)) {
   451       TransitionState(ErrorResponse(
   452         NS_LITERAL_STRING("Can't get settings lock")), Disabled);
   453     }
   455     return;
   456   }
   458   NS_DispatchToMainThread(new EnableRunnable(mUpperBoundInKHz,
   459                                              mLowerBoundInKHz,
   460                                              mChannelWidthInKHz));
   461 }
   463 void
   464 FMRadioService::Disable(FMRadioReplyRunnable* aReplyRunnable)
   465 {
   466   // When airplane-mode is enabled, we will call this function from
   467   // FMRadioService::Observe without passing a FMRadioReplyRunnable,
   468   // so we have to check if |aReplyRunnable| is null before we dispatch it.
   469   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   471   switch (mState) {
   472     case Disabling:
   473       if (aReplyRunnable) {
   474         aReplyRunnable->SetReply(
   475           ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
   476         NS_DispatchToMainThread(aReplyRunnable);
   477       }
   478       return;
   479     case Disabled:
   480       if (aReplyRunnable) {
   481         aReplyRunnable->SetReply(
   482           ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
   483         NS_DispatchToMainThread(aReplyRunnable);
   484       }
   485       return;
   486     case Enabled:
   487     case Enabling:
   488     case Seeking:
   489       break;
   490   }
   492   nsRefPtr<FMRadioReplyRunnable> enablingRequest = mPendingRequest;
   494   // If the FM Radio is currently seeking, no fail-to-seek or similar
   495   // event will be fired, execute the seek callback manually.
   496   if (mState == Seeking) {
   497     TransitionState(ErrorResponse(
   498       NS_LITERAL_STRING("Seek action is cancelled")), Disabling);
   499   }
   501   FMRadioState preState = mState;
   502   SetState(Disabling);
   503   mPendingRequest = aReplyRunnable;
   505   if (preState == Enabling) {
   506     // If the radio is currently enabling, we fire the error callback on the
   507     // enable request immediately. When the radio finishes enabling, we'll call
   508     // DoDisable and fire the success callback on the disable request.
   509     enablingRequest->SetReply(
   510       ErrorResponse(NS_LITERAL_STRING("Enable action is cancelled")));
   511     NS_DispatchToMainThread(enablingRequest);
   513     // If we haven't read the airplane mode settings yet we won't enable the
   514     // FM radio HW, so fail the disable request immediately.
   515     if (!mHasReadAirplaneModeSetting) {
   516       SetState(Disabled);
   518       if (aReplyRunnable) {
   519         aReplyRunnable->SetReply(SuccessResponse());
   520         NS_DispatchToMainThread(aReplyRunnable);
   521       }
   522     }
   524     return;
   525   }
   527   DoDisable();
   528 }
   530 void
   531 FMRadioService::DoDisable()
   532 {
   533   // To make such codes work:
   534   //    navigator.mozFMRadio.disable();
   535   //    navigator.mozFMRadio.ondisabled = function() {
   536   //      console.log("We will catch disabled event ");
   537   //    };
   538   // we need to call hal::DisableFMRadio() asynchronously. Same reason for
   539   // EnableRunnable and SetFrequencyRunnable.
   540   NS_DispatchToMainThread(new DisableRunnable());
   541 }
   543 void
   544 FMRadioService::SetFrequency(double aFrequencyInMHz,
   545                              FMRadioReplyRunnable* aReplyRunnable)
   546 {
   547   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   548   MOZ_ASSERT(aReplyRunnable);
   550   switch (mState) {
   551     case Disabled:
   552       aReplyRunnable->SetReply(
   553         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
   554       NS_DispatchToMainThread(aReplyRunnable);
   555       return;
   556     case Enabling:
   557       aReplyRunnable->SetReply(
   558         ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
   559       NS_DispatchToMainThread(aReplyRunnable);
   560       return;
   561     case Disabling:
   562       aReplyRunnable->SetReply(
   563         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
   564       NS_DispatchToMainThread(aReplyRunnable);
   565       return;
   566     case Seeking:
   567       CancelFMRadioSeek();
   568       TransitionState(ErrorResponse(
   569         NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
   570       break;
   571     case Enabled:
   572       break;
   573   }
   575   int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
   577   if (!roundedFrequency) {
   578     aReplyRunnable->SetReply(ErrorResponse(
   579       NS_LITERAL_STRING("Frequency is out of range")));
   580     NS_DispatchToMainThread(aReplyRunnable);
   581     return;
   582   }
   584   NS_DispatchToMainThread(new SetFrequencyRunnable(roundedFrequency));
   586   aReplyRunnable->SetReply(SuccessResponse());
   587   NS_DispatchToMainThread(aReplyRunnable);
   588 }
   590 void
   591 FMRadioService::Seek(FMRadioSeekDirection aDirection,
   592                      FMRadioReplyRunnable* aReplyRunnable)
   593 {
   594   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   595   MOZ_ASSERT(aReplyRunnable);
   597   switch (mState) {
   598     case Enabling:
   599       aReplyRunnable->SetReply(
   600         ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
   601       NS_DispatchToMainThread(aReplyRunnable);
   602       return;
   603     case Disabled:
   604       aReplyRunnable->SetReply(
   605         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
   606       NS_DispatchToMainThread(aReplyRunnable);
   607       return;
   608     case Seeking:
   609       aReplyRunnable->SetReply(
   610         ErrorResponse(NS_LITERAL_STRING("FM radio currently seeking")));
   611       NS_DispatchToMainThread(aReplyRunnable);
   612       return;
   613     case Disabling:
   614       aReplyRunnable->SetReply(
   615         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
   616       NS_DispatchToMainThread(aReplyRunnable);
   617       return;
   618     case Enabled:
   619       break;
   620   }
   622   SetState(Seeking);
   623   mPendingRequest = aReplyRunnable;
   625   NS_DispatchToMainThread(new SeekRunnable(aDirection));
   626 }
   628 void
   629 FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
   630 {
   631   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   632   MOZ_ASSERT(aReplyRunnable);
   634   // We accept canceling seek request only if it's currently seeking.
   635   if (mState != Seeking) {
   636     aReplyRunnable->SetReply(
   637       ErrorResponse(NS_LITERAL_STRING("FM radio currently not seeking")));
   638     NS_DispatchToMainThread(aReplyRunnable);
   639     return;
   640   }
   642   // Cancel the seek immediately to prevent it from completing.
   643   CancelFMRadioSeek();
   645   TransitionState(
   646     ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
   648   aReplyRunnable->SetReply(SuccessResponse());
   649   NS_DispatchToMainThread(aReplyRunnable);
   650 }
   652 NS_IMETHODIMP
   653 FMRadioService::Observe(nsISupports * aSubject,
   654                         const char * aTopic,
   655                         const char16_t * aData)
   656 {
   657   MOZ_ASSERT(NS_IsMainThread());
   658   MOZ_ASSERT(sFMRadioService);
   660   if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
   661     return NS_OK;
   662   }
   664   // The string that we're interested in will be a JSON string looks like:
   665   //  {"key":"airplaneMode.enabled","value":true}
   666   AutoSafeJSContext cx;
   667   const nsDependentString dataStr(aData);
   668   JS::Rooted<JS::Value> val(cx);
   669   if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
   670       !val.isObject()) {
   671     NS_WARNING("Bad JSON string format.");
   672     return NS_OK;
   673   }
   675   JS::Rooted<JSObject*> obj(cx, &val.toObject());
   676   JS::Rooted<JS::Value> key(cx);
   677   if (!JS_GetProperty(cx, obj, "key", &key) ||
   678       !key.isString()) {
   679     NS_WARNING("Failed to get string property `key`.");
   680     return NS_OK;
   681   }
   683   JS::Rooted<JSString*> jsKey(cx, key.toString());
   684   nsDependentJSString keyStr;
   685   if (!keyStr.init(cx, jsKey)) {
   686     return NS_OK;
   687   }
   689   if (keyStr.EqualsLiteral(SETTING_KEY_AIRPLANEMODE_ENABLED)) {
   690     JS::Rooted<JS::Value> value(cx);
   691     if (!JS_GetProperty(cx, obj, "value", &value)) {
   692       NS_WARNING("Failed to get property `value`.");
   693       return NS_OK;
   694     }
   696     if (!value.isBoolean()) {
   697       return NS_OK;
   698     }
   700     mAirplaneModeEnabled = value.toBoolean();
   701     mHasReadAirplaneModeSetting = true;
   703     // Disable the FM radio HW if Airplane mode is enabled.
   704     if (mAirplaneModeEnabled) {
   705       Disable(nullptr);
   706     }
   707   }
   709   return NS_OK;
   710 }
   712 void
   713 FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
   714 {
   715   mObserverList.Broadcast(aType);
   716 }
   718 void
   719 FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
   720 {
   721   switch (aInfo.operation()) {
   722     case FM_RADIO_OPERATION_ENABLE:
   723       MOZ_ASSERT(IsFMRadioOn());
   724       MOZ_ASSERT(mState == Disabling || mState == Enabling);
   726       // If we're disabling, disable the radio right now.
   727       if (mState == Disabling) {
   728         DoDisable();
   729         return;
   730       }
   732       // Fire success callback on the enable request.
   733       TransitionState(SuccessResponse(), Enabled);
   735       // To make sure the FM app will get the right frequency after the FM
   736       // radio is enabled, we have to set the frequency first.
   737       SetFMRadioFrequency(mPendingFrequencyInKHz);
   739       // Bug 949855: enable audio after the FM radio HW is enabled, to make sure
   740       // 'hw.fm.isAnalog' could be detected as |true| during first time launch.
   741       // This case is for audio output on analog path, i.e. 'ro.moz.fm.noAnalog'
   742       // is not |true|.
   743       EnableAudio(true);
   745       // Update the current frequency without sending the`FrequencyChanged`
   746       // event, to make sure the FM app will get the right frequency when the
   747       // `EnabledChange` event is sent.
   748       mPendingFrequencyInKHz = GetFMRadioFrequency();
   749       UpdatePowerState();
   751       // The frequency was changed from '0' to some meaningful number, so we
   752       // should send the `FrequencyChanged` event manually.
   753       NotifyFMRadioEvent(FrequencyChanged);
   754       break;
   755     case FM_RADIO_OPERATION_DISABLE:
   756       MOZ_ASSERT(mState == Disabling);
   758       TransitionState(SuccessResponse(), Disabled);
   759       UpdatePowerState();
   760       break;
   761     case FM_RADIO_OPERATION_SEEK:
   763       // Seek action might be cancelled by SetFrequency(), we need to check if
   764       // the current state is Seeking.
   765       if (mState == Seeking) {
   766         TransitionState(SuccessResponse(), Enabled);
   767       }
   769       UpdateFrequency();
   770       break;
   771     case FM_RADIO_OPERATION_TUNE:
   772       UpdateFrequency();
   773       break;
   774     default:
   775       MOZ_CRASH();
   776   }
   777 }
   779 void
   780 FMRadioService::UpdatePowerState()
   781 {
   782   bool enabled = IsFMRadioOn();
   783   if (enabled != mEnabled) {
   784     mEnabled = enabled;
   785     NotifyFMRadioEvent(EnabledChanged);
   786   }
   787 }
   789 void
   790 FMRadioService::UpdateFrequency()
   791 {
   792   int32_t frequency = GetFMRadioFrequency();
   793   if (mPendingFrequencyInKHz != frequency) {
   794     mPendingFrequencyInKHz = frequency;
   795     NotifyFMRadioEvent(FrequencyChanged);
   796   }
   797 }
   799 // static
   800 FMRadioService*
   801 FMRadioService::Singleton()
   802 {
   803   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   804   MOZ_ASSERT(NS_IsMainThread());
   806   if (!sFMRadioService) {
   807     sFMRadioService = new FMRadioService();
   808   }
   810   return sFMRadioService;
   811 }
   813 NS_IMPL_ISUPPORTS(FMRadioService, nsIObserver)
   815 END_FMRADIO_NAMESPACE

mercurial