dom/fmradio/FMRadioService.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:8a8194a888a9
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/. */
6
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"
18
19 #define BAND_87500_108000_kHz 1
20 #define BAND_76000_108000_kHz 2
21 #define BAND_76000_90000_kHz 3
22
23 #define CHANNEL_WIDTH_200KHZ 200
24 #define CHANNEL_WIDTH_100KHZ 100
25 #define CHANNEL_WIDTH_50KHZ 50
26
27 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
28 #define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
29
30 using namespace mozilla::hal;
31 using mozilla::Preferences;
32
33 BEGIN_FMRADIO_NAMESPACE
34
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 }
45
46 StaticRefPtr<FMRadioService> FMRadioService::sFMRadioService;
47
48 FMRadioService::FMRadioService()
49 : mPendingFrequencyInKHz(0)
50 , mState(Disabled)
51 , mHasReadAirplaneModeSetting(false)
52 , mAirplaneModeEnabled(false)
53 , mPendingRequest(nullptr)
54 , mObserverList(FMRadioEventObserverList())
55 {
56
57 // Read power state and frequency from Hal.
58 mEnabled = IsFMRadioOn();
59 if (mEnabled) {
60 mPendingFrequencyInKHz = GetFMRadioFrequency();
61 SetState(Enabled);
62 }
63
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 }
79
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 }
93
94 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
95
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 }
101
102 RegisterFMRadioObserver(this);
103 }
104
105 FMRadioService::~FMRadioService()
106 {
107 UnregisterFMRadioObserver(this);
108 }
109
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) { }
117
118 NS_IMETHOD Run()
119 {
120 FMRadioSettings info;
121 info.upperLimit() = mUpperLimit;
122 info.lowerLimit() = mLowerLimit;
123 info.spaceType() = mSpaceType;
124
125 EnableFMRadio(info);
126
127 return NS_OK;
128 }
129
130 private:
131 int32_t mUpperLimit;
132 int32_t mLowerLimit;
133 int32_t mSpaceType;
134 };
135
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
144
145 ReadAirplaneModeSettingTask(nsRefPtr<FMRadioReplyRunnable> aPendingRequest)
146 : mPendingRequest(aPendingRequest) { }
147
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);
153
154 fmRadioService->mHasReadAirplaneModeSetting = true;
155
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 }
162
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 }
175
176 return NS_OK;
177 }
178
179 NS_IMETHOD
180 HandleError(const nsAString& aName)
181 {
182 FMRadioService* fmRadioService = FMRadioService::Singleton();
183 MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
184
185 fmRadioService->TransitionState(ErrorResponse(
186 NS_LITERAL_STRING("Unexpected error")), Disabled);
187
188 return NS_OK;
189 }
190
191 private:
192 nsRefPtr<FMRadioReplyRunnable> mPendingRequest;
193 };
194
195 NS_IMPL_ISUPPORTS(ReadAirplaneModeSettingTask, nsISettingsServiceCallback)
196
197 class DisableRunnable MOZ_FINAL : public nsRunnable
198 {
199 public:
200 DisableRunnable() { }
201
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);
208
209 return NS_OK;
210 }
211 };
212
213 class SetFrequencyRunnable MOZ_FINAL : public nsRunnable
214 {
215 public:
216 SetFrequencyRunnable(int32_t aFrequency)
217 : mFrequency(aFrequency) { }
218
219 NS_IMETHOD Run()
220 {
221 SetFMRadioFrequency(mFrequency);
222 return NS_OK;
223 }
224
225 private:
226 int32_t mFrequency;
227 };
228
229 class SeekRunnable MOZ_FINAL : public nsRunnable
230 {
231 public:
232 SeekRunnable(FMRadioSeekDirection aDirection) : mDirection(aDirection) { }
233
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 }
244
245 return NS_OK;
246 }
247
248 private:
249 FMRadioSeekDirection mDirection;
250 };
251
252 void
253 FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
254 FMRadioState aState)
255 {
256 if (mPendingRequest) {
257 mPendingRequest->SetReply(aResponse);
258 NS_DispatchToMainThread(mPendingRequest);
259 }
260
261 SetState(aState);
262 }
263
264 void
265 FMRadioService::SetState(FMRadioState aState)
266 {
267 mState = aState;
268 mPendingRequest = nullptr;
269 }
270
271 void
272 FMRadioService::AddObserver(FMRadioEventObserver* aObserver)
273 {
274 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
275 mObserverList.AddObserver(aObserver);
276 }
277
278 void
279 FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
280 {
281 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
282 mObserverList.RemoveObserver(aObserver);
283
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 }
292
293 void
294 FMRadioService::EnableAudio(bool aAudioEnabled)
295 {
296 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
297
298 nsCOMPtr<nsIAudioManager> audioManager =
299 do_GetService("@mozilla.org/telephony/audiomanager;1");
300 if (!audioManager) {
301 return;
302 }
303
304 bool audioEnabled;
305 audioManager->GetFmRadioAudioEnabled(&audioEnabled);
306 if (audioEnabled != aAudioEnabled) {
307 audioManager->SetFmRadioAudioEnabled(aAudioEnabled);
308 }
309 }
310
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;
327
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 }
334
335 int32_t partToBeRounded = round(aFrequencyInMHz * 1000) - mLowerBoundInKHz;
336 int32_t roundedPart = round(partToBeRounded / (double)mChannelWidthInKHz) *
337 mChannelWidthInKHz;
338
339 return mLowerBoundInKHz + roundedPart;
340 }
341
342 bool
343 FMRadioService::IsEnabled() const
344 {
345 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
346 return IsFMRadioOn();
347 }
348
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 }
357
358 return 0;
359 }
360
361 double
362 FMRadioService::GetFrequencyUpperBound() const
363 {
364 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
365 return mUpperBoundInKHz / 1000.0;
366 }
367
368 double
369 FMRadioService::GetFrequencyLowerBound() const
370 {
371 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
372 return mLowerBoundInKHz / 1000.0;
373 }
374
375 double
376 FMRadioService::GetChannelWidth() const
377 {
378 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
379 return mChannelWidthInKHz / 1000.0;
380 }
381
382 void
383 FMRadioService::Enable(double aFrequencyInMHz,
384 FMRadioReplyRunnable* aReplyRunnable)
385 {
386 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
387 MOZ_ASSERT(aReplyRunnable);
388
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 }
409
410 int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
411
412 if (!roundedFrequency) {
413 aReplyRunnable->SetReply(ErrorResponse(
414 NS_LITERAL_STRING("Frequency is out of range")));
415 NS_DispatchToMainThread(aReplyRunnable);
416 return;
417 }
418
419 if (mHasReadAirplaneModeSetting && mAirplaneModeEnabled) {
420 aReplyRunnable->SetReply(ErrorResponse(
421 NS_LITERAL_STRING("Airplane mode currently enabled")));
422 NS_DispatchToMainThread(aReplyRunnable);
423 return;
424 }
425
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;
430
431 // Cache the frequency value, and set it after the FM radio HW is enabled
432 mPendingFrequencyInKHz = roundedFrequency;
433
434 if (!mHasReadAirplaneModeSetting) {
435 nsCOMPtr<nsISettingsService> settings =
436 do_GetService("@mozilla.org/settingsService;1");
437
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 }
445
446 nsRefPtr<ReadAirplaneModeSettingTask> callback =
447 new ReadAirplaneModeSettingTask(mPendingRequest);
448
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 }
454
455 return;
456 }
457
458 NS_DispatchToMainThread(new EnableRunnable(mUpperBoundInKHz,
459 mLowerBoundInKHz,
460 mChannelWidthInKHz));
461 }
462
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!");
470
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 }
491
492 nsRefPtr<FMRadioReplyRunnable> enablingRequest = mPendingRequest;
493
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 }
500
501 FMRadioState preState = mState;
502 SetState(Disabling);
503 mPendingRequest = aReplyRunnable;
504
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);
512
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);
517
518 if (aReplyRunnable) {
519 aReplyRunnable->SetReply(SuccessResponse());
520 NS_DispatchToMainThread(aReplyRunnable);
521 }
522 }
523
524 return;
525 }
526
527 DoDisable();
528 }
529
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 }
542
543 void
544 FMRadioService::SetFrequency(double aFrequencyInMHz,
545 FMRadioReplyRunnable* aReplyRunnable)
546 {
547 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
548 MOZ_ASSERT(aReplyRunnable);
549
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 }
574
575 int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
576
577 if (!roundedFrequency) {
578 aReplyRunnable->SetReply(ErrorResponse(
579 NS_LITERAL_STRING("Frequency is out of range")));
580 NS_DispatchToMainThread(aReplyRunnable);
581 return;
582 }
583
584 NS_DispatchToMainThread(new SetFrequencyRunnable(roundedFrequency));
585
586 aReplyRunnable->SetReply(SuccessResponse());
587 NS_DispatchToMainThread(aReplyRunnable);
588 }
589
590 void
591 FMRadioService::Seek(FMRadioSeekDirection aDirection,
592 FMRadioReplyRunnable* aReplyRunnable)
593 {
594 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
595 MOZ_ASSERT(aReplyRunnable);
596
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 }
621
622 SetState(Seeking);
623 mPendingRequest = aReplyRunnable;
624
625 NS_DispatchToMainThread(new SeekRunnable(aDirection));
626 }
627
628 void
629 FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
630 {
631 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
632 MOZ_ASSERT(aReplyRunnable);
633
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 }
641
642 // Cancel the seek immediately to prevent it from completing.
643 CancelFMRadioSeek();
644
645 TransitionState(
646 ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
647
648 aReplyRunnable->SetReply(SuccessResponse());
649 NS_DispatchToMainThread(aReplyRunnable);
650 }
651
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);
659
660 if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
661 return NS_OK;
662 }
663
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 }
674
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 }
682
683 JS::Rooted<JSString*> jsKey(cx, key.toString());
684 nsDependentJSString keyStr;
685 if (!keyStr.init(cx, jsKey)) {
686 return NS_OK;
687 }
688
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 }
695
696 if (!value.isBoolean()) {
697 return NS_OK;
698 }
699
700 mAirplaneModeEnabled = value.toBoolean();
701 mHasReadAirplaneModeSetting = true;
702
703 // Disable the FM radio HW if Airplane mode is enabled.
704 if (mAirplaneModeEnabled) {
705 Disable(nullptr);
706 }
707 }
708
709 return NS_OK;
710 }
711
712 void
713 FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
714 {
715 mObserverList.Broadcast(aType);
716 }
717
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);
725
726 // If we're disabling, disable the radio right now.
727 if (mState == Disabling) {
728 DoDisable();
729 return;
730 }
731
732 // Fire success callback on the enable request.
733 TransitionState(SuccessResponse(), Enabled);
734
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);
738
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);
744
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();
750
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);
757
758 TransitionState(SuccessResponse(), Disabled);
759 UpdatePowerState();
760 break;
761 case FM_RADIO_OPERATION_SEEK:
762
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 }
768
769 UpdateFrequency();
770 break;
771 case FM_RADIO_OPERATION_TUNE:
772 UpdateFrequency();
773 break;
774 default:
775 MOZ_CRASH();
776 }
777 }
778
779 void
780 FMRadioService::UpdatePowerState()
781 {
782 bool enabled = IsFMRadioOn();
783 if (enabled != mEnabled) {
784 mEnabled = enabled;
785 NotifyFMRadioEvent(EnabledChanged);
786 }
787 }
788
789 void
790 FMRadioService::UpdateFrequency()
791 {
792 int32_t frequency = GetFMRadioFrequency();
793 if (mPendingFrequencyInKHz != frequency) {
794 mPendingFrequencyInKHz = frequency;
795 NotifyFMRadioEvent(FrequencyChanged);
796 }
797 }
798
799 // static
800 FMRadioService*
801 FMRadioService::Singleton()
802 {
803 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
804 MOZ_ASSERT(NS_IsMainThread());
805
806 if (!sFMRadioService) {
807 sFMRadioService = new FMRadioService();
808 }
809
810 return sFMRadioService;
811 }
812
813 NS_IMPL_ISUPPORTS(FMRadioService, nsIObserver)
814
815 END_FMRADIO_NAMESPACE
816

mercurial