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.

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

mercurial