dom/bluetooth/bluez/BluetoothHfpManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial