dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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

mercurial