Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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"
11 #include "BluetoothProfileController.h"
12 #include "BluetoothReplyRunnable.h"
13 #include "BluetoothService.h"
14 #include "BluetoothSocket.h"
15 #include "BluetoothUtils.h"
16 #include "BluetoothUuid.h"
18 #include "jsapi.h"
19 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/StaticPtr.h"
22 #include "nsContentUtils.h"
23 #include "nsIObserverService.h"
24 #include "nsISettingsService.h"
25 #include "nsServiceManagerUtils.h"
27 #ifdef MOZ_B2G_RIL
28 #include "nsIDOMIccInfo.h"
29 #include "nsIDOMMobileConnection.h"
30 #include "nsIIccProvider.h"
31 #include "nsIMobileConnectionProvider.h"
32 #include "nsITelephonyProvider.h"
33 #include "nsRadioInterfaceLayer.h"
34 #endif
36 /**
37 * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
38 * Capabilities" in Bluetooth hands-free profile 1.6
39 */
40 #define BRSF_BIT_THREE_WAY_CALLING 1
41 #define BSRF_BIT_EC_NR_FUNCTION (1 << 1)
42 #define BRSF_BIT_VOICE_RECOGNITION (1 << 2)
43 #define BRSF_BIT_IN_BAND_RING_TONE (1 << 3)
44 #define BRSF_BIT_ATTACH_NUM_TO_VOICE_TAG (1 << 4)
45 #define BRSF_BIT_ABILITY_TO_REJECT_CALL (1 << 5)
46 #define BRSF_BIT_ENHANCED_CALL_STATUS (1 << 6)
47 #define BRSF_BIT_ENHANCED_CALL_CONTROL (1 << 7)
48 #define BRSF_BIT_EXTENDED_ERR_RESULT_CODES (1 << 8)
49 #define BRSF_BIT_CODEC_NEGOTIATION (1 << 9)
51 #ifdef MOZ_B2G_RIL
52 /**
53 * These constants are used in result code such as +CLIP and +CCWA. The value
54 * of these constants is the same as TOA_INTERNATIONAL/TOA_UNKNOWN defined in
55 * ril_consts.js
56 */
57 #define TOA_UNKNOWN 0x81
58 #define TOA_INTERNATIONAL 0x91
59 #endif
61 #define CR_LF "\xd\xa";
63 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
64 #define AUDIO_VOLUME_BT_SCO_ID "audio.volume.bt_sco"
66 #define RESPONSE_CIEV "+CIEV: "
67 #define RESPONSE_CIND "+CIND: "
68 #define RESPONSE_CLCC "+CLCC: "
69 #define RESPONSE_BRSF "+BRSF: "
70 #define RESPONSE_VGS "+VGS: "
71 #define RESPONSE_CME_ERROR "+CME ERROR: "
73 using namespace mozilla;
74 using namespace mozilla::ipc;
75 USING_BLUETOOTH_NAMESPACE
77 namespace {
78 StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
79 bool sInShutdown = false;
80 static const char kHfpCrlf[] = "\xd\xa";
82 #ifdef MOZ_B2G_RIL
83 // Sending ringtone related
84 static bool sStopSendingRingFlag = true;
85 static int sRingInterval = 3000; //unit: ms
87 // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a
88 // magic number. The mechanism should be revised once we can get call history.
89 static int sWaitingForDialingInterval = 2000; //unit: ms
91 // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the
92 // time window set in Dialer and the extra '0.7' second is a magic number.
93 // The mechanism should be revised once we know the exact time at which
94 // Dialer stops playing.
95 static int sBusyToneInterval = 3700; //unit: ms
96 #endif // MOZ_B2G_RIL
97 } // anonymous namespace
99 #ifdef MOZ_B2G_RIL
100 /* CallState for sCINDItems[CINDType::CALL].value
101 * - NO_CALL: there are no calls in progress
102 * - IN_PROGRESS: at least one call is in progress
103 */
104 enum CallState {
105 NO_CALL,
106 IN_PROGRESS
107 };
109 /* CallSetupState for sCINDItems[CINDType::CALLSETUP].value
110 * - NO_CALLSETUP: not currently in call set up
111 * - INCOMING: an incoming call process ongoing
112 * - OUTGOING: an outgoing call set up is ongoing
113 * - OUTGOING_ALERTING: remote party being alerted in an outgoing call
114 */
115 enum CallSetupState {
116 NO_CALLSETUP,
117 INCOMING,
118 OUTGOING,
119 OUTGOING_ALERTING
120 };
122 /* CallHeldState for sCINDItems[CINDType::CALLHELD].value
123 * - NO_CALLHELD: no calls held
124 * - ONHOLD_ACTIVE: both an active and a held call
125 * - ONHOLD_NOACTIVE: call on hold, no active call
126 */
127 enum CallHeldState {
128 NO_CALLHELD,
129 ONHOLD_ACTIVE,
130 ONHOLD_NOACTIVE
131 };
132 #endif // MOZ_B2G_RIL
134 typedef struct {
135 const char* name;
136 const char* range;
137 int value;
138 bool activated;
139 } CINDItem;
141 enum CINDType {
142 BATTCHG = 1,
143 #ifdef MOZ_B2G_RIL
144 CALL,
145 CALLHELD,
146 CALLSETUP,
147 SERVICE,
148 SIGNAL,
149 ROAM
150 #endif
151 };
153 static CINDItem sCINDItems[] = {
154 {},
155 {"battchg", "0-5", 5, true},
156 #ifdef MOZ_B2G_RIL
157 {"call", "0,1", CallState::NO_CALL, true},
158 {"callheld", "0-2", CallHeldState::NO_CALLHELD, true},
159 {"callsetup", "0-3", CallSetupState::NO_CALLSETUP, true},
160 {"service", "0,1", 0, true},
161 {"signal", "0-5", 0, true},
162 {"roam", "0,1", 0, true}
163 #endif
164 };
166 class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
167 {
168 public:
169 NS_DECL_ISUPPORTS
171 NS_IMETHOD
172 Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
173 {
174 MOZ_ASSERT(NS_IsMainThread());
176 JSContext *cx = nsContentUtils::GetCurrentJSContext();
177 NS_ENSURE_TRUE(cx, NS_OK);
179 if (!aResult.isNumber()) {
180 BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!");
181 return NS_OK;
182 }
184 BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
185 hfp->mCurrentVgs = aResult.toNumber();
187 return NS_OK;
188 }
190 NS_IMETHOD
191 HandleError(const nsAString& aName)
192 {
193 BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'");
194 return NS_OK;
195 }
196 };
198 NS_IMPL_ISUPPORTS(BluetoothHfpManager::GetVolumeTask,
199 nsISettingsServiceCallback);
201 NS_IMETHODIMP
202 BluetoothHfpManager::Observe(nsISupports* aSubject,
203 const char* aTopic,
204 const char16_t* aData)
205 {
206 if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
207 HandleVolumeChanged(nsDependentString(aData));
208 } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
209 HandleShutdown();
210 } else {
211 MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
212 return NS_ERROR_UNEXPECTED;
213 }
215 return NS_OK;
216 }
218 void
219 BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo)
220 {
221 // Range of battery level: [0, 1], double
222 // Range of CIND::BATTCHG: [0, 5], int
223 int level = ceil(aBatteryInfo.level() * 5.0);
224 if (level != sCINDItems[CINDType::BATTCHG].value) {
225 sCINDItems[CINDType::BATTCHG].value = level;
226 SendCommand(RESPONSE_CIEV, CINDType::BATTCHG);
227 }
228 }
230 #ifdef MOZ_B2G_RIL
231 class BluetoothHfpManager::RespondToBLDNTask : public Task
232 {
233 private:
234 void Run() MOZ_OVERRIDE
235 {
236 MOZ_ASSERT(sBluetoothHfpManager);
238 if (!sBluetoothHfpManager->mDialingRequestProcessed) {
239 sBluetoothHfpManager->mDialingRequestProcessed = true;
240 sBluetoothHfpManager->SendLine("ERROR");
241 }
242 }
243 };
245 class BluetoothHfpManager::SendRingIndicatorTask : public Task
246 {
247 public:
248 SendRingIndicatorTask(const nsAString& aNumber, int aType)
249 : mNumber(aNumber)
250 , mType(aType)
251 {
252 MOZ_ASSERT(NS_IsMainThread());
253 }
255 void Run() MOZ_OVERRIDE
256 {
257 MOZ_ASSERT(NS_IsMainThread());
259 // Stop sending RING indicator
260 if (sStopSendingRingFlag) {
261 return;
262 }
264 if (!sBluetoothHfpManager) {
265 BT_WARNING("BluetoothHfpManager no longer exists, cannot send ring!");
266 return;
267 }
269 nsAutoCString ringMsg("RING");
270 sBluetoothHfpManager->SendLine(ringMsg.get());
272 if (!mNumber.IsEmpty()) {
273 nsAutoCString clipMsg("+CLIP: \"");
274 clipMsg.Append(NS_ConvertUTF16toUTF8(mNumber).get());
275 clipMsg.AppendLiteral("\",");
276 clipMsg.AppendInt(mType);
277 sBluetoothHfpManager->SendLine(clipMsg.get());
278 }
280 MessageLoop::current()->
281 PostDelayedTask(FROM_HERE,
282 new SendRingIndicatorTask(mNumber, mType),
283 sRingInterval);
284 }
286 private:
287 nsString mNumber;
288 int mType;
289 };
290 #endif // MOZ_B2G_RIL
292 class BluetoothHfpManager::CloseScoTask : public Task
293 {
294 private:
295 void Run() MOZ_OVERRIDE
296 {
297 MOZ_ASSERT(sBluetoothHfpManager);
299 sBluetoothHfpManager->DisconnectSco();
300 }
301 };
303 #ifdef MOZ_B2G_RIL
304 static bool
305 IsValidDtmf(const char aChar) {
306 // Valid DTMF: [*#0-9ABCD]
307 if (aChar == '*' || aChar == '#') {
308 return true;
309 } else if (aChar >= '0' && aChar <= '9') {
310 return true;
311 } else if (aChar >= 'A' && aChar <= 'D') {
312 return true;
313 }
314 return false;
315 }
317 static bool
318 IsMandatoryIndicator(const CINDType aType) {
319 return (aType == CINDType::CALL) ||
320 (aType == CINDType::CALLHELD) ||
321 (aType == CINDType::CALLSETUP);
322 }
324 /**
325 * Call
326 */
327 Call::Call()
328 {
329 Reset();
330 }
332 void
333 Call::Reset()
334 {
335 mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
336 mDirection = false;
337 mIsConference = false;
338 mNumber.Truncate();
339 mType = TOA_UNKNOWN;
340 }
342 bool
343 Call::IsActive()
344 {
345 return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
346 }
347 #endif // MOZ_B2G_RIL
349 /**
350 * BluetoothHfpManager
351 */
352 BluetoothHfpManager::BluetoothHfpManager()
353 {
354 #ifdef MOZ_B2G_RIL
355 mPhoneType = PhoneType::NONE;
356 #endif // MOZ_B2G_RIL
358 Reset();
359 }
361 #ifdef MOZ_B2G_RIL
362 void
363 BluetoothHfpManager::ResetCallArray()
364 {
365 mCurrentCallArray.Clear();
366 // Append a call object at the beginning of mCurrentCallArray since call
367 // index from RIL starts at 1.
368 Call call;
369 mCurrentCallArray.AppendElement(call);
371 if (mPhoneType == PhoneType::CDMA) {
372 mCdmaSecondCall.Reset();
373 }
374 }
375 #endif // MOZ_B2G_RIL
377 void
378 BluetoothHfpManager::Reset()
379 {
380 #ifdef MOZ_B2G_RIL
381 sStopSendingRingFlag = true;
382 sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
383 sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
384 sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
385 #endif
386 for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
387 sCINDItems[i].activated = true;
388 }
390 #ifdef MOZ_B2G_RIL
391 mCCWA = false;
392 mCLIP = false;
393 mDialingRequestProcessed = true;
395 // We disable BSIR by default as it requires OEM implement BT SCO + SPEAKER
396 // output audio path in audio driver. OEM can enable BSIR by setting
397 // mBSIR=true here.
398 //
399 // Please see Bug 878728 for more information.
400 mBSIR = false;
402 ResetCallArray();
403 #endif
404 mCMEE = false;
405 mCMER = false;
406 mConnectScoRequest = false;
407 mSlcConnected = false;
408 mIsHsp = false;
409 mReceiveVgsFlag = false;
410 mController = nullptr;
411 }
413 bool
414 BluetoothHfpManager::Init()
415 {
416 MOZ_ASSERT(NS_IsMainThread());
418 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
419 NS_ENSURE_TRUE(obs, false);
421 if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) ||
422 NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
423 BT_WARNING("Failed to add observers!");
424 return false;
425 }
427 hal::RegisterBatteryObserver(this);
429 #ifdef MOZ_B2G_RIL
430 mListener = new BluetoothRilListener();
431 if (!mListener->Listen(true)) {
432 BT_WARNING("Failed to start listening RIL");
433 return false;
434 }
435 #endif
437 nsCOMPtr<nsISettingsService> settings =
438 do_GetService("@mozilla.org/settingsService;1");
439 NS_ENSURE_TRUE(settings, false);
441 nsCOMPtr<nsISettingsServiceLock> settingsLock;
442 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
443 NS_ENSURE_SUCCESS(rv, false);
445 nsRefPtr<GetVolumeTask> callback = new GetVolumeTask();
446 rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback);
447 NS_ENSURE_SUCCESS(rv, false);
449 Listen();
451 mScoSocket = new BluetoothSocket(this,
452 BluetoothSocketType::SCO,
453 true,
454 false);
455 mScoSocketStatus = mScoSocket->GetConnectionStatus();
456 ListenSco();
457 return true;
458 }
460 BluetoothHfpManager::~BluetoothHfpManager()
461 {
462 #ifdef MOZ_B2G_RIL
463 if (!mListener->Listen(false)) {
464 BT_WARNING("Failed to stop listening RIL");
465 }
466 mListener = nullptr;
467 #endif
469 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
470 NS_ENSURE_TRUE_VOID(obs);
472 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
473 NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) {
474 BT_WARNING("Failed to remove observers!");
475 }
477 hal::UnregisterBatteryObserver(this);
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 void
503 BluetoothHfpManager::NotifyConnectionStatusChanged(const nsAString& aType)
504 {
505 MOZ_ASSERT(NS_IsMainThread());
507 // Notify Gecko observers
508 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
509 NS_ENSURE_TRUE_VOID(obs);
511 if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(),
512 mDeviceAddress.get()))) {
513 BT_WARNING("Failed to notify observsers!");
514 }
516 // Dispatch an event of status change
517 bool status;
518 nsAutoString eventName;
519 if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
520 status = IsConnected();
521 eventName.AssignLiteral(HFP_STATUS_CHANGED_ID);
522 } else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
523 status = IsScoConnected();
524 eventName.AssignLiteral(SCO_STATUS_CHANGED_ID);
525 } else {
526 MOZ_ASSERT(false);
527 return;
528 }
530 DispatchStatusChangedEvent(eventName, mDeviceAddress, status);
531 }
533 #ifdef MOZ_B2G_RIL
534 void
535 BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
536 {
537 nsString type, name;
538 BluetoothValue v;
539 InfallibleTArray<BluetoothNamedValue> parameters;
540 type.AssignLiteral("bluetooth-dialer-command");
542 name.AssignLiteral("command");
543 v = nsString(aCommand);
544 parameters.AppendElement(BluetoothNamedValue(name, v));
546 if (!BroadcastSystemMessage(type, parameters)) {
547 BT_WARNING("Failed to broadcast system message to dialer");
548 }
549 }
550 #endif // MOZ_B2G_RIL
552 void
553 BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
554 {
555 MOZ_ASSERT(NS_IsMainThread());
557 // The string that we're interested in will be a JSON string that looks like:
558 // {"key":"volumeup", "value":10}
559 // {"key":"volumedown", "value":2}
561 JSContext* cx = nsContentUtils::GetSafeJSContext();
562 NS_ENSURE_TRUE_VOID(cx);
564 JS::Rooted<JS::Value> val(cx);
565 NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
566 NS_ENSURE_TRUE_VOID(val.isObject());
568 JS::Rooted<JSObject*> obj(cx, &val.toObject());
569 JS::Rooted<JS::Value> key(cx);
570 if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
571 return;
572 }
574 bool match;
575 if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
576 !match) {
577 return;
578 }
580 JS::Rooted<JS::Value> value(cx);
581 if (!JS_GetProperty(cx, obj, "value", &value)||
582 !value.isNumber()) {
583 return;
584 }
586 mCurrentVgs = value.toNumber();
588 // Adjust volume by headset and we don't have to send volume back to headset
589 if (mReceiveVgsFlag) {
590 mReceiveVgsFlag = false;
591 return;
592 }
594 // Only send volume back when there's a connected headset
595 if (IsConnected()) {
596 SendCommand(RESPONSE_VGS, mCurrentVgs);
597 }
598 }
600 #ifdef MOZ_B2G_RIL
601 void
602 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
603 {
604 nsCOMPtr<nsIMobileConnectionProvider> connection =
605 do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
606 NS_ENSURE_TRUE_VOID(connection);
608 nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
609 connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
610 NS_ENSURE_TRUE_VOID(voiceInfo);
612 nsString type;
613 voiceInfo->GetType(type);
614 mPhoneType = GetPhoneType(type);
616 bool roaming;
617 voiceInfo->GetRoaming(&roaming);
618 UpdateCIND(CINDType::ROAM, roaming);
620 nsString regState;
621 voiceInfo->GetState(regState);
622 bool service = regState.EqualsLiteral("registered");
623 if (service != sCINDItems[CINDType::SERVICE].value) {
624 // Notify BluetoothRilListener of service change
625 mListener->ServiceChanged(aClientId, service);
626 }
627 UpdateCIND(CINDType::SERVICE, service);
629 JSContext* cx = nsContentUtils::GetSafeJSContext();
630 NS_ENSURE_TRUE_VOID(cx);
631 JS::Rooted<JS::Value> value(cx);
632 voiceInfo->GetRelSignalStrength(&value);
633 NS_ENSURE_TRUE_VOID(value.isNumber());
634 uint8_t signal = ceil(value.toNumber() / 20.0);
635 UpdateCIND(CINDType::SIGNAL, signal);
637 /**
638 * Possible return values for mode are:
639 * - null (unknown): set mNetworkSelectionMode to 0 (auto)
640 * - automatic: set mNetworkSelectionMode to 0 (auto)
641 * - manual: set mNetworkSelectionMode to 1 (manual)
642 */
643 nsString mode;
644 connection->GetNetworkSelectionMode(aClientId, mode);
645 if (mode.EqualsLiteral("manual")) {
646 mNetworkSelectionMode = 1;
647 } else {
648 mNetworkSelectionMode = 0;
649 }
651 nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
652 voiceInfo->GetNetwork(getter_AddRefs(network));
653 NS_ENSURE_TRUE_VOID(network);
654 network->GetLongName(mOperatorName);
656 // According to GSM 07.07, "<format> indicates if the format is alphanumeric
657 // or numeric; long alphanumeric format can be upto 16 characters long and
658 // short format up to 8 characters (refer GSM MoU SE.13 [9])..."
659 // However, we found that the operator name may sometimes be longer than 16
660 // characters. After discussion, we decided to fix this here but not in RIL
661 // or modem.
662 //
663 // Please see Bug 871366 for more information.
664 if (mOperatorName.Length() > 16) {
665 BT_WARNING("The operator name was longer than 16 characters. We cut it.");
666 mOperatorName.Left(mOperatorName, 16);
667 }
668 }
670 void
671 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
672 {
673 nsCOMPtr<nsIIccProvider> icc =
674 do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
675 NS_ENSURE_TRUE_VOID(icc);
677 nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
678 icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
679 NS_ENSURE_TRUE_VOID(iccInfo);
681 nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
682 NS_ENSURE_TRUE_VOID(gsmIccInfo);
683 gsmIccInfo->GetMsisdn(mMsisdn);
684 }
685 #endif // MOZ_B2G_RIL
687 void
688 BluetoothHfpManager::HandleShutdown()
689 {
690 MOZ_ASSERT(NS_IsMainThread());
691 sInShutdown = true;
692 Disconnect(nullptr);
693 DisconnectSco();
694 sBluetoothHfpManager = nullptr;
695 }
697 // Virtual function of class SocketConsumer
698 void
699 BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
700 nsAutoPtr<UnixSocketRawData>& aMessage)
701 {
702 MOZ_ASSERT(NS_IsMainThread());
703 MOZ_ASSERT(aSocket);
705 nsAutoCString msg((const char*)aMessage->mData.get(), aMessage->mSize);
706 msg.StripWhitespace();
708 nsTArray<nsCString> atCommandValues;
710 // For more information, please refer to 4.34.1 "Bluetooth Defined AT
711 // Capabilities" in Bluetooth hands-free profile 1.6
712 if (msg.Find("AT+BRSF=") != -1) {
713 #ifdef MOZ_B2G_RIL
714 uint32_t brsf = BRSF_BIT_ABILITY_TO_REJECT_CALL |
715 BRSF_BIT_ENHANCED_CALL_STATUS;
717 // No support for three way calling in CDMA since
718 // CDMA disallows to hang existing call for CHLD=1
719 if (mPhoneType != PhoneType::CDMA) {
720 brsf |= BRSF_BIT_THREE_WAY_CALLING;
721 }
723 if (mBSIR) {
724 brsf |= BRSF_BIT_IN_BAND_RING_TONE;
725 }
726 #else
727 uint32_t brsf = 0;
728 #endif // MOZ_B2G_RIL
730 SendCommand(RESPONSE_BRSF, brsf);
731 } else if (msg.Find("AT+CIND=?") != -1) {
732 // Asking for CIND range
733 SendCommand(RESPONSE_CIND, 0);
734 } else if (msg.Find("AT+CIND?") != -1) {
735 // Asking for CIND value
736 SendCommand(RESPONSE_CIND, 1);
737 } else if (msg.Find("AT+CMER=") != -1) {
738 /**
739 * SLC establishment is done when AT+CMER has been received.
740 * Do nothing but respond with "OK".
741 */
742 ParseAtCommand(msg, 8, atCommandValues);
744 if (atCommandValues.Length() < 4) {
745 BT_WARNING("Could't get the value of command [AT+CMER=]");
746 goto respond_with_ok;
747 }
749 if (!atCommandValues[0].EqualsLiteral("3") ||
750 !atCommandValues[1].EqualsLiteral("0") ||
751 !atCommandValues[2].EqualsLiteral("0")) {
752 BT_WARNING("Wrong value of CMER");
753 goto respond_with_ok;
754 }
756 mCMER = atCommandValues[3].EqualsLiteral("1");
758 /**
759 * SLC is connected once the "indicator status update" is enabled by
760 * AT+CMER command. See 4.2.1 in Bluetooth hands-free profile 1.6
761 * for more details.
762 */
763 if (mCMER) {
764 mSlcConnected = true;
765 }
767 // If we get internal request for SCO connection,
768 // setup SCO after Service Level Connection established.
769 if (mConnectScoRequest) {
770 mConnectScoRequest = false;
771 ConnectSco();
772 }
773 } else if (msg.Find("AT+CMEE=") != -1) {
774 ParseAtCommand(msg, 8, atCommandValues);
776 if (atCommandValues.IsEmpty()) {
777 BT_WARNING("Could't get the value of command [AT+CMEE=]");
778 goto respond_with_ok;
779 }
781 // AT+CMEE = 0: +CME ERROR shall not be used
782 // AT+CMEE = 1: use numeric <err>
783 // AT+CMEE = 2: use verbose <err>
784 mCMEE = !atCommandValues[0].EqualsLiteral("0");
785 #ifdef MOZ_B2G_RIL
786 } else if (msg.Find("AT+COPS=") != -1) {
787 ParseAtCommand(msg, 8, atCommandValues);
789 if (atCommandValues.Length() != 2) {
790 BT_WARNING("Could't get the value of command [AT+COPS=]");
791 goto respond_with_ok;
792 }
794 // Handsfree only support AT+COPS=3,0
795 if (!atCommandValues[0].EqualsLiteral("3") ||
796 !atCommandValues[1].EqualsLiteral("0")) {
797 if (mCMEE) {
798 SendCommand(RESPONSE_CME_ERROR, BluetoothCmeError::OPERATION_NOT_SUPPORTED);
799 } else {
800 SendLine("ERROR");
801 }
802 return;
803 }
804 } else if (msg.Find("AT+COPS?") != -1) {
805 nsAutoCString message("+COPS: ");
806 message.AppendInt(mNetworkSelectionMode);
807 message.AppendLiteral(",0,\"");
808 message.Append(NS_ConvertUTF16toUTF8(mOperatorName));
809 message.AppendLiteral("\"");
810 SendLine(message.get());
811 } else if (msg.Find("AT+VTS=") != -1) {
812 ParseAtCommand(msg, 7, atCommandValues);
814 if (atCommandValues.Length() != 1) {
815 BT_WARNING("Couldn't get the value of command [AT+VTS=]");
816 goto respond_with_ok;
817 }
819 if (IsValidDtmf(atCommandValues[0].get()[0])) {
820 nsAutoCString message("VTS=");
821 message += atCommandValues[0].get()[0];
822 NotifyDialer(NS_ConvertUTF8toUTF16(message));
823 }
824 #endif // MOZ_B2G_RIL
825 } else if (msg.Find("AT+VGM=") != -1) {
826 ParseAtCommand(msg, 7, atCommandValues);
828 if (atCommandValues.IsEmpty()) {
829 BT_WARNING("Couldn't get the value of command [AT+VGM]");
830 goto respond_with_ok;
831 }
833 nsresult rv;
834 int vgm = atCommandValues[0].ToInteger(&rv);
835 if (NS_FAILED(rv)) {
836 BT_WARNING("Failed to extract microphone volume from bluetooth headset!");
837 goto respond_with_ok;
838 }
840 if (vgm < 0 || vgm > 15) {
841 BT_WARNING("Received invalid VGM value");
842 goto respond_with_ok;
843 }
845 mCurrentVgm = vgm;
846 #ifdef MOZ_B2G_RIL
847 } else if (msg.Find("AT+CHLD=?") != -1) {
848 SendLine("+CHLD: (0,1,2,3)");
849 } else if (msg.Find("AT+CHLD=") != -1) {
850 ParseAtCommand(msg, 8, atCommandValues);
852 if (atCommandValues.IsEmpty()) {
853 BT_WARNING("Could't get the value of command [AT+CHLD=]");
854 goto respond_with_ok;
855 }
857 /**
858 * The following three cases are supported:
859 * AT+CHLD=0 - Releases all held calls or sets User Determined User Busy
860 * (UDUB) for a waiting call
861 * AT+CHLD=1 - Releases active calls and accepts the other (held or
862 * waiting) call
863 * AT+CHLD=2 - Places active calls on hold and accepts the other (held
864 * or waiting) call
865 * AT+CHLD=3 - Adds a held call to the conversation.
866 *
867 * The following cases are NOT supported yet:
868 * AT+CHLD=1<idx>, AT+CHLD=2<idx>, AT+CHLD=4
869 * Please see 4.33.2 in Bluetooth hands-free profile 1.6 for more
870 * information.
871 */
872 char chld = atCommandValues[0][0];
873 bool valid = true;
874 if (atCommandValues[0].Length() > 1) {
875 BT_WARNING("No index should be included in command [AT+CHLD]");
876 valid = false;
877 } else if (chld == '4') {
878 BT_WARNING("The value of command [AT+CHLD] is not supported");
879 valid = false;
880 } else if (chld == '0') {
881 // We need to rename these dialer commands for better readability
882 // and expandability.
883 // See bug 884190 for more information.
884 NotifyDialer(NS_LITERAL_STRING("CHLD=0"));
885 } else if (chld == '1') {
886 NotifyDialer(NS_LITERAL_STRING("CHLD=1"));
887 } else if (chld == '2') {
888 NotifyDialer(NS_LITERAL_STRING("CHLD=2"));
889 } else if (chld == '3') {
890 NotifyDialer(NS_LITERAL_STRING("CHLD=3"));
891 } else {
892 BT_WARNING("Wrong value of command [AT+CHLD]");
893 valid = false;
894 }
896 if (!valid) {
897 SendLine("ERROR");
898 return;
899 }
900 #endif // MOZ_B2G_RIL
901 } else if (msg.Find("AT+VGS=") != -1) {
902 // Adjust volume by headset
903 mReceiveVgsFlag = true;
904 ParseAtCommand(msg, 7, atCommandValues);
906 if (atCommandValues.IsEmpty()) {
907 BT_WARNING("Could't get the value of command [AT+VGS=]");
908 goto respond_with_ok;
909 }
911 nsresult rv;
912 int newVgs = atCommandValues[0].ToInteger(&rv);
913 if (NS_FAILED(rv)) {
914 BT_WARNING("Failed to extract volume value from bluetooth headset!");
915 goto respond_with_ok;
916 }
918 if (newVgs == mCurrentVgs) {
919 goto respond_with_ok;
920 }
922 if (newVgs < 0 || newVgs > 15) {
923 BT_WARNING("Received invalid VGS value");
924 goto respond_with_ok;
925 }
927 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
928 if (!os) {
929 BT_WARNING("Failed to get observer service!");
930 goto respond_with_ok;
931 }
933 nsString data;
934 data.AppendInt(newVgs);
935 os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get());
936 #ifdef MOZ_B2G_RIL
937 } else if ((msg.Find("AT+BLDN") != -1) || (msg.Find("ATD>") != -1)) {
938 // Dialer app of FFOS v1 does not have plan to support Memory Dailing.
939 // However, in order to pass Bluetooth HFP certification, we still have to
940 // make a call when we receive AT command 'ATD>n'.
941 mDialingRequestProcessed = false;
943 if (msg.Find("AT+BLDN") != -1) {
944 NotifyDialer(NS_LITERAL_STRING("BLDN"));
945 } else {
946 NotifyDialer(NS_ConvertUTF8toUTF16(msg));
947 }
949 MessageLoop::current()->
950 PostDelayedTask(FROM_HERE, new RespondToBLDNTask(),
951 sWaitingForDialingInterval);
953 // Don't send response 'OK' here because we'll respond later in either
954 // RespondToBLDNTask or HandleCallStateChanged()
955 return;
956 } else if (msg.Find("ATA") != -1) {
957 NotifyDialer(NS_LITERAL_STRING("ATA"));
958 } else if (msg.Find("AT+CHUP") != -1) {
959 NotifyDialer(NS_LITERAL_STRING("CHUP"));
960 } else if (msg.Find("AT+CLCC") != -1) {
961 SendCommand(RESPONSE_CLCC);
962 } else if (msg.Find("ATD") != -1) {
963 nsAutoCString message(msg), newMsg;
964 int end = message.FindChar(';');
965 if (end < 0) {
966 BT_WARNING("Could't get the value of command [ATD]");
967 goto respond_with_ok;
968 }
970 newMsg += nsDependentCSubstring(message, 0, end);
971 NotifyDialer(NS_ConvertUTF8toUTF16(newMsg));
972 } else if (msg.Find("AT+CLIP=") != -1) {
973 ParseAtCommand(msg, 8, atCommandValues);
975 if (atCommandValues.IsEmpty()) {
976 BT_WARNING("Could't get the value of command [AT+CLIP=]");
977 goto respond_with_ok;
978 }
980 mCLIP = atCommandValues[0].EqualsLiteral("1");
981 } else if (msg.Find("AT+CCWA=") != -1) {
982 ParseAtCommand(msg, 8, atCommandValues);
984 if (atCommandValues.IsEmpty()) {
985 BT_WARNING("Could't get the value of command [AT+CCWA=]");
986 goto respond_with_ok;
987 }
989 mCCWA = atCommandValues[0].EqualsLiteral("1");
990 } else if (msg.Find("AT+CKPD") != -1) {
991 if (!sStopSendingRingFlag) {
992 // Bluetooth HSP spec 4.2.2
993 // There is an incoming call, notify Dialer to pick up the phone call
994 // and SCO will be established after we get the CallStateChanged event
995 // indicating the call is answered successfully.
996 NotifyDialer(NS_LITERAL_STRING("ATA"));
997 } else {
998 if (!IsScoConnected()) {
999 // Bluetooth HSP spec 4.3
1000 // If there's no SCO, set up a SCO link.
1001 ConnectSco();
1002 } else if (!mFirstCKPD) {
1003 // Bluetooth HSP spec 4.5
1004 // There are two ways to release SCO: sending CHUP to dialer or closing
1005 // SCO socket directly. We notify dialer only if there is at least one
1006 // active call.
1007 if (mCurrentCallArray.Length() > 1) {
1008 NotifyDialer(NS_LITERAL_STRING("CHUP"));
1009 } else {
1010 DisconnectSco();
1011 }
1012 } else {
1013 // Three conditions have to be matched to come in here:
1014 // (1) Not sending RING indicator
1015 // (2) A SCO link exists
1016 // (3) This is the very first AT+CKPD=200 of this session
1017 // It is the case of Figure 4.3, Bluetooth HSP spec. Do nothing.
1018 BT_WARNING("AT+CKPD=200: Do nothing");
1019 }
1020 }
1022 mFirstCKPD = false;
1023 } else if (msg.Find("AT+CNUM") != -1) {
1024 if (!mMsisdn.IsEmpty()) {
1025 nsAutoCString message("+CNUM: ,\"");
1026 message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
1027 message.AppendLiteral("\",");
1028 message.AppendInt(TOA_UNKNOWN);
1029 message.AppendLiteral(",,4");
1030 SendLine(message.get());
1031 }
1032 } else if (msg.Find("AT+BIA=") != -1) {
1033 ParseAtCommand(msg, 7, atCommandValues);
1035 for (uint8_t i = 0; i < atCommandValues.Length(); i++) {
1036 CINDType indicatorType = (CINDType) (i + 1);
1037 if (indicatorType >= (int)ArrayLength(sCINDItems)) {
1038 // Ignore excess parameters at the end
1039 break;
1040 }
1042 if (!IsMandatoryIndicator(indicatorType)) {
1043 /**
1044 * Accept only following indicator states:
1045 * - "1": activate
1046 * - "0": deactivate
1047 * - "" : maintain current state
1048 * Otherwise we regard the command incorrectly formatted.
1049 */
1050 if (atCommandValues[i].EqualsLiteral("1")) {
1051 sCINDItems[indicatorType].activated = 1;
1052 } else if (atCommandValues[i].EqualsLiteral("0")) {
1053 sCINDItems[indicatorType].activated = 0;
1054 } else if (!atCommandValues[i].EqualsLiteral("")) {
1055 SendLine("ERROR");
1056 return;
1057 }
1058 } else {
1059 // Ignore requests to activate/deactivate mandatory indicators
1060 }
1061 }
1062 #endif // MOZ_B2G_RIL
1063 } else {
1064 nsCString warningMsg;
1065 warningMsg.Append(NS_LITERAL_CSTRING("Unsupported AT command: "));
1066 warningMsg.Append(msg);
1067 warningMsg.Append(NS_LITERAL_CSTRING(", reply with ERROR"));
1068 BT_WARNING(warningMsg.get());
1070 SendLine("ERROR");
1071 return;
1072 }
1074 respond_with_ok:
1075 // We always respond to remote device with "OK" in general cases.
1076 SendLine("OK");
1077 }
1079 void
1080 BluetoothHfpManager::Connect(const nsAString& aDeviceAddress,
1081 BluetoothProfileController* aController)
1082 {
1083 MOZ_ASSERT(NS_IsMainThread());
1084 MOZ_ASSERT(aController && !mController);
1086 BluetoothService* bs = BluetoothService::Get();
1087 if (!bs || sInShutdown) {
1088 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1089 return;
1090 }
1092 if (mSocket) {
1093 if (mDeviceAddress == aDeviceAddress) {
1094 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
1095 } else {
1096 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_REACHED_CONNECTION_LIMIT));
1097 }
1098 return;
1099 }
1101 nsString uuid;
1102 BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid);
1104 if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
1105 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1106 return;
1107 }
1109 // Stop listening because currently we only support one connection at a time.
1110 if (mHandsfreeSocket) {
1111 mHandsfreeSocket->Disconnect();
1112 mHandsfreeSocket = nullptr;
1113 }
1115 if (mHeadsetSocket) {
1116 mHeadsetSocket->Disconnect();
1117 mHeadsetSocket = nullptr;
1118 }
1120 mController = aController;
1121 mSocket =
1122 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
1123 }
1125 bool
1126 BluetoothHfpManager::Listen()
1127 {
1128 MOZ_ASSERT(NS_IsMainThread());
1130 if (sInShutdown) {
1131 BT_WARNING("Listen called while in shutdown!");
1132 return false;
1133 }
1135 if (mSocket) {
1136 BT_WARNING("mSocket exists. Failed to listen.");
1137 return false;
1138 }
1140 if (!mHandsfreeSocket) {
1141 mHandsfreeSocket =
1142 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
1144 if (!mHandsfreeSocket->Listen(
1145 BluetoothReservedChannels::CHANNEL_HANDSFREE_AG)) {
1146 BT_WARNING("[HFP] Can't listen on RFCOMM socket!");
1147 mHandsfreeSocket = nullptr;
1148 return false;
1149 }
1150 }
1152 if (!mHeadsetSocket) {
1153 mHeadsetSocket =
1154 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
1156 if (!mHeadsetSocket->Listen(
1157 BluetoothReservedChannels::CHANNEL_HEADSET_AG)) {
1158 BT_WARNING("[HSP] Can't listen on RFCOMM socket!");
1159 mHandsfreeSocket->Disconnect();
1160 mHandsfreeSocket = nullptr;
1161 mHeadsetSocket = nullptr;
1162 return false;
1163 }
1164 }
1166 return true;
1167 }
1169 void
1170 BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
1171 {
1172 MOZ_ASSERT(NS_IsMainThread());
1174 if (!mSocket) {
1175 if (aController) {
1176 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
1177 }
1178 return;
1179 }
1181 MOZ_ASSERT(!mController);
1183 mController = aController;
1184 mSocket->Disconnect();
1185 }
1187 #ifdef MOZ_B2G_RIL
1188 void
1189 BluetoothHfpManager::SendCCWA(const nsAString& aNumber, int aType)
1190 {
1191 if (mCCWA) {
1192 nsAutoCString ccwaMsg("+CCWA: \"");
1193 ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
1194 ccwaMsg.AppendLiteral("\",");
1195 ccwaMsg.AppendInt(aType);
1196 SendLine(ccwaMsg.get());
1197 }
1198 }
1200 bool
1201 BluetoothHfpManager::SendCLCC(const Call& aCall, int aIndex)
1202 {
1203 if (aCall.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
1204 return true;
1205 }
1207 nsAutoCString message(RESPONSE_CLCC);
1208 message.AppendInt(aIndex);
1209 message.AppendLiteral(",");
1210 message.AppendInt(aCall.mDirection);
1211 message.AppendLiteral(",");
1213 int status = 0;
1214 switch (aCall.mState) {
1215 case nsITelephonyProvider::CALL_STATE_CONNECTED:
1216 if (mPhoneType == PhoneType::CDMA && aIndex == 1) {
1217 status = (mCdmaSecondCall.IsActive()) ? 1 : 0;
1218 }
1219 message.AppendInt(status);
1220 break;
1221 case nsITelephonyProvider::CALL_STATE_HELD:
1222 message.AppendInt(1);
1223 break;
1224 case nsITelephonyProvider::CALL_STATE_DIALING:
1225 message.AppendInt(2);
1226 break;
1227 case nsITelephonyProvider::CALL_STATE_ALERTING:
1228 message.AppendInt(3);
1229 break;
1230 case nsITelephonyProvider::CALL_STATE_INCOMING:
1231 if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
1232 message.AppendInt(4);
1233 } else {
1234 message.AppendInt(5);
1235 }
1236 break;
1237 default:
1238 BT_WARNING("Not handling call status for CLCC");
1239 break;
1240 }
1242 message.AppendLiteral(",0,0,\"");
1243 message.Append(NS_ConvertUTF16toUTF8(aCall.mNumber));
1244 message.AppendLiteral("\",");
1245 message.AppendInt(aCall.mType);
1247 return SendLine(message.get());
1248 }
1249 #endif // MOZ_B2G_RIL
1251 bool
1252 BluetoothHfpManager::SendLine(const char* aMessage)
1253 {
1254 MOZ_ASSERT(mSocket);
1256 nsAutoCString msg;
1258 msg.AppendLiteral(kHfpCrlf);
1259 msg.Append(aMessage);
1260 msg.AppendLiteral(kHfpCrlf);
1262 return mSocket->SendSocketData(msg);
1263 }
1265 bool
1266 BluetoothHfpManager::SendCommand(const char* aCommand, uint32_t aValue)
1267 {
1268 if (!IsConnected()) {
1269 BT_WARNING("Trying to SendCommand() without a SLC");
1270 return false;
1271 }
1273 nsAutoCString message;
1274 message += aCommand;
1276 if (!strcmp(aCommand, RESPONSE_CIEV)) {
1277 if (!mCMER || !sCINDItems[aValue].activated) {
1278 // Indicator status update is disabled
1279 return true;
1280 }
1282 if ((aValue < 1) || (aValue > ArrayLength(sCINDItems) - 1)) {
1283 BT_WARNING("unexpected CINDType for CIEV command");
1284 return false;
1285 }
1287 message.AppendInt(aValue);
1288 message.AppendLiteral(",");
1289 message.AppendInt(sCINDItems[aValue].value);
1290 } else if (!strcmp(aCommand, RESPONSE_CIND)) {
1291 if (!aValue) {
1292 // Query for range
1293 for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
1294 message.AppendLiteral("(\"");
1295 message.Append(sCINDItems[i].name);
1296 message.AppendLiteral("\",(");
1297 message.Append(sCINDItems[i].range);
1298 message.AppendLiteral(")");
1299 if (i == (ArrayLength(sCINDItems) - 1)) {
1300 message.AppendLiteral(")");
1301 break;
1302 }
1303 message.AppendLiteral("),");
1304 }
1305 } else {
1306 // Query for value
1307 for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
1308 message.AppendInt(sCINDItems[i].value);
1309 if (i == (ArrayLength(sCINDItems) - 1)) {
1310 break;
1311 }
1312 message.AppendLiteral(",");
1313 }
1314 }
1315 #ifdef MOZ_B2G_RIL
1316 } else if (!strcmp(aCommand, RESPONSE_CLCC)) {
1317 bool rv = true;
1318 uint32_t callNumbers = mCurrentCallArray.Length();
1319 uint32_t i;
1320 for (i = 1; i < callNumbers; i++) {
1321 rv &= SendCLCC(mCurrentCallArray[i], i);
1322 }
1324 if (!mCdmaSecondCall.mNumber.IsEmpty()) {
1325 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1326 MOZ_ASSERT(i == 2);
1328 rv &= SendCLCC(mCdmaSecondCall, 2);
1329 }
1331 return rv;
1332 #endif // MOZ_B2G_RIL
1333 } else {
1334 message.AppendInt(aValue);
1335 }
1337 return SendLine(message.get());
1338 }
1340 #ifdef MOZ_B2G_RIL
1341 void
1342 BluetoothHfpManager::UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend)
1343 {
1344 if (sCINDItems[aType].value != aValue) {
1345 sCINDItems[aType].value = aValue;
1346 if (aSend) {
1347 SendCommand(RESPONSE_CIEV, aType);
1348 }
1349 }
1350 }
1352 uint32_t
1353 BluetoothHfpManager::FindFirstCall(uint16_t aState)
1354 {
1355 uint32_t callLength = mCurrentCallArray.Length();
1357 for (uint32_t i = 1; i < callLength; ++i) {
1358 if (mCurrentCallArray[i].mState == aState) {
1359 return i;
1360 }
1361 }
1363 return 0;
1364 }
1366 uint32_t
1367 BluetoothHfpManager::GetNumberOfCalls(uint16_t aState)
1368 {
1369 uint32_t num = 0;
1370 uint32_t callLength = mCurrentCallArray.Length();
1372 for (uint32_t i = 1; i < callLength; ++i) {
1373 if (mCurrentCallArray[i].mState == aState) {
1374 ++num;
1375 }
1376 }
1378 return num;
1379 }
1381 uint32_t
1382 BluetoothHfpManager::GetNumberOfConCalls()
1383 {
1384 uint32_t num = 0;
1385 uint32_t callLength = mCurrentCallArray.Length();
1387 for (uint32_t i = 1; i < callLength; ++i) {
1388 if (mCurrentCallArray[i].mIsConference) {
1389 ++num;
1390 }
1391 }
1393 return num;
1394 }
1396 uint32_t
1397 BluetoothHfpManager::GetNumberOfConCalls(uint16_t aState)
1398 {
1399 uint32_t num = 0;
1400 uint32_t callLength = mCurrentCallArray.Length();
1402 for (uint32_t i = 1; i < callLength; ++i) {
1403 if (mCurrentCallArray[i].mIsConference
1404 && mCurrentCallArray[i].mState == aState) {
1405 ++num;
1406 }
1407 }
1409 return num;
1410 }
1412 void
1413 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
1414 uint16_t aCallState,
1415 const nsAString& aError,
1416 const nsAString& aNumber,
1417 const bool aIsOutgoing,
1418 const bool aIsConference,
1419 bool aSend)
1420 {
1421 if (!IsConnected()) {
1422 // Normal case. No need to print out warnings.
1423 return;
1424 }
1426 // aCallIndex can be UINT32_MAX for the pending outgoing call state update.
1427 // aCallIndex will be updated again after real call state changes. See Bug
1428 // 990467.
1429 if (aCallIndex == UINT32_MAX) {
1430 return;
1431 }
1433 while (aCallIndex >= mCurrentCallArray.Length()) {
1434 Call call;
1435 mCurrentCallArray.AppendElement(call);
1436 }
1438 uint16_t prevCallState = mCurrentCallArray[aCallIndex].mState;
1439 mCurrentCallArray[aCallIndex].mState = aCallState;
1440 mCurrentCallArray[aCallIndex].mDirection = !aIsOutgoing;
1442 bool prevCallIsConference = mCurrentCallArray[aCallIndex].mIsConference;
1443 mCurrentCallArray[aCallIndex].mIsConference = aIsConference;
1445 // Same logic as implementation in ril_worker.js
1446 if (aNumber.Length() && aNumber[0] == '+') {
1447 mCurrentCallArray[aCallIndex].mType = TOA_INTERNATIONAL;
1448 }
1449 mCurrentCallArray[aCallIndex].mNumber = aNumber;
1451 nsRefPtr<nsRunnable> sendRingTask;
1452 nsString address;
1454 switch (aCallState) {
1455 case nsITelephonyProvider::CALL_STATE_HELD:
1456 switch (prevCallState) {
1457 case nsITelephonyProvider::CALL_STATE_CONNECTED: {
1458 uint32_t numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
1459 uint32_t numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
1460 uint32_t numConCalls = GetNumberOfConCalls();
1462 /**
1463 * An active call becomes a held call.
1464 *
1465 * If this call is not a conference call,
1466 * - callheld state = ONHOLD_NOACTIVE if no active call remains;
1467 * - callheld state = ONHOLD_ACTIVE otherwise.
1468 * If this call belongs to a conference call and all other members of
1469 * the conference call have become held calls,
1470 * - callheld state = ONHOLD_NOACTIVE if no active call remains;
1471 * - callheld state = ONHOLD_ACTIVE otherwise.
1472 *
1473 * Note number of active calls may be 0 in-between state transition
1474 * (c1 has become held but c2 has not become active yet), so we regard
1475 * no active call remains if there is no other active/held call
1476 * besides this changed call/group of conference call.
1477 */
1478 if (!aIsConference) {
1479 if (numActive + numHeld == 1) {
1480 // A single active call is put on hold.
1481 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
1482 } else {
1483 // An active call is placed on hold or active/held calls swapped.
1484 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
1485 }
1486 SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
1487 } else if (GetNumberOfConCalls(nsITelephonyProvider::CALL_STATE_HELD)
1488 == numConCalls) {
1489 if (numActive + numHeld == numConCalls) {
1490 // An active conference call is put on hold.
1491 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
1492 } else {
1493 // Active calls are placed on hold or active/held calls swapped.
1494 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
1495 }
1496 SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
1497 }
1498 break;
1499 }
1500 case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
1501 // The call state changed from DISCONNECTED to HELD. It could happen
1502 // when user held a call before Bluetooth got connected.
1503 if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
1504 // callheld = ONHOLD_ACTIVE if an active call already exists.
1505 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
1506 SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
1507 }
1508 break;
1509 }
1510 break;
1511 case nsITelephonyProvider::CALL_STATE_INCOMING:
1512 if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
1513 SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
1514 UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
1515 } else {
1516 // Start sending RING indicator to HF
1517 sStopSendingRingFlag = false;
1518 UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
1520 if (mBSIR) {
1521 // Setup audio connection for in-band ring tone
1522 ConnectSco();
1523 }
1525 nsAutoString number(aNumber);
1526 if (!mCLIP) {
1527 number.AssignLiteral("");
1528 }
1530 MessageLoop::current()->PostDelayedTask(
1531 FROM_HERE,
1532 new SendRingIndicatorTask(number,
1533 mCurrentCallArray[aCallIndex].mType),
1534 sRingInterval);
1535 }
1536 break;
1537 case nsITelephonyProvider::CALL_STATE_DIALING:
1538 if (!mDialingRequestProcessed) {
1539 SendLine("OK");
1540 mDialingRequestProcessed = true;
1541 }
1543 UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING, aSend);
1544 ConnectSco();
1545 break;
1546 case nsITelephonyProvider::CALL_STATE_ALERTING:
1547 UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
1549 // If there's an ongoing call when the headset is just connected, we have
1550 // to open a sco socket here.
1551 ConnectSco();
1552 break;
1553 case nsITelephonyProvider::CALL_STATE_CONNECTED:
1554 /**
1555 * A call becomes active because:
1556 * - user answers an incoming call,
1557 * - user dials a outgoing call and it is answered, or
1558 * - SLC is connected when a call is active.
1559 */
1560 switch (prevCallState) {
1561 case nsITelephonyProvider::CALL_STATE_INCOMING:
1562 case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
1563 // Incoming call, no break
1564 sStopSendingRingFlag = true;
1565 ConnectSco();
1566 // NO BREAK HERE. continue to next statement
1567 case nsITelephonyProvider::CALL_STATE_DIALING:
1568 case nsITelephonyProvider::CALL_STATE_ALERTING:
1569 // Outgoing call
1570 UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
1571 UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
1573 if (FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
1574 // callheld state = ONHOLD_ACTIVE if a held call already exists.
1575 UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
1576 }
1577 break;
1578 case nsITelephonyProvider::CALL_STATE_CONNECTED:
1579 // User wants to add a held call to the conversation.
1580 // The original connected call becomes a conference call here.
1581 if (aIsConference) {
1582 UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
1583 }
1584 break;
1585 case nsITelephonyProvider::CALL_STATE_HELD:
1586 if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
1587 if (aIsConference && !prevCallIsConference) {
1588 // The held call was merged and becomes a conference call.
1589 UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
1590 } else if (sCINDItems[CINDType::CALLHELD].value ==
1591 CallHeldState::ONHOLD_NOACTIVE) {
1592 // The held call(s) become connected call(s).
1593 UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
1594 }
1595 }
1596 break;
1598 default:
1599 BT_WARNING("Not handling state changed");
1600 }
1601 break;
1602 case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
1603 switch (prevCallState) {
1604 case nsITelephonyProvider::CALL_STATE_INCOMING:
1605 // Incoming call, no break
1606 sStopSendingRingFlag = true;
1607 case nsITelephonyProvider::CALL_STATE_DIALING:
1608 case nsITelephonyProvider::CALL_STATE_ALERTING:
1609 // Outgoing call
1610 UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
1611 break;
1612 case nsITelephonyProvider::CALL_STATE_CONNECTED:
1613 // No call is ongoing
1614 if (sCINDItems[CINDType::CALLHELD].value ==
1615 CallHeldState::NO_CALLHELD) {
1616 UpdateCIND(CINDType::CALL, CallState::NO_CALL, aSend);
1617 }
1618 break;
1619 default:
1620 BT_WARNING("Not handling state changed");
1621 }
1623 // Handle held calls separately
1624 if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
1625 UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
1626 } else if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
1627 UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_NOACTIVE, aSend);
1628 } else {
1629 UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
1630 }
1632 // -1 is necessary because call 0 is an invalid (padding) call object.
1633 if (mCurrentCallArray.Length() - 1 ==
1634 GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) {
1635 // In order to let user hear busy tone via connected Bluetooth headset,
1636 // we postpone the timing of dropping SCO.
1637 if (!(aError.Equals(NS_LITERAL_STRING("BusyError")))) {
1638 DisconnectSco();
1639 } else {
1640 // Close Sco later since Dialer is still playing busy tone via HF.
1641 MessageLoop::current()->PostDelayedTask(FROM_HERE,
1642 new CloseScoTask(),
1643 sBusyToneInterval);
1644 }
1646 ResetCallArray();
1647 }
1648 break;
1649 default:
1650 BT_WARNING("Not handling state changed");
1651 break;
1652 }
1653 }
1655 PhoneType
1656 BluetoothHfpManager::GetPhoneType(const nsAString& aType)
1657 {
1658 // FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
1659 if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
1660 aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
1661 aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
1662 aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
1663 return PhoneType::GSM;
1664 } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
1665 aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
1666 aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
1667 return PhoneType::CDMA;
1668 }
1670 return PhoneType::NONE;
1671 }
1673 void
1674 BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
1675 {
1676 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1678 // Always regard second call as incoming call since v1.2 RIL
1679 // doesn't support outgoing second call in CDMA.
1680 mCdmaSecondCall.mDirection = true;
1682 mCdmaSecondCall.mNumber = aNumber;
1683 mCdmaSecondCall.mType = (aNumber[0] == '+') ? TOA_INTERNATIONAL :
1684 TOA_UNKNOWN;
1686 SendCCWA(aNumber, mCdmaSecondCall.mType);
1687 UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
1688 }
1690 void
1691 BluetoothHfpManager::AnswerWaitingCall()
1692 {
1693 MOZ_ASSERT(NS_IsMainThread());
1694 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1696 // Pick up second call. First call is held now.
1697 mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED;
1698 UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
1700 sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
1701 SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
1702 }
1704 void
1705 BluetoothHfpManager::IgnoreWaitingCall()
1706 {
1707 MOZ_ASSERT(NS_IsMainThread());
1708 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1710 mCdmaSecondCall.Reset();
1711 UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
1712 }
1714 void
1715 BluetoothHfpManager::ToggleCalls()
1716 {
1717 MOZ_ASSERT(NS_IsMainThread());
1718 MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
1720 // Toggle acitve and held calls
1721 mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
1722 nsITelephonyProvider::CALL_STATE_HELD :
1723 nsITelephonyProvider::CALL_STATE_CONNECTED;
1724 }
1725 #endif // MOZ_B2G_RIL
1727 void
1728 BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
1729 {
1730 MOZ_ASSERT(aSocket);
1731 #ifdef MOZ_B2G_RIL
1732 MOZ_ASSERT(mListener);
1733 #endif
1735 // Success to create a SCO socket
1736 if (aSocket == mScoSocket) {
1737 OnScoConnectSuccess();
1738 return;
1739 }
1741 /**
1742 * If the created connection is an inbound connection, close another server
1743 * socket because currently only one SLC is allowed. After that, we need to
1744 * make sure that both server socket would be nulled out. As for outbound
1745 * connections, we do nothing since sockets have been already handled in
1746 * function Connect().
1747 */
1748 if (aSocket == mHandsfreeSocket) {
1749 MOZ_ASSERT(!mSocket);
1750 mIsHsp = false;
1751 mHandsfreeSocket.swap(mSocket);
1753 mHeadsetSocket->Disconnect();
1754 mHeadsetSocket = nullptr;
1755 } else if (aSocket == mHeadsetSocket) {
1756 MOZ_ASSERT(!mSocket);
1757 mIsHsp = true;
1758 mHeadsetSocket.swap(mSocket);
1760 mHandsfreeSocket->Disconnect();
1761 mHandsfreeSocket = nullptr;
1762 }
1764 #ifdef MOZ_B2G_RIL
1765 // Enumerate current calls
1766 mListener->EnumerateCalls();
1768 mFirstCKPD = true;
1769 #endif
1771 // Cache device path for NotifySettings() since we can't get socket address
1772 // when a headset disconnect with us
1773 mSocket->GetAddress(mDeviceAddress);
1774 NotifyConnectionStatusChanged(
1775 NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
1777 ListenSco();
1779 OnConnect(EmptyString());
1780 }
1782 void
1783 BluetoothHfpManager::OnSocketConnectError(BluetoothSocket* aSocket)
1784 {
1785 // Failed to create a SCO socket
1786 if (aSocket == mScoSocket) {
1787 OnScoConnectError();
1788 return;
1789 }
1791 mHandsfreeSocket = nullptr;
1792 mHeadsetSocket = nullptr;
1794 OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
1795 }
1797 void
1798 BluetoothHfpManager::OnSocketDisconnect(BluetoothSocket* aSocket)
1799 {
1800 MOZ_ASSERT(aSocket);
1802 if (aSocket == mScoSocket) {
1803 // SCO socket is closed
1804 OnScoDisconnect();
1805 return;
1806 }
1808 if (aSocket != mSocket) {
1809 // Do nothing when a listening server socket is closed.
1810 return;
1811 }
1813 DisconnectSco();
1815 NotifyConnectionStatusChanged(
1816 NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
1817 OnDisconnect(EmptyString());
1819 Reset();
1820 }
1822 void
1823 BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
1824 {
1825 // UpdateSdpRecord() is not called so this callback function should not
1826 // be invoked.
1827 MOZ_ASSUME_UNREACHABLE("UpdateSdpRecords() should be called somewhere");
1828 }
1830 void
1831 BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
1832 const nsAString& aServiceUuid,
1833 int aChannel)
1834 {
1835 MOZ_ASSERT(NS_IsMainThread());
1836 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
1838 BluetoothService* bs = BluetoothService::Get();
1839 NS_ENSURE_TRUE_VOID(bs);
1841 if (aChannel < 0) {
1842 // If we can't find Handsfree server channel number on the remote device,
1843 // try to create HSP connection instead.
1844 nsString hspUuid;
1845 BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, hspUuid);
1847 if (aServiceUuid.Equals(hspUuid)) {
1848 OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND));
1849 } else if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress,
1850 hspUuid, this))) {
1851 OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
1852 } else {
1853 mIsHsp = true;
1854 }
1856 return;
1857 }
1859 MOZ_ASSERT(mSocket);
1861 if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
1862 OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
1863 }
1864 }
1866 void
1867 BluetoothHfpManager::OnScoConnectSuccess()
1868 {
1869 // For active connection request, we need to reply the DOMRequest
1870 if (mScoRunnable) {
1871 DispatchBluetoothReply(mScoRunnable,
1872 BluetoothValue(true), EmptyString());
1873 mScoRunnable = nullptr;
1874 }
1876 NotifyConnectionStatusChanged(
1877 NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
1879 mScoSocketStatus = mScoSocket->GetConnectionStatus();
1880 }
1882 void
1883 BluetoothHfpManager::OnScoConnectError()
1884 {
1885 if (mScoRunnable) {
1886 NS_NAMED_LITERAL_STRING(replyError, "Failed to create SCO socket!");
1887 DispatchBluetoothReply(mScoRunnable, BluetoothValue(), replyError);
1889 mScoRunnable = nullptr;
1890 }
1892 ListenSco();
1893 }
1895 void
1896 BluetoothHfpManager::OnScoDisconnect()
1897 {
1898 if (mScoSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
1899 ListenSco();
1900 NotifyConnectionStatusChanged(
1901 NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
1902 }
1903 }
1905 bool
1906 BluetoothHfpManager::IsConnected()
1907 {
1908 if (mSocket) {
1909 return mSocket->GetConnectionStatus() ==
1910 SocketConnectionStatus::SOCKET_CONNECTED;
1911 }
1913 return false;
1914 }
1916 void
1917 BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress)
1918 {
1919 return mSocket->GetAddress(aDeviceAddress);
1920 }
1922 bool
1923 BluetoothHfpManager::ConnectSco(BluetoothReplyRunnable* aRunnable)
1924 {
1925 MOZ_ASSERT(NS_IsMainThread());
1927 NS_ENSURE_TRUE(!sInShutdown, false);
1928 NS_ENSURE_TRUE(IsConnected(), false);
1930 SocketConnectionStatus status = mScoSocket->GetConnectionStatus();
1931 if (status == SocketConnectionStatus::SOCKET_CONNECTED ||
1932 status == SocketConnectionStatus::SOCKET_CONNECTING ||
1933 (mScoRunnable && (mScoRunnable != aRunnable))) {
1934 BT_WARNING("SCO connection exists or is being established");
1935 return false;
1936 }
1938 // If we are not using HSP, we have to make sure Service Level Connection
1939 // established before we start to set up SCO (synchronous connection).
1940 if (!mSlcConnected && !mIsHsp) {
1941 mConnectScoRequest = true;
1942 BT_WARNING("ConnectSco called before Service Level Connection established");
1943 return false;
1944 }
1946 // Stop listening
1947 mScoSocket->Disconnect();
1949 mScoSocket->Connect(NS_ConvertUTF16toUTF8(mDeviceAddress), -1);
1950 mScoSocketStatus = mScoSocket->GetConnectionStatus();
1952 mScoRunnable = aRunnable;
1953 return true;
1954 }
1956 bool
1957 BluetoothHfpManager::DisconnectSco()
1958 {
1959 if (!IsScoConnected()) {
1960 BT_WARNING("SCO has been already disconnected.");
1961 return false;
1962 }
1964 mScoSocket->Disconnect();
1965 return true;
1966 }
1968 bool
1969 BluetoothHfpManager::ListenSco()
1970 {
1971 MOZ_ASSERT(NS_IsMainThread());
1973 if (sInShutdown) {
1974 BT_WARNING("ListenSco called while in shutdown!");
1975 return false;
1976 }
1978 if (mScoSocket->GetConnectionStatus() ==
1979 SocketConnectionStatus::SOCKET_LISTENING) {
1980 BT_WARNING("SCO socket has been already listening");
1981 return false;
1982 }
1984 mScoSocket->Disconnect();
1986 if (!mScoSocket->Listen(-1)) {
1987 BT_WARNING("Can't listen on SCO socket!");
1988 return false;
1989 }
1991 mScoSocketStatus = mScoSocket->GetConnectionStatus();
1992 return true;
1993 }
1995 bool
1996 BluetoothHfpManager::IsScoConnected()
1997 {
1998 if (mScoSocket) {
1999 return mScoSocket->GetConnectionStatus() ==
2000 SocketConnectionStatus::SOCKET_CONNECTED;
2001 }
2002 return false;
2003 }
2005 void
2006 BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
2007 {
2008 MOZ_ASSERT(NS_IsMainThread());
2010 // When we failed to create a socket, restart listening.
2011 if (!aErrorStr.IsEmpty()) {
2012 mSocket = nullptr;
2013 Listen();
2014 }
2016 /**
2017 * On the one hand, notify the controller that we've done for outbound
2018 * connections. On the other hand, we do nothing for inbound connections.
2019 */
2020 NS_ENSURE_TRUE_VOID(mController);
2022 nsRefPtr<BluetoothProfileController> controller = mController.forget();
2023 controller->NotifyCompletion(aErrorStr);
2024 }
2026 void
2027 BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr)
2028 {
2029 MOZ_ASSERT(NS_IsMainThread());
2031 // Start listening
2032 mSocket = nullptr;
2033 Listen();
2035 /**
2036 * On the one hand, notify the controller that we've done for outbound
2037 * connections. On the other hand, we do nothing for inbound connections.
2038 */
2039 NS_ENSURE_TRUE_VOID(mController);
2041 nsRefPtr<BluetoothProfileController> controller = mController.forget();
2042 controller->NotifyCompletion(aErrorStr);
2043 }
2045 NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver)