Wed, 31 Dec 2014 06:09:35 +0100
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 "base/basictypes.h"
9 #include "BluetoothHfpManager.h"
10 #include "BluetoothProfileController.h"
11 #include "BluetoothUtils.h"
13 #include "jsapi.h"
14 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/StaticPtr.h"
17 #include "nsContentUtils.h"
18 #include "nsIAudioManager.h"
19 #include "nsIDOMIccInfo.h"
20 #include "nsIDOMMobileConnection.h"
21 #include "nsIIccProvider.h"
22 #include "nsIMobileConnectionProvider.h"
23 #include "nsIObserverService.h"
24 #include "nsISettingsService.h"
25 #include "nsITelephonyProvider.h"
26 #include "nsRadioInterfaceLayer.h"
27 #include "nsServiceManagerUtils.h"
28 #include "nsThreadUtils.h"
30 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
31 #define AUDIO_VOLUME_BT_SCO_ID "audio.volume.bt_sco"
33 /**
34 * Dispatch task with arguments to main thread.
35 */
36 #define BT_HF_DISPATCH_MAIN(args...) \
37 NS_DispatchToMainThread(new MainThreadTask(args))
39 /**
40 * Process bluedroid callbacks with corresponding handlers.
41 */
42 #define BT_HF_PROCESS_CB(func, args...) \
43 do { \
44 NS_ENSURE_TRUE_VOID(sBluetoothHfpManager); \
45 sBluetoothHfpManager->func(args); \
46 } while(0)
48 using namespace mozilla;
49 using namespace mozilla::ipc;
50 USING_BLUETOOTH_NAMESPACE
52 namespace {
53 StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
54 static const bthf_interface_t* sBluetoothHfpInterface = nullptr;
56 bool sInShutdown = false;
58 // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a
59 // magic number. The mechanism should be revised once we can get call history.
60 static int sWaitingForDialingInterval = 2000; //unit: ms
62 // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the
63 // time window set in Dialer and the extra '0.7' second is a magic number.
64 // The mechanism should be revised once we know the exact time at which
65 // Dialer stops playing.
66 static int sBusyToneInterval = 3700; //unit: ms
67 } // anonymous namespace
69 // Main thread task commands
70 enum MainThreadTaskCmd {
71 NOTIFY_CONN_STATE_CHANGED,
72 NOTIFY_DIALER,
73 NOTIFY_SCO_VOLUME_CHANGED,
74 POST_TASK_RESPOND_TO_BLDN,
75 POST_TASK_CLOSE_SCO
76 };
78 static void
79 ConnectionStateCallback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr)
80 {
81 BT_HF_PROCESS_CB(ProcessConnectionState, state, bd_addr);
82 }
84 static void
85 AudioStateCallback(bthf_audio_state_t state, bt_bdaddr_t* bd_addr)
86 {
87 BT_HF_PROCESS_CB(ProcessAudioState, state, bd_addr);
88 }
90 static void
91 VoiceRecognitionCallback(bthf_vr_state_t state)
92 {
93 // No support
94 }
96 static void
97 AnswerCallCallback()
98 {
99 BT_HF_PROCESS_CB(ProcessAnswerCall);
100 }
102 static void
103 HangupCallCallback()
104 {
105 BT_HF_PROCESS_CB(ProcessHangupCall);
106 }
108 static void
109 VolumeControlCallback(bthf_volume_type_t type, int volume)
110 {
111 BT_HF_PROCESS_CB(ProcessVolumeControl, type, volume);
112 }
114 static void
115 DialCallCallback(char *number)
116 {
117 BT_HF_PROCESS_CB(ProcessDialCall, number);
118 }
120 static void
121 DtmfCmdCallback(char dtmf)
122 {
123 BT_HF_PROCESS_CB(ProcessDtmfCmd, dtmf);
124 }
126 static void
127 NoiceReductionCallback(bthf_nrec_t nrec)
128 {
129 // No support
130 }
132 static void
133 AtChldCallback(bthf_chld_type_t chld)
134 {
135 BT_HF_PROCESS_CB(ProcessAtChld, chld);
136 }
138 static void
139 AtCnumCallback()
140 {
141 BT_HF_PROCESS_CB(ProcessAtCnum);
142 }
144 static void
145 AtCindCallback()
146 {
147 BT_HF_PROCESS_CB(ProcessAtCind);
148 }
150 static void
151 AtCopsCallback()
152 {
153 BT_HF_PROCESS_CB(ProcessAtCops);
154 }
156 static void
157 AtClccCallback()
158 {
159 BT_HF_PROCESS_CB(ProcessAtClcc);
160 }
162 static void
163 UnknownAtCallback(char *at_string)
164 {
165 BT_HF_PROCESS_CB(ProcessUnknownAt, at_string);
166 }
168 static void
169 KeyPressedCallback()
170 {
171 BT_HF_PROCESS_CB(ProcessKeyPressed);
172 }
174 static bthf_callbacks_t sBluetoothHfpCallbacks = {
175 sizeof(sBluetoothHfpCallbacks),
176 ConnectionStateCallback,
177 AudioStateCallback,
178 VoiceRecognitionCallback,
179 AnswerCallCallback,
180 HangupCallCallback,
181 VolumeControlCallback,
182 DialCallCallback,
183 DtmfCmdCallback,
184 NoiceReductionCallback,
185 AtChldCallback,
186 AtCnumCallback,
187 AtCindCallback,
188 AtCopsCallback,
189 AtClccCallback,
190 UnknownAtCallback,
191 KeyPressedCallback
192 };
194 static bool
195 IsValidDtmf(const char aChar) {
196 // Valid DTMF: [*#0-9ABCD]
197 return (aChar == '*' || aChar == '#') ||
198 (aChar >= '0' && aChar <= '9') ||
199 (aChar >= 'A' && aChar <= 'D');
200 }
202 class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
203 {
204 public:
205 NS_DECL_ISUPPORTS
207 NS_IMETHOD
208 Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
209 {
210 MOZ_ASSERT(NS_IsMainThread());
212 JSContext *cx = nsContentUtils::GetCurrentJSContext();
213 NS_ENSURE_TRUE(cx, NS_OK);
215 if (!aResult.isNumber()) {
216 BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!");
217 return NS_OK;
218 }
220 BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
221 hfp->mCurrentVgs = aResult.toNumber();
223 return NS_OK;
224 }
226 NS_IMETHOD
227 HandleError(const nsAString& aName)
228 {
229 BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'");
230 return NS_OK;
231 }
232 };
234 class BluetoothHfpManager::CloseScoTask : public Task
235 {
236 private:
237 void Run() MOZ_OVERRIDE
238 {
239 MOZ_ASSERT(sBluetoothHfpManager);
240 sBluetoothHfpManager->DisconnectSco();
241 }
242 };
244 class BluetoothHfpManager::RespondToBLDNTask : public Task
245 {
246 private:
247 void Run() MOZ_OVERRIDE
248 {
249 MOZ_ASSERT(sBluetoothHfpManager);
251 if (!sBluetoothHfpManager->mDialingRequestProcessed) {
252 sBluetoothHfpManager->mDialingRequestProcessed = true;
253 sBluetoothHfpManager->SendResponse(BTHF_AT_RESPONSE_ERROR);
254 }
255 }
256 };
258 class BluetoothHfpManager::MainThreadTask : public nsRunnable
259 {
260 public:
261 MainThreadTask(const int aCommand,
262 const nsAString& aParameter = EmptyString())
263 : mCommand(aCommand), mParameter(aParameter)
264 {
265 }
267 nsresult Run()
268 {
269 MOZ_ASSERT(NS_IsMainThread());
270 MOZ_ASSERT(sBluetoothHfpManager);
272 switch (mCommand) {
273 case MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED:
274 sBluetoothHfpManager->NotifyConnectionStateChanged(mParameter);
275 break;
276 case MainThreadTaskCmd::NOTIFY_DIALER:
277 sBluetoothHfpManager->NotifyDialer(mParameter);
278 break;
279 case MainThreadTaskCmd::NOTIFY_SCO_VOLUME_CHANGED:
280 {
281 nsCOMPtr<nsIObserverService> os =
282 mozilla::services::GetObserverService();
283 NS_ENSURE_TRUE(os, NS_OK);
285 os->NotifyObservers(nullptr, "bluetooth-volume-change",
286 mParameter.get());
287 }
288 break;
289 case MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN:
290 MessageLoop::current()->
291 PostDelayedTask(FROM_HERE, new RespondToBLDNTask(),
292 sWaitingForDialingInterval);
293 break;
294 case MainThreadTaskCmd::POST_TASK_CLOSE_SCO:
295 MessageLoop::current()->
296 PostDelayedTask(FROM_HERE, new CloseScoTask(),
297 sBusyToneInterval);
298 break;
299 default:
300 BT_WARNING("MainThreadTask: Unknown command %d", mCommand);
301 break;
302 }
304 return NS_OK;
305 }
307 private:
308 int mCommand;
309 nsString mParameter;
310 };
312 NS_IMPL_ISUPPORTS(BluetoothHfpManager::GetVolumeTask,
313 nsISettingsServiceCallback);
315 /**
316 * Call
317 */
318 Call::Call()
319 {
320 Reset();
321 }
323 void
324 Call::Set(const nsAString& aNumber, const bool aIsOutgoing)
325 {
326 mNumber = aNumber;
327 mDirection = (aIsOutgoing) ? BTHF_CALL_DIRECTION_OUTGOING :
328 BTHF_CALL_DIRECTION_INCOMING;
329 // Same logic as implementation in ril_worker.js
330 if (aNumber.Length() && aNumber[0] == '+') {
331 mType = BTHF_CALL_ADDRTYPE_INTERNATIONAL;
332 }
333 }
335 void
336 Call::Reset()
337 {
338 mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
339 mDirection = BTHF_CALL_DIRECTION_OUTGOING;
340 mNumber.Truncate();
341 mType = BTHF_CALL_ADDRTYPE_UNKNOWN;
342 }
344 bool
345 Call::IsActive()
346 {
347 return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
348 }
350 /**
351 * BluetoothHfpManager
352 */
353 BluetoothHfpManager::BluetoothHfpManager() : mPhoneType(PhoneType::NONE)
354 {
355 Reset();
356 }
358 void
359 BluetoothHfpManager::ResetCallArray()
360 {
361 mCurrentCallArray.Clear();
362 // Append a call object at the beginning of mCurrentCallArray since call
363 // index from RIL starts at 1.
364 Call call;
365 mCurrentCallArray.AppendElement(call);
367 if (mPhoneType == PhoneType::CDMA) {
368 mCdmaSecondCall.Reset();
369 }
370 }
372 void
373 BluetoothHfpManager::Reset()
374 {
375 mReceiveVgsFlag = false;
376 mDialingRequestProcessed = true;
378 mConnectionState = BTHF_CONNECTION_STATE_DISCONNECTED;
379 mPrevConnectionState = BTHF_CONNECTION_STATE_DISCONNECTED;
380 mAudioState = BTHF_AUDIO_STATE_DISCONNECTED;
382 // Phone & Device CIND
383 ResetCallArray();
384 mBattChg = 5;
385 mService = 0;
386 mRoam = 0;
387 mSignal = 0;
389 mController = nullptr;
390 }
392 bool
393 BluetoothHfpManager::Init()
394 {
395 MOZ_ASSERT(NS_IsMainThread());
397 NS_ENSURE_TRUE(InitHfpInterface(), false);
399 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
400 NS_ENSURE_TRUE(obs, false);
402 if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) ||
403 NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
404 BT_WARNING("Failed to add observers!");
405 return false;
406 }
408 hal::RegisterBatteryObserver(this);
410 mListener = new BluetoothRilListener();
411 NS_ENSURE_TRUE(mListener->Listen(true), false);
413 nsCOMPtr<nsISettingsService> settings =
414 do_GetService("@mozilla.org/settingsService;1");
415 NS_ENSURE_TRUE(settings, false);
417 nsCOMPtr<nsISettingsServiceLock> settingsLock;
418 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
419 NS_ENSURE_SUCCESS(rv, false);
421 nsRefPtr<GetVolumeTask> callback = new GetVolumeTask();
422 rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback);
423 NS_ENSURE_SUCCESS(rv, false);
425 return true;
426 }
428 bool
429 BluetoothHfpManager::InitHfpInterface()
430 {
431 const bt_interface_t* btInf = GetBluetoothInterface();
432 NS_ENSURE_TRUE(btInf, false);
434 if (sBluetoothHfpInterface) {
435 sBluetoothHfpInterface->cleanup();
436 sBluetoothHfpInterface = nullptr;
437 }
439 bthf_interface_t *interface = (bthf_interface_t *)
440 btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID);
441 NS_ENSURE_TRUE(interface, false);
443 NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
444 interface->init(&sBluetoothHfpCallbacks), false);
445 sBluetoothHfpInterface = interface;
447 return true;
448 }
450 BluetoothHfpManager::~BluetoothHfpManager()
451 {
452 if (!mListener->Listen(false)) {
453 BT_WARNING("Failed to stop listening RIL");
454 }
455 mListener = nullptr;
457 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
458 NS_ENSURE_TRUE_VOID(obs);
460 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
461 NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) {
462 BT_WARNING("Failed to remove observers!");
463 }
465 hal::UnregisterBatteryObserver(this);
466 DeinitHfpInterface();
467 }
469 void
470 BluetoothHfpManager::DeinitHfpInterface()
471 {
472 NS_ENSURE_TRUE_VOID(GetBluetoothInterface());
474 if (sBluetoothHfpInterface) {
475 sBluetoothHfpInterface->cleanup();
476 sBluetoothHfpInterface = nullptr;
477 }
478 }
480 //static
481 BluetoothHfpManager*
482 BluetoothHfpManager::Get()
483 {
484 MOZ_ASSERT(NS_IsMainThread());
486 // If sBluetoothHfpManager already exists, exit early
487 if (sBluetoothHfpManager) {
488 return sBluetoothHfpManager;
489 }
491 // If we're in shutdown, don't create a new instance
492 NS_ENSURE_FALSE(sInShutdown, nullptr);
494 // Create a new instance, register, and return
495 BluetoothHfpManager* manager = new BluetoothHfpManager();
496 NS_ENSURE_TRUE(manager->Init(), nullptr);
498 sBluetoothHfpManager = manager;
499 return sBluetoothHfpManager;
500 }
502 NS_IMETHODIMP
503 BluetoothHfpManager::Observe(nsISupports* aSubject,
504 const char* aTopic,
505 const char16_t* aData)
506 {
507 if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
508 HandleVolumeChanged(nsDependentString(aData));
509 } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
510 HandleShutdown();
511 } else {
512 MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
513 return NS_ERROR_UNEXPECTED;
514 }
516 return NS_OK;
517 }
519 void
520 BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo)
521 {
522 // Range of battery level: [0, 1], double
523 // Range of CIND::BATTCHG: [0, 5], int
524 mBattChg = (int) ceil(aBatteryInfo.level() * 5.0);
525 UpdateDeviceCIND();
526 }
528 void
529 BluetoothHfpManager::ProcessConnectionState(bthf_connection_state_t aState,
530 bt_bdaddr_t* aBdAddress)
531 {
532 BT_LOGR("state %d", aState);
534 mPrevConnectionState = mConnectionState;
535 mConnectionState = aState;
537 if (aState == BTHF_CONNECTION_STATE_SLC_CONNECTED) {
538 BdAddressTypeToString(aBdAddress, mDeviceAddress);
539 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
540 NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
541 } else if (aState == BTHF_CONNECTION_STATE_DISCONNECTED) {
542 DisconnectSco();
543 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
544 NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
545 }
546 }
548 void
549 BluetoothHfpManager::ProcessAudioState(bthf_audio_state_t aState,
550 bt_bdaddr_t* aBdAddress)
551 {
552 BT_LOGR("state %d", aState);
554 mAudioState = aState;
556 if (aState == BTHF_AUDIO_STATE_CONNECTED ||
557 aState == BTHF_AUDIO_STATE_DISCONNECTED) {
558 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED,
559 NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
560 }
561 }
563 void
564 BluetoothHfpManager::ProcessAnswerCall()
565 {
566 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
567 NS_LITERAL_STRING("ATA"));
568 }
570 void
571 BluetoothHfpManager::ProcessHangupCall()
572 {
573 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
574 NS_LITERAL_STRING("CHUP"));
575 }
577 void
578 BluetoothHfpManager::ProcessVolumeControl(bthf_volume_type_t aType,
579 int aVolume)
580 {
581 NS_ENSURE_TRUE_VOID(aVolume >= 0 && aVolume <= 15);
583 if (aType == BTHF_VOLUME_TYPE_MIC) {
584 mCurrentVgm = aVolume;
585 } else if (aType == BTHF_VOLUME_TYPE_SPK) {
586 mReceiveVgsFlag = true;
588 if (aVolume == mCurrentVgs) {
589 // Keep current volume
590 return;
591 }
593 nsString data;
594 data.AppendInt(aVolume);
595 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_SCO_VOLUME_CHANGED, data);
596 }
597 }
599 void
600 BluetoothHfpManager::ProcessDtmfCmd(char aDtmf)
601 {
602 NS_ENSURE_TRUE_VOID(IsValidDtmf(aDtmf));
604 nsAutoCString message("VTS=");
605 message += aDtmf;
606 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
607 NS_ConvertUTF8toUTF16(message));
608 }
610 void
611 BluetoothHfpManager::ProcessAtChld(bthf_chld_type_t aChld)
612 {
613 nsAutoCString message("CHLD=");
614 message.AppendInt((int)aChld);
615 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
616 NS_ConvertUTF8toUTF16(message));
618 SendResponse(BTHF_AT_RESPONSE_OK);
619 }
621 void BluetoothHfpManager::ProcessDialCall(char *aNumber)
622 {
623 nsAutoCString message(aNumber);
625 // There are three cases based on aNumber,
626 // 1) Empty value: Redial, BLDN
627 // 2) >xxx: Memory dial, ATD>xxx
628 // 3) xxx: Normal dial, ATDxxx
629 // We need to respond OK/Error for dial requests for every case listed above,
630 // 1) and 2): Respond in either RespondToBLDNTask or
631 // HandleCallStateChanged()
632 // 3): Respond here
633 if (message.IsEmpty()) {
634 mDialingRequestProcessed = false;
635 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
636 NS_LITERAL_STRING("BLDN"));
637 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
638 } else if (message[0] == '>') {
639 mDialingRequestProcessed = false;
640 nsAutoCString newMsg("ATD");
641 newMsg += StringHead(message, message.Length() - 1);
642 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
643 NS_ConvertUTF8toUTF16(newMsg));
644 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
645 } else {
646 nsAutoCString newMsg("ATD");
647 newMsg += StringHead(message, message.Length() - 1);
648 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
649 NS_ConvertUTF8toUTF16(newMsg));
650 SendResponse(BTHF_AT_RESPONSE_OK);
651 }
652 }
654 void
655 BluetoothHfpManager::ProcessAtCnum()
656 {
657 if (!mMsisdn.IsEmpty()) {
658 nsAutoCString message("+CNUM: ,\"");
659 message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
660 message.AppendLiteral("\",");
661 message.AppendInt(BTHF_CALL_ADDRTYPE_UNKNOWN);
662 message.AppendLiteral(",,4");
664 SendLine(message.get());
665 }
667 SendResponse(BTHF_AT_RESPONSE_OK);
668 }
670 void
671 BluetoothHfpManager::ProcessAtCind()
672 {
673 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
675 int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
676 int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
678 bt_status_t status = sBluetoothHfpInterface->cind_response(
679 mService,
680 numActive,
681 numHeld,
682 ConvertToBthfCallState(GetCallSetupState()),
683 mSignal,
684 mRoam,
685 mBattChg);
686 NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
687 }
689 void
690 BluetoothHfpManager::ProcessAtCops()
691 {
692 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
693 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
694 sBluetoothHfpInterface->cops_response(
695 NS_ConvertUTF16toUTF8(mOperatorName).get()));
696 }
698 void
699 BluetoothHfpManager::ProcessAtClcc()
700 {
701 uint32_t callNumbers = mCurrentCallArray.Length();
702 uint32_t i;
703 for (i = 1; i < callNumbers; i++) {
704 SendCLCC(mCurrentCallArray[i], i);
705 }
707 if (!mCdmaSecondCall.mNumber.IsEmpty()) {
708 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
709 MOZ_ASSERT(i == 2);
711 SendCLCC(mCdmaSecondCall, 2);
712 }
714 SendResponse(BTHF_AT_RESPONSE_OK);
715 }
717 void
718 BluetoothHfpManager::ProcessUnknownAt(char *aAtString)
719 {
720 BT_LOGR("[%s]", aAtString);
722 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
723 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
724 sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_ERROR, 0));
725 }
727 void
728 BluetoothHfpManager::ProcessKeyPressed()
729 {
730 bool hasActiveCall =
731 (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED) > 0);
733 // Refer to AOSP HeadsetStateMachine.processKeyPressed
734 if (FindFirstCall(nsITelephonyProvider::CALL_STATE_INCOMING)
735 && !hasActiveCall) {
736 /**
737 * Bluetooth HSP spec 4.2.2
738 * There is an incoming call, notify Dialer to pick up the phone call
739 * and SCO will be established after we get the CallStateChanged event
740 * indicating the call is answered successfully.
741 */
742 ProcessAnswerCall();
743 } else if (hasActiveCall) {
744 if (!IsScoConnected()) {
745 /**
746 * Bluetooth HSP spec 4.3
747 * If there's no SCO, set up a SCO link.
748 */
749 ConnectSco();
750 } else {
751 /**
752 * Bluetooth HSP spec 4.5
753 * There are two ways to release SCO: sending CHUP to dialer or closing
754 * SCO socket directly. We notify dialer only if there is at least one
755 * active call.
756 */
757 ProcessHangupCall();
758 }
759 } else {
760 // BLDN
761 mDialingRequestProcessed = false;
762 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER,
763 NS_LITERAL_STRING("BLDN"));
764 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
765 }
766 }
768 void
769 BluetoothHfpManager::NotifyConnectionStateChanged(const nsAString& aType)
770 {
771 MOZ_ASSERT(NS_IsMainThread());
773 // Notify Gecko observers
774 nsCOMPtr<nsIObserverService> obs =
775 do_GetService("@mozilla.org/observer-service;1");
776 NS_ENSURE_TRUE_VOID(obs);
778 if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(),
779 mDeviceAddress.get()))) {
780 BT_WARNING("Failed to notify observsers!");
781 }
783 // Dispatch an event of status change
784 bool status;
785 nsAutoString eventName;
786 if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
787 status = IsConnected();
788 eventName.AssignLiteral(HFP_STATUS_CHANGED_ID);
789 } else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
790 status = IsScoConnected();
791 eventName.AssignLiteral(SCO_STATUS_CHANGED_ID);
792 } else {
793 MOZ_ASSERT(false);
794 return;
795 }
797 DispatchStatusChangedEvent(eventName, mDeviceAddress, status);
799 // Notify profile controller
800 if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
801 if (IsConnected()) {
802 MOZ_ASSERT(mListener);
804 // Enumerate current calls
805 mListener->EnumerateCalls();
807 OnConnect(EmptyString());
808 } else if (mConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) {
809 mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
810 if (mPrevConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) {
811 // Bug 979160: This implies the outgoing connection failure.
812 // When the outgoing hfp connection fails, state changes to disconnected
813 // state. Since bluedroid would not report connecting state, but only
814 // report connected/disconnected.
815 OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
816 } else {
817 OnDisconnect(EmptyString());
818 }
819 Reset();
820 }
821 }
822 }
824 void
825 BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
826 {
827 NS_NAMED_LITERAL_STRING(type, "bluetooth-dialer-command");
828 InfallibleTArray<BluetoothNamedValue> parameters;
830 BT_APPEND_NAMED_VALUE(parameters, "command", nsString(aCommand));
832 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
833 }
835 void
836 BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
837 {
838 MOZ_ASSERT(NS_IsMainThread());
840 // The string that we're interested in will be a JSON string that looks like:
841 // {"key":"volumeup", "value":10}
842 // {"key":"volumedown", "value":2}
843 JSContext* cx = nsContentUtils::GetSafeJSContext();
844 NS_ENSURE_TRUE_VOID(cx);
846 JS::Rooted<JS::Value> val(cx);
847 NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
848 NS_ENSURE_TRUE_VOID(val.isObject());
850 JS::Rooted<JSObject*> obj(cx, &val.toObject());
851 JS::Rooted<JS::Value> key(cx);
852 if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
853 return;
854 }
856 bool match;
857 if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
858 !match) {
859 return;
860 }
862 JS::Rooted<JS::Value> value(cx);
863 if (!JS_GetProperty(cx, obj, "value", &value) ||
864 !value.isNumber()) {
865 return;
866 }
868 mCurrentVgs = value.toNumber();
870 // Adjust volume by headset and we don't have to send volume back to headset
871 if (mReceiveVgsFlag) {
872 mReceiveVgsFlag = false;
873 return;
874 }
876 // Only send volume back when there's a connected headset
877 if (IsConnected()) {
878 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
879 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
880 sBluetoothHfpInterface->volume_control(BTHF_VOLUME_TYPE_SPK,
881 mCurrentVgs));
882 }
883 }
885 void
886 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
887 {
888 nsCOMPtr<nsIMobileConnectionProvider> connection =
889 do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
890 NS_ENSURE_TRUE_VOID(connection);
892 nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
893 connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
894 NS_ENSURE_TRUE_VOID(voiceInfo);
896 nsString type;
897 voiceInfo->GetType(type);
898 mPhoneType = GetPhoneType(type);
900 bool roaming;
901 voiceInfo->GetRoaming(&roaming);
902 mRoam = (roaming) ? 1 : 0;
904 // Service
905 nsString regState;
906 voiceInfo->GetState(regState);
907 mService = (regState.EqualsLiteral("registered")) ? 1 : 0;
909 // Signal
910 JSContext* cx = nsContentUtils::GetSafeJSContext();
911 NS_ENSURE_TRUE_VOID(cx);
912 JS::Rooted<JS::Value> value(cx);
913 voiceInfo->GetRelSignalStrength(&value);
914 NS_ENSURE_TRUE_VOID(value.isNumber());
915 mSignal = (int)ceil(value.toNumber() / 20.0);
917 UpdateDeviceCIND();
919 // Operator name
920 nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
921 voiceInfo->GetNetwork(getter_AddRefs(network));
922 NS_ENSURE_TRUE_VOID(network);
923 network->GetLongName(mOperatorName);
925 // According to GSM 07.07, "<format> indicates if the format is alphanumeric
926 // or numeric; long alphanumeric format can be upto 16 characters long and
927 // short format up to 8 characters (refer GSM MoU SE.13 [9])..."
928 // However, we found that the operator name may sometimes be longer than 16
929 // characters. After discussion, we decided to fix this here but not in RIL
930 // or modem.
931 //
932 // Please see Bug 871366 for more information.
933 if (mOperatorName.Length() > 16) {
934 BT_WARNING("The operator name was longer than 16 characters. We cut it.");
935 mOperatorName.Left(mOperatorName, 16);
936 }
937 }
939 void
940 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
941 {
942 nsCOMPtr<nsIIccProvider> icc =
943 do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
944 NS_ENSURE_TRUE_VOID(icc);
946 nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
947 icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
948 NS_ENSURE_TRUE_VOID(iccInfo);
950 nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
951 NS_ENSURE_TRUE_VOID(gsmIccInfo);
952 gsmIccInfo->GetMsisdn(mMsisdn);
953 }
955 void
956 BluetoothHfpManager::HandleShutdown()
957 {
958 MOZ_ASSERT(NS_IsMainThread());
959 sInShutdown = true;
960 Disconnect(nullptr);
961 DisconnectSco();
962 sBluetoothHfpManager = nullptr;
963 }
965 void
966 BluetoothHfpManager::SendCLCC(Call& aCall, int aIndex)
967 {
968 NS_ENSURE_TRUE_VOID(aCall.mState !=
969 nsITelephonyProvider::CALL_STATE_DISCONNECTED);
970 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
972 bthf_call_state_t callState = ConvertToBthfCallState(aCall.mState);
974 if (mPhoneType == PhoneType::CDMA && aIndex == 1 && aCall.IsActive()) {
975 callState = (mCdmaSecondCall.IsActive()) ? BTHF_CALL_STATE_HELD :
976 BTHF_CALL_STATE_ACTIVE;
977 }
979 if (callState == BTHF_CALL_STATE_INCOMING &&
980 FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
981 callState = BTHF_CALL_STATE_WAITING;
982 }
984 bt_status_t status = sBluetoothHfpInterface->clcc_response(
985 aIndex,
986 aCall.mDirection,
987 callState,
988 BTHF_CALL_TYPE_VOICE,
989 BTHF_CALL_MPTY_TYPE_SINGLE,
990 NS_ConvertUTF16toUTF8(aCall.mNumber).get(),
991 aCall.mType);
992 NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
993 }
995 void
996 BluetoothHfpManager::SendLine(const char* aMessage)
997 {
998 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
999 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
1000 sBluetoothHfpInterface->formatted_at_response(aMessage));
1001 }
1003 void
1004 BluetoothHfpManager::SendResponse(bthf_at_response_t aResponseCode)
1005 {
1006 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
1007 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
1008 sBluetoothHfpInterface->at_response(aResponseCode, 0));
1009 }
1011 void
1012 BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex)
1013 {
1014 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
1016 int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
1017 int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
1018 bthf_call_state_t callSetupState =
1019 ConvertToBthfCallState(GetCallSetupState());
1020 nsAutoCString number =
1021 NS_ConvertUTF16toUTF8(mCurrentCallArray[aCallIndex].mNumber);
1022 bthf_call_addrtype_t type = mCurrentCallArray[aCallIndex].mType;
1024 BT_LOGR("[%d] state %d => BTHF: active[%d] held[%d] setupstate[%d]",
1025 aCallIndex, mCurrentCallArray[aCallIndex].mState,
1026 numActive, numHeld, callSetupState);
1028 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
1029 sBluetoothHfpInterface->phone_state_change(
1030 numActive, numHeld, callSetupState, number.get(), type));
1031 }
1033 void
1034 BluetoothHfpManager::UpdateDeviceCIND()
1035 {
1036 NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
1037 NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
1038 sBluetoothHfpInterface->device_status_notification(
1039 (bthf_network_state_t) mService,
1040 (bthf_service_type_t) mRoam,
1041 mSignal,
1042 mBattChg));
1043 }
1045 uint32_t
1046 BluetoothHfpManager::FindFirstCall(uint16_t aState)
1047 {
1048 uint32_t callLength = mCurrentCallArray.Length();
1050 for (uint32_t i = 1; i < callLength; ++i) {
1051 if (mCurrentCallArray[i].mState == aState) {
1052 return i;
1053 }
1054 }
1056 return 0;
1057 }
1059 uint32_t
1060 BluetoothHfpManager::GetNumberOfCalls(uint16_t aState)
1061 {
1062 uint32_t num = 0;
1063 uint32_t callLength = mCurrentCallArray.Length();
1065 for (uint32_t i = 1; i < callLength; ++i) {
1066 if (mCurrentCallArray[i].mState == aState) {
1067 ++num;
1068 }
1069 }
1071 return num;
1072 }
1074 uint16_t
1075 BluetoothHfpManager::GetCallSetupState()
1076 {
1077 uint32_t callLength = mCurrentCallArray.Length();
1079 for (uint32_t i = 1; i < callLength; ++i) {
1080 switch (mCurrentCallArray[i].mState) {
1081 case nsITelephonyProvider::CALL_STATE_INCOMING:
1082 case nsITelephonyProvider::CALL_STATE_DIALING:
1083 case nsITelephonyProvider::CALL_STATE_ALERTING:
1084 return mCurrentCallArray[i].mState;
1085 default:
1086 break;
1087 }
1088 }
1090 return nsITelephonyProvider::CALL_STATE_DISCONNECTED;
1091 }
1093 bthf_call_state_t
1094 BluetoothHfpManager::ConvertToBthfCallState(int aCallState)
1095 {
1096 bthf_call_state_t state;
1098 // Refer to AOSP BluetoothPhoneService.convertCallState
1099 if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) {
1100 state = BTHF_CALL_STATE_INCOMING;
1101 } else if (aCallState == nsITelephonyProvider::CALL_STATE_DIALING) {
1102 state = BTHF_CALL_STATE_DIALING;
1103 } else if (aCallState == nsITelephonyProvider::CALL_STATE_ALERTING) {
1104 state = BTHF_CALL_STATE_ALERTING;
1105 } else if (aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED) {
1106 state = BTHF_CALL_STATE_ACTIVE;
1107 } else if (aCallState == nsITelephonyProvider::CALL_STATE_HELD) {
1108 state = BTHF_CALL_STATE_HELD;
1109 } else { // disconnected
1110 state = BTHF_CALL_STATE_IDLE;
1111 }
1113 return state;
1114 }
1116 bool
1117 BluetoothHfpManager::IsTransitionState(uint16_t aCallState, bool aIsConference)
1118 {
1119 /**
1120 * Regard this callstate change as during CHLD=2 transition state if
1121 * - the call becomes active, and numActive > 1
1122 * - the call becomes held, and numHeld > 1 or an incoming call exists
1123 *
1124 * TODO:
1125 * 1) handle CHLD=1 transition state
1126 * 2) handle conference call cases
1127 */
1128 if (!aIsConference) {
1129 switch (aCallState) {
1130 case nsITelephonyProvider::CALL_STATE_CONNECTED:
1131 return (GetNumberOfCalls(aCallState) > 1);
1132 case nsITelephonyProvider::CALL_STATE_HELD:
1133 return (GetNumberOfCalls(aCallState) > 1 ||
1134 FindFirstCall(nsITelephonyProvider::CALL_STATE_INCOMING));
1135 default:
1136 break;
1137 }
1138 }
1140 return false;
1141 }
1143 void
1144 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
1145 uint16_t aCallState,
1146 const nsAString& aError,
1147 const nsAString& aNumber,
1148 const bool aIsOutgoing,
1149 const bool aIsConference,
1150 bool aSend)
1151 {
1152 // aCallIndex can be UINT32_MAX for the pending outgoing call state update.
1153 // aCallIndex will be updated again after real call state changes. See Bug
1154 // 990467.
1155 if (aCallIndex == UINT32_MAX) {
1156 return;
1157 }
1159 // Update call state only
1160 while (aCallIndex >= mCurrentCallArray.Length()) {
1161 Call call;
1162 mCurrentCallArray.AppendElement(call);
1163 }
1164 mCurrentCallArray[aCallIndex].mState = aCallState;
1166 // Return if SLC is disconnected
1167 if (!IsConnected()) {
1168 return;
1169 }
1171 // Update call information besides call state
1172 mCurrentCallArray[aCallIndex].Set(aNumber, aIsOutgoing);
1174 // Notify bluedroid of phone state change if this
1175 // call state change is not during transition state
1176 if (!IsTransitionState(aCallState, aIsConference)) {
1177 UpdatePhoneCIND(aCallIndex);
1178 }
1180 switch (aCallState) {
1181 case nsITelephonyProvider::CALL_STATE_DIALING:
1182 // We've send Dialer a dialing request and this is the response.
1183 if (!mDialingRequestProcessed) {
1184 SendResponse(BTHF_AT_RESPONSE_OK);
1185 mDialingRequestProcessed = true;
1186 }
1187 break;
1188 case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
1189 // -1 is necessary because call 0 is an invalid (padding) call object.
1190 if (mCurrentCallArray.Length() - 1 ==
1191 GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) {
1192 // In order to let user hear busy tone via connected Bluetooth headset,
1193 // we postpone the timing of dropping SCO.
1194 if (aError.Equals(NS_LITERAL_STRING("BusyError"))) {
1195 // FIXME: UpdatePhoneCIND later since it causes SCO close but
1196 // Dialer is still playing busy tone via HF.
1197 BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_CLOSE_SCO);
1198 }
1200 ResetCallArray();
1201 }
1202 break;
1203 default:
1204 break;
1205 }
1206 }
1208 PhoneType
1209 BluetoothHfpManager::GetPhoneType(const nsAString& aType)
1210 {
1211 // FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
1212 if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
1213 aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
1214 aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
1215 aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
1216 return PhoneType::GSM;
1217 } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
1218 aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
1219 aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
1220 return PhoneType::CDMA;
1221 }
1223 return PhoneType::NONE;
1224 }
1226 void
1227 BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
1228 {
1229 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1231 // Always regard second call as incoming call since v1.2 RIL
1232 // doesn't support outgoing second call in CDMA.
1233 mCdmaSecondCall.Set(aNumber, false);
1235 // FIXME: check CDMA + bluedroid
1236 //UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
1237 }
1239 void
1240 BluetoothHfpManager::AnswerWaitingCall()
1241 {
1242 MOZ_ASSERT(NS_IsMainThread());
1243 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1245 // Pick up second call. First call is held now.
1246 mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED;
1247 // FIXME: check CDMA + bluedroid
1248 //UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
1250 //sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
1251 //SendCommand("+CIEV: ", CINDType::CALLHELD);
1252 }
1254 void
1255 BluetoothHfpManager::IgnoreWaitingCall()
1256 {
1257 MOZ_ASSERT(NS_IsMainThread());
1258 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1260 mCdmaSecondCall.Reset();
1261 // FIXME: check CDMA + bluedroid
1262 //UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
1263 }
1265 void
1266 BluetoothHfpManager::ToggleCalls()
1267 {
1268 MOZ_ASSERT(NS_IsMainThread());
1269 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1271 // Toggle acitve and held calls
1272 mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
1273 nsITelephonyProvider::CALL_STATE_HELD :
1274 nsITelephonyProvider::CALL_STATE_CONNECTED;
1275 }
1277 bool
1278 BluetoothHfpManager::ConnectSco()
1279 {
1280 MOZ_ASSERT(NS_IsMainThread());
1282 NS_ENSURE_TRUE(!sInShutdown, false);
1283 NS_ENSURE_TRUE(IsConnected() && !IsScoConnected(), false);
1284 NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
1286 bt_bdaddr_t deviceBdAddress;
1287 StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
1288 NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
1289 sBluetoothHfpInterface->connect_audio(&deviceBdAddress), false);
1291 return true;
1292 }
1294 bool
1295 BluetoothHfpManager::DisconnectSco()
1296 {
1297 NS_ENSURE_TRUE(IsScoConnected(), false);
1298 NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
1300 bt_bdaddr_t deviceBdAddress;
1301 StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
1302 NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
1303 sBluetoothHfpInterface->disconnect_audio(&deviceBdAddress), false);
1305 return true;
1306 }
1308 bool
1309 BluetoothHfpManager::IsScoConnected()
1310 {
1311 return (mAudioState == BTHF_AUDIO_STATE_CONNECTED);
1312 }
1314 bool
1315 BluetoothHfpManager::IsConnected()
1316 {
1317 return (mConnectionState == BTHF_CONNECTION_STATE_SLC_CONNECTED);
1318 }
1320 void
1321 BluetoothHfpManager::Connect(const nsAString& aDeviceAddress,
1322 BluetoothProfileController* aController)
1323 {
1324 MOZ_ASSERT(NS_IsMainThread());
1325 MOZ_ASSERT(aController && !mController);
1327 if (sInShutdown) {
1328 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1329 return;
1330 }
1332 if (!sBluetoothHfpInterface) {
1333 BT_LOGR("sBluetoothHfpInterface is null");
1334 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1335 return;
1336 }
1338 bt_bdaddr_t deviceBdAddress;
1339 StringToBdAddressType(aDeviceAddress, &deviceBdAddress);
1341 bt_status_t result = sBluetoothHfpInterface->connect(&deviceBdAddress);
1342 if (BT_STATUS_SUCCESS != result) {
1343 BT_LOGR("Failed to connect: %x", result);
1344 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
1345 return;
1346 }
1348 mDeviceAddress = aDeviceAddress;
1349 mController = aController;
1350 }
1352 void
1353 BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
1354 {
1355 MOZ_ASSERT(NS_IsMainThread());
1356 MOZ_ASSERT(!mController);
1358 if (!sBluetoothHfpInterface) {
1359 BT_LOGR("sBluetoothHfpInterface is null");
1360 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1361 return;
1362 }
1364 bt_bdaddr_t deviceBdAddress;
1365 StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
1367 bt_status_t result = sBluetoothHfpInterface->disconnect(&deviceBdAddress);
1368 if (BT_STATUS_SUCCESS != result) {
1369 BT_LOGR("Failed to disconnect: %x", result);
1370 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
1371 return;
1372 }
1374 mController = aController;
1375 }
1377 void
1378 BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
1379 {
1380 MOZ_ASSERT(NS_IsMainThread());
1382 /**
1383 * On the one hand, notify the controller that we've done for outbound
1384 * connections. On the other hand, we do nothing for inbound connections.
1385 */
1386 NS_ENSURE_TRUE_VOID(mController);
1388 mController->NotifyCompletion(aErrorStr);
1389 mController = nullptr;
1390 }
1392 void
1393 BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr)
1394 {
1395 MOZ_ASSERT(NS_IsMainThread());
1397 /**
1398 * On the one hand, notify the controller that we've done for outbound
1399 * connections. On the other hand, we do nothing for inbound connections.
1400 */
1401 NS_ENSURE_TRUE_VOID(mController);
1403 mController->NotifyCompletion(aErrorStr);
1404 mController = nullptr;
1405 }
1407 void
1408 BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
1409 {
1410 // Bluedroid handles this part
1411 MOZ_ASSERT(false);
1412 }
1414 void
1415 BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
1416 const nsAString& aServiceUuid,
1417 int aChannel)
1418 {
1419 // Bluedroid handles this part
1420 MOZ_ASSERT(false);
1421 }
1423 void
1424 BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress)
1425 {
1426 aDeviceAddress = mDeviceAddress;
1427 }
1429 NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver)