1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2046 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "base/basictypes.h" 1.11 + 1.12 +#include "BluetoothHfpManager.h" 1.13 + 1.14 +#include "BluetoothProfileController.h" 1.15 +#include "BluetoothReplyRunnable.h" 1.16 +#include "BluetoothService.h" 1.17 +#include "BluetoothSocket.h" 1.18 +#include "BluetoothUtils.h" 1.19 +#include "BluetoothUuid.h" 1.20 + 1.21 +#include "jsapi.h" 1.22 +#include "mozilla/dom/bluetooth/BluetoothTypes.h" 1.23 +#include "mozilla/Services.h" 1.24 +#include "mozilla/StaticPtr.h" 1.25 +#include "nsContentUtils.h" 1.26 +#include "nsIObserverService.h" 1.27 +#include "nsISettingsService.h" 1.28 +#include "nsServiceManagerUtils.h" 1.29 + 1.30 +#ifdef MOZ_B2G_RIL 1.31 +#include "nsIDOMIccInfo.h" 1.32 +#include "nsIDOMMobileConnection.h" 1.33 +#include "nsIIccProvider.h" 1.34 +#include "nsIMobileConnectionProvider.h" 1.35 +#include "nsITelephonyProvider.h" 1.36 +#include "nsRadioInterfaceLayer.h" 1.37 +#endif 1.38 + 1.39 +/** 1.40 + * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT 1.41 + * Capabilities" in Bluetooth hands-free profile 1.6 1.42 + */ 1.43 +#define BRSF_BIT_THREE_WAY_CALLING 1 1.44 +#define BSRF_BIT_EC_NR_FUNCTION (1 << 1) 1.45 +#define BRSF_BIT_VOICE_RECOGNITION (1 << 2) 1.46 +#define BRSF_BIT_IN_BAND_RING_TONE (1 << 3) 1.47 +#define BRSF_BIT_ATTACH_NUM_TO_VOICE_TAG (1 << 4) 1.48 +#define BRSF_BIT_ABILITY_TO_REJECT_CALL (1 << 5) 1.49 +#define BRSF_BIT_ENHANCED_CALL_STATUS (1 << 6) 1.50 +#define BRSF_BIT_ENHANCED_CALL_CONTROL (1 << 7) 1.51 +#define BRSF_BIT_EXTENDED_ERR_RESULT_CODES (1 << 8) 1.52 +#define BRSF_BIT_CODEC_NEGOTIATION (1 << 9) 1.53 + 1.54 +#ifdef MOZ_B2G_RIL 1.55 +/** 1.56 + * These constants are used in result code such as +CLIP and +CCWA. The value 1.57 + * of these constants is the same as TOA_INTERNATIONAL/TOA_UNKNOWN defined in 1.58 + * ril_consts.js 1.59 + */ 1.60 +#define TOA_UNKNOWN 0x81 1.61 +#define TOA_INTERNATIONAL 0x91 1.62 +#endif 1.63 + 1.64 +#define CR_LF "\xd\xa"; 1.65 + 1.66 +#define MOZSETTINGS_CHANGED_ID "mozsettings-changed" 1.67 +#define AUDIO_VOLUME_BT_SCO_ID "audio.volume.bt_sco" 1.68 + 1.69 +#define RESPONSE_CIEV "+CIEV: " 1.70 +#define RESPONSE_CIND "+CIND: " 1.71 +#define RESPONSE_CLCC "+CLCC: " 1.72 +#define RESPONSE_BRSF "+BRSF: " 1.73 +#define RESPONSE_VGS "+VGS: " 1.74 +#define RESPONSE_CME_ERROR "+CME ERROR: " 1.75 + 1.76 +using namespace mozilla; 1.77 +using namespace mozilla::ipc; 1.78 +USING_BLUETOOTH_NAMESPACE 1.79 + 1.80 +namespace { 1.81 + StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager; 1.82 + bool sInShutdown = false; 1.83 + static const char kHfpCrlf[] = "\xd\xa"; 1.84 + 1.85 +#ifdef MOZ_B2G_RIL 1.86 + // Sending ringtone related 1.87 + static bool sStopSendingRingFlag = true; 1.88 + static int sRingInterval = 3000; //unit: ms 1.89 + 1.90 + // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a 1.91 + // magic number. The mechanism should be revised once we can get call history. 1.92 + static int sWaitingForDialingInterval = 2000; //unit: ms 1.93 + 1.94 + // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the 1.95 + // time window set in Dialer and the extra '0.7' second is a magic number. 1.96 + // The mechanism should be revised once we know the exact time at which 1.97 + // Dialer stops playing. 1.98 + static int sBusyToneInterval = 3700; //unit: ms 1.99 +#endif // MOZ_B2G_RIL 1.100 +} // anonymous namespace 1.101 + 1.102 +#ifdef MOZ_B2G_RIL 1.103 +/* CallState for sCINDItems[CINDType::CALL].value 1.104 + * - NO_CALL: there are no calls in progress 1.105 + * - IN_PROGRESS: at least one call is in progress 1.106 + */ 1.107 +enum CallState { 1.108 + NO_CALL, 1.109 + IN_PROGRESS 1.110 +}; 1.111 + 1.112 +/* CallSetupState for sCINDItems[CINDType::CALLSETUP].value 1.113 + * - NO_CALLSETUP: not currently in call set up 1.114 + * - INCOMING: an incoming call process ongoing 1.115 + * - OUTGOING: an outgoing call set up is ongoing 1.116 + * - OUTGOING_ALERTING: remote party being alerted in an outgoing call 1.117 + */ 1.118 +enum CallSetupState { 1.119 + NO_CALLSETUP, 1.120 + INCOMING, 1.121 + OUTGOING, 1.122 + OUTGOING_ALERTING 1.123 +}; 1.124 + 1.125 +/* CallHeldState for sCINDItems[CINDType::CALLHELD].value 1.126 + * - NO_CALLHELD: no calls held 1.127 + * - ONHOLD_ACTIVE: both an active and a held call 1.128 + * - ONHOLD_NOACTIVE: call on hold, no active call 1.129 + */ 1.130 +enum CallHeldState { 1.131 + NO_CALLHELD, 1.132 + ONHOLD_ACTIVE, 1.133 + ONHOLD_NOACTIVE 1.134 +}; 1.135 +#endif // MOZ_B2G_RIL 1.136 + 1.137 +typedef struct { 1.138 + const char* name; 1.139 + const char* range; 1.140 + int value; 1.141 + bool activated; 1.142 +} CINDItem; 1.143 + 1.144 +enum CINDType { 1.145 + BATTCHG = 1, 1.146 +#ifdef MOZ_B2G_RIL 1.147 + CALL, 1.148 + CALLHELD, 1.149 + CALLSETUP, 1.150 + SERVICE, 1.151 + SIGNAL, 1.152 + ROAM 1.153 +#endif 1.154 +}; 1.155 + 1.156 +static CINDItem sCINDItems[] = { 1.157 + {}, 1.158 + {"battchg", "0-5", 5, true}, 1.159 +#ifdef MOZ_B2G_RIL 1.160 + {"call", "0,1", CallState::NO_CALL, true}, 1.161 + {"callheld", "0-2", CallHeldState::NO_CALLHELD, true}, 1.162 + {"callsetup", "0-3", CallSetupState::NO_CALLSETUP, true}, 1.163 + {"service", "0,1", 0, true}, 1.164 + {"signal", "0-5", 0, true}, 1.165 + {"roam", "0,1", 0, true} 1.166 +#endif 1.167 +}; 1.168 + 1.169 +class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback 1.170 +{ 1.171 +public: 1.172 + NS_DECL_ISUPPORTS 1.173 + 1.174 + NS_IMETHOD 1.175 + Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) 1.176 + { 1.177 + MOZ_ASSERT(NS_IsMainThread()); 1.178 + 1.179 + JSContext *cx = nsContentUtils::GetCurrentJSContext(); 1.180 + NS_ENSURE_TRUE(cx, NS_OK); 1.181 + 1.182 + if (!aResult.isNumber()) { 1.183 + BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!"); 1.184 + return NS_OK; 1.185 + } 1.186 + 1.187 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.188 + hfp->mCurrentVgs = aResult.toNumber(); 1.189 + 1.190 + return NS_OK; 1.191 + } 1.192 + 1.193 + NS_IMETHOD 1.194 + HandleError(const nsAString& aName) 1.195 + { 1.196 + BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'"); 1.197 + return NS_OK; 1.198 + } 1.199 +}; 1.200 + 1.201 +NS_IMPL_ISUPPORTS(BluetoothHfpManager::GetVolumeTask, 1.202 + nsISettingsServiceCallback); 1.203 + 1.204 +NS_IMETHODIMP 1.205 +BluetoothHfpManager::Observe(nsISupports* aSubject, 1.206 + const char* aTopic, 1.207 + const char16_t* aData) 1.208 +{ 1.209 + if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) { 1.210 + HandleVolumeChanged(nsDependentString(aData)); 1.211 + } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.212 + HandleShutdown(); 1.213 + } else { 1.214 + MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!"); 1.215 + return NS_ERROR_UNEXPECTED; 1.216 + } 1.217 + 1.218 + return NS_OK; 1.219 +} 1.220 + 1.221 +void 1.222 +BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo) 1.223 +{ 1.224 + // Range of battery level: [0, 1], double 1.225 + // Range of CIND::BATTCHG: [0, 5], int 1.226 + int level = ceil(aBatteryInfo.level() * 5.0); 1.227 + if (level != sCINDItems[CINDType::BATTCHG].value) { 1.228 + sCINDItems[CINDType::BATTCHG].value = level; 1.229 + SendCommand(RESPONSE_CIEV, CINDType::BATTCHG); 1.230 + } 1.231 +} 1.232 + 1.233 +#ifdef MOZ_B2G_RIL 1.234 +class BluetoothHfpManager::RespondToBLDNTask : public Task 1.235 +{ 1.236 +private: 1.237 + void Run() MOZ_OVERRIDE 1.238 + { 1.239 + MOZ_ASSERT(sBluetoothHfpManager); 1.240 + 1.241 + if (!sBluetoothHfpManager->mDialingRequestProcessed) { 1.242 + sBluetoothHfpManager->mDialingRequestProcessed = true; 1.243 + sBluetoothHfpManager->SendLine("ERROR"); 1.244 + } 1.245 + } 1.246 +}; 1.247 + 1.248 +class BluetoothHfpManager::SendRingIndicatorTask : public Task 1.249 +{ 1.250 +public: 1.251 + SendRingIndicatorTask(const nsAString& aNumber, int aType) 1.252 + : mNumber(aNumber) 1.253 + , mType(aType) 1.254 + { 1.255 + MOZ_ASSERT(NS_IsMainThread()); 1.256 + } 1.257 + 1.258 + void Run() MOZ_OVERRIDE 1.259 + { 1.260 + MOZ_ASSERT(NS_IsMainThread()); 1.261 + 1.262 + // Stop sending RING indicator 1.263 + if (sStopSendingRingFlag) { 1.264 + return; 1.265 + } 1.266 + 1.267 + if (!sBluetoothHfpManager) { 1.268 + BT_WARNING("BluetoothHfpManager no longer exists, cannot send ring!"); 1.269 + return; 1.270 + } 1.271 + 1.272 + nsAutoCString ringMsg("RING"); 1.273 + sBluetoothHfpManager->SendLine(ringMsg.get()); 1.274 + 1.275 + if (!mNumber.IsEmpty()) { 1.276 + nsAutoCString clipMsg("+CLIP: \""); 1.277 + clipMsg.Append(NS_ConvertUTF16toUTF8(mNumber).get()); 1.278 + clipMsg.AppendLiteral("\","); 1.279 + clipMsg.AppendInt(mType); 1.280 + sBluetoothHfpManager->SendLine(clipMsg.get()); 1.281 + } 1.282 + 1.283 + MessageLoop::current()-> 1.284 + PostDelayedTask(FROM_HERE, 1.285 + new SendRingIndicatorTask(mNumber, mType), 1.286 + sRingInterval); 1.287 + } 1.288 + 1.289 +private: 1.290 + nsString mNumber; 1.291 + int mType; 1.292 +}; 1.293 +#endif // MOZ_B2G_RIL 1.294 + 1.295 +class BluetoothHfpManager::CloseScoTask : public Task 1.296 +{ 1.297 +private: 1.298 + void Run() MOZ_OVERRIDE 1.299 + { 1.300 + MOZ_ASSERT(sBluetoothHfpManager); 1.301 + 1.302 + sBluetoothHfpManager->DisconnectSco(); 1.303 + } 1.304 +}; 1.305 + 1.306 +#ifdef MOZ_B2G_RIL 1.307 +static bool 1.308 +IsValidDtmf(const char aChar) { 1.309 + // Valid DTMF: [*#0-9ABCD] 1.310 + if (aChar == '*' || aChar == '#') { 1.311 + return true; 1.312 + } else if (aChar >= '0' && aChar <= '9') { 1.313 + return true; 1.314 + } else if (aChar >= 'A' && aChar <= 'D') { 1.315 + return true; 1.316 + } 1.317 + return false; 1.318 +} 1.319 + 1.320 +static bool 1.321 +IsMandatoryIndicator(const CINDType aType) { 1.322 + return (aType == CINDType::CALL) || 1.323 + (aType == CINDType::CALLHELD) || 1.324 + (aType == CINDType::CALLSETUP); 1.325 +} 1.326 + 1.327 +/** 1.328 + * Call 1.329 + */ 1.330 +Call::Call() 1.331 +{ 1.332 + Reset(); 1.333 +} 1.334 + 1.335 +void 1.336 +Call::Reset() 1.337 +{ 1.338 + mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED; 1.339 + mDirection = false; 1.340 + mIsConference = false; 1.341 + mNumber.Truncate(); 1.342 + mType = TOA_UNKNOWN; 1.343 +} 1.344 + 1.345 +bool 1.346 +Call::IsActive() 1.347 +{ 1.348 + return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED); 1.349 +} 1.350 +#endif // MOZ_B2G_RIL 1.351 + 1.352 +/** 1.353 + * BluetoothHfpManager 1.354 + */ 1.355 +BluetoothHfpManager::BluetoothHfpManager() 1.356 +{ 1.357 +#ifdef MOZ_B2G_RIL 1.358 + mPhoneType = PhoneType::NONE; 1.359 +#endif // MOZ_B2G_RIL 1.360 + 1.361 + Reset(); 1.362 +} 1.363 + 1.364 +#ifdef MOZ_B2G_RIL 1.365 +void 1.366 +BluetoothHfpManager::ResetCallArray() 1.367 +{ 1.368 + mCurrentCallArray.Clear(); 1.369 + // Append a call object at the beginning of mCurrentCallArray since call 1.370 + // index from RIL starts at 1. 1.371 + Call call; 1.372 + mCurrentCallArray.AppendElement(call); 1.373 + 1.374 + if (mPhoneType == PhoneType::CDMA) { 1.375 + mCdmaSecondCall.Reset(); 1.376 + } 1.377 +} 1.378 +#endif // MOZ_B2G_RIL 1.379 + 1.380 +void 1.381 +BluetoothHfpManager::Reset() 1.382 +{ 1.383 +#ifdef MOZ_B2G_RIL 1.384 + sStopSendingRingFlag = true; 1.385 + sCINDItems[CINDType::CALL].value = CallState::NO_CALL; 1.386 + sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP; 1.387 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD; 1.388 +#endif 1.389 + for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) { 1.390 + sCINDItems[i].activated = true; 1.391 + } 1.392 + 1.393 +#ifdef MOZ_B2G_RIL 1.394 + mCCWA = false; 1.395 + mCLIP = false; 1.396 + mDialingRequestProcessed = true; 1.397 + 1.398 + // We disable BSIR by default as it requires OEM implement BT SCO + SPEAKER 1.399 + // output audio path in audio driver. OEM can enable BSIR by setting 1.400 + // mBSIR=true here. 1.401 + // 1.402 + // Please see Bug 878728 for more information. 1.403 + mBSIR = false; 1.404 + 1.405 + ResetCallArray(); 1.406 +#endif 1.407 + mCMEE = false; 1.408 + mCMER = false; 1.409 + mConnectScoRequest = false; 1.410 + mSlcConnected = false; 1.411 + mIsHsp = false; 1.412 + mReceiveVgsFlag = false; 1.413 + mController = nullptr; 1.414 +} 1.415 + 1.416 +bool 1.417 +BluetoothHfpManager::Init() 1.418 +{ 1.419 + MOZ_ASSERT(NS_IsMainThread()); 1.420 + 1.421 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.422 + NS_ENSURE_TRUE(obs, false); 1.423 + 1.424 + if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) || 1.425 + NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) { 1.426 + BT_WARNING("Failed to add observers!"); 1.427 + return false; 1.428 + } 1.429 + 1.430 + hal::RegisterBatteryObserver(this); 1.431 + 1.432 +#ifdef MOZ_B2G_RIL 1.433 + mListener = new BluetoothRilListener(); 1.434 + if (!mListener->Listen(true)) { 1.435 + BT_WARNING("Failed to start listening RIL"); 1.436 + return false; 1.437 + } 1.438 +#endif 1.439 + 1.440 + nsCOMPtr<nsISettingsService> settings = 1.441 + do_GetService("@mozilla.org/settingsService;1"); 1.442 + NS_ENSURE_TRUE(settings, false); 1.443 + 1.444 + nsCOMPtr<nsISettingsServiceLock> settingsLock; 1.445 + nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock)); 1.446 + NS_ENSURE_SUCCESS(rv, false); 1.447 + 1.448 + nsRefPtr<GetVolumeTask> callback = new GetVolumeTask(); 1.449 + rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback); 1.450 + NS_ENSURE_SUCCESS(rv, false); 1.451 + 1.452 + Listen(); 1.453 + 1.454 + mScoSocket = new BluetoothSocket(this, 1.455 + BluetoothSocketType::SCO, 1.456 + true, 1.457 + false); 1.458 + mScoSocketStatus = mScoSocket->GetConnectionStatus(); 1.459 + ListenSco(); 1.460 + return true; 1.461 +} 1.462 + 1.463 +BluetoothHfpManager::~BluetoothHfpManager() 1.464 +{ 1.465 +#ifdef MOZ_B2G_RIL 1.466 + if (!mListener->Listen(false)) { 1.467 + BT_WARNING("Failed to stop listening RIL"); 1.468 + } 1.469 + mListener = nullptr; 1.470 +#endif 1.471 + 1.472 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.473 + NS_ENSURE_TRUE_VOID(obs); 1.474 + 1.475 + if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) || 1.476 + NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) { 1.477 + BT_WARNING("Failed to remove observers!"); 1.478 + } 1.479 + 1.480 + hal::UnregisterBatteryObserver(this); 1.481 +} 1.482 + 1.483 +//static 1.484 +BluetoothHfpManager* 1.485 +BluetoothHfpManager::Get() 1.486 +{ 1.487 + MOZ_ASSERT(NS_IsMainThread()); 1.488 + 1.489 + // If sBluetoothHfpManager already exists, exit early 1.490 + if (sBluetoothHfpManager) { 1.491 + return sBluetoothHfpManager; 1.492 + } 1.493 + 1.494 + // If we're in shutdown, don't create a new instance 1.495 + NS_ENSURE_FALSE(sInShutdown, nullptr); 1.496 + 1.497 + // Create a new instance, register, and return 1.498 + BluetoothHfpManager* manager = new BluetoothHfpManager(); 1.499 + NS_ENSURE_TRUE(manager->Init(), nullptr); 1.500 + 1.501 + sBluetoothHfpManager = manager; 1.502 + return sBluetoothHfpManager; 1.503 +} 1.504 + 1.505 +void 1.506 +BluetoothHfpManager::NotifyConnectionStatusChanged(const nsAString& aType) 1.507 +{ 1.508 + MOZ_ASSERT(NS_IsMainThread()); 1.509 + 1.510 + // Notify Gecko observers 1.511 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.512 + NS_ENSURE_TRUE_VOID(obs); 1.513 + 1.514 + if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(), 1.515 + mDeviceAddress.get()))) { 1.516 + BT_WARNING("Failed to notify observsers!"); 1.517 + } 1.518 + 1.519 + // Dispatch an event of status change 1.520 + bool status; 1.521 + nsAutoString eventName; 1.522 + if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) { 1.523 + status = IsConnected(); 1.524 + eventName.AssignLiteral(HFP_STATUS_CHANGED_ID); 1.525 + } else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) { 1.526 + status = IsScoConnected(); 1.527 + eventName.AssignLiteral(SCO_STATUS_CHANGED_ID); 1.528 + } else { 1.529 + MOZ_ASSERT(false); 1.530 + return; 1.531 + } 1.532 + 1.533 + DispatchStatusChangedEvent(eventName, mDeviceAddress, status); 1.534 +} 1.535 + 1.536 +#ifdef MOZ_B2G_RIL 1.537 +void 1.538 +BluetoothHfpManager::NotifyDialer(const nsAString& aCommand) 1.539 +{ 1.540 + nsString type, name; 1.541 + BluetoothValue v; 1.542 + InfallibleTArray<BluetoothNamedValue> parameters; 1.543 + type.AssignLiteral("bluetooth-dialer-command"); 1.544 + 1.545 + name.AssignLiteral("command"); 1.546 + v = nsString(aCommand); 1.547 + parameters.AppendElement(BluetoothNamedValue(name, v)); 1.548 + 1.549 + if (!BroadcastSystemMessage(type, parameters)) { 1.550 + BT_WARNING("Failed to broadcast system message to dialer"); 1.551 + } 1.552 +} 1.553 +#endif // MOZ_B2G_RIL 1.554 + 1.555 +void 1.556 +BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData) 1.557 +{ 1.558 + MOZ_ASSERT(NS_IsMainThread()); 1.559 + 1.560 + // The string that we're interested in will be a JSON string that looks like: 1.561 + // {"key":"volumeup", "value":10} 1.562 + // {"key":"volumedown", "value":2} 1.563 + 1.564 + JSContext* cx = nsContentUtils::GetSafeJSContext(); 1.565 + NS_ENSURE_TRUE_VOID(cx); 1.566 + 1.567 + JS::Rooted<JS::Value> val(cx); 1.568 + NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)); 1.569 + NS_ENSURE_TRUE_VOID(val.isObject()); 1.570 + 1.571 + JS::Rooted<JSObject*> obj(cx, &val.toObject()); 1.572 + JS::Rooted<JS::Value> key(cx); 1.573 + if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) { 1.574 + return; 1.575 + } 1.576 + 1.577 + bool match; 1.578 + if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) || 1.579 + !match) { 1.580 + return; 1.581 + } 1.582 + 1.583 + JS::Rooted<JS::Value> value(cx); 1.584 + if (!JS_GetProperty(cx, obj, "value", &value)|| 1.585 + !value.isNumber()) { 1.586 + return; 1.587 + } 1.588 + 1.589 + mCurrentVgs = value.toNumber(); 1.590 + 1.591 + // Adjust volume by headset and we don't have to send volume back to headset 1.592 + if (mReceiveVgsFlag) { 1.593 + mReceiveVgsFlag = false; 1.594 + return; 1.595 + } 1.596 + 1.597 + // Only send volume back when there's a connected headset 1.598 + if (IsConnected()) { 1.599 + SendCommand(RESPONSE_VGS, mCurrentVgs); 1.600 + } 1.601 +} 1.602 + 1.603 +#ifdef MOZ_B2G_RIL 1.604 +void 1.605 +BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId) 1.606 +{ 1.607 + nsCOMPtr<nsIMobileConnectionProvider> connection = 1.608 + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); 1.609 + NS_ENSURE_TRUE_VOID(connection); 1.610 + 1.611 + nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo; 1.612 + connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo)); 1.613 + NS_ENSURE_TRUE_VOID(voiceInfo); 1.614 + 1.615 + nsString type; 1.616 + voiceInfo->GetType(type); 1.617 + mPhoneType = GetPhoneType(type); 1.618 + 1.619 + bool roaming; 1.620 + voiceInfo->GetRoaming(&roaming); 1.621 + UpdateCIND(CINDType::ROAM, roaming); 1.622 + 1.623 + nsString regState; 1.624 + voiceInfo->GetState(regState); 1.625 + bool service = regState.EqualsLiteral("registered"); 1.626 + if (service != sCINDItems[CINDType::SERVICE].value) { 1.627 + // Notify BluetoothRilListener of service change 1.628 + mListener->ServiceChanged(aClientId, service); 1.629 + } 1.630 + UpdateCIND(CINDType::SERVICE, service); 1.631 + 1.632 + JSContext* cx = nsContentUtils::GetSafeJSContext(); 1.633 + NS_ENSURE_TRUE_VOID(cx); 1.634 + JS::Rooted<JS::Value> value(cx); 1.635 + voiceInfo->GetRelSignalStrength(&value); 1.636 + NS_ENSURE_TRUE_VOID(value.isNumber()); 1.637 + uint8_t signal = ceil(value.toNumber() / 20.0); 1.638 + UpdateCIND(CINDType::SIGNAL, signal); 1.639 + 1.640 + /** 1.641 + * Possible return values for mode are: 1.642 + * - null (unknown): set mNetworkSelectionMode to 0 (auto) 1.643 + * - automatic: set mNetworkSelectionMode to 0 (auto) 1.644 + * - manual: set mNetworkSelectionMode to 1 (manual) 1.645 + */ 1.646 + nsString mode; 1.647 + connection->GetNetworkSelectionMode(aClientId, mode); 1.648 + if (mode.EqualsLiteral("manual")) { 1.649 + mNetworkSelectionMode = 1; 1.650 + } else { 1.651 + mNetworkSelectionMode = 0; 1.652 + } 1.653 + 1.654 + nsCOMPtr<nsIDOMMozMobileNetworkInfo> network; 1.655 + voiceInfo->GetNetwork(getter_AddRefs(network)); 1.656 + NS_ENSURE_TRUE_VOID(network); 1.657 + network->GetLongName(mOperatorName); 1.658 + 1.659 + // According to GSM 07.07, "<format> indicates if the format is alphanumeric 1.660 + // or numeric; long alphanumeric format can be upto 16 characters long and 1.661 + // short format up to 8 characters (refer GSM MoU SE.13 [9])..." 1.662 + // However, we found that the operator name may sometimes be longer than 16 1.663 + // characters. After discussion, we decided to fix this here but not in RIL 1.664 + // or modem. 1.665 + // 1.666 + // Please see Bug 871366 for more information. 1.667 + if (mOperatorName.Length() > 16) { 1.668 + BT_WARNING("The operator name was longer than 16 characters. We cut it."); 1.669 + mOperatorName.Left(mOperatorName, 16); 1.670 + } 1.671 +} 1.672 + 1.673 +void 1.674 +BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId) 1.675 +{ 1.676 + nsCOMPtr<nsIIccProvider> icc = 1.677 + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); 1.678 + NS_ENSURE_TRUE_VOID(icc); 1.679 + 1.680 + nsCOMPtr<nsIDOMMozIccInfo> iccInfo; 1.681 + icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo)); 1.682 + NS_ENSURE_TRUE_VOID(iccInfo); 1.683 + 1.684 + nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo); 1.685 + NS_ENSURE_TRUE_VOID(gsmIccInfo); 1.686 + gsmIccInfo->GetMsisdn(mMsisdn); 1.687 +} 1.688 +#endif // MOZ_B2G_RIL 1.689 + 1.690 +void 1.691 +BluetoothHfpManager::HandleShutdown() 1.692 +{ 1.693 + MOZ_ASSERT(NS_IsMainThread()); 1.694 + sInShutdown = true; 1.695 + Disconnect(nullptr); 1.696 + DisconnectSco(); 1.697 + sBluetoothHfpManager = nullptr; 1.698 +} 1.699 + 1.700 +// Virtual function of class SocketConsumer 1.701 +void 1.702 +BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket, 1.703 + nsAutoPtr<UnixSocketRawData>& aMessage) 1.704 +{ 1.705 + MOZ_ASSERT(NS_IsMainThread()); 1.706 + MOZ_ASSERT(aSocket); 1.707 + 1.708 + nsAutoCString msg((const char*)aMessage->mData.get(), aMessage->mSize); 1.709 + msg.StripWhitespace(); 1.710 + 1.711 + nsTArray<nsCString> atCommandValues; 1.712 + 1.713 + // For more information, please refer to 4.34.1 "Bluetooth Defined AT 1.714 + // Capabilities" in Bluetooth hands-free profile 1.6 1.715 + if (msg.Find("AT+BRSF=") != -1) { 1.716 +#ifdef MOZ_B2G_RIL 1.717 + uint32_t brsf = BRSF_BIT_ABILITY_TO_REJECT_CALL | 1.718 + BRSF_BIT_ENHANCED_CALL_STATUS; 1.719 + 1.720 + // No support for three way calling in CDMA since 1.721 + // CDMA disallows to hang existing call for CHLD=1 1.722 + if (mPhoneType != PhoneType::CDMA) { 1.723 + brsf |= BRSF_BIT_THREE_WAY_CALLING; 1.724 + } 1.725 + 1.726 + if (mBSIR) { 1.727 + brsf |= BRSF_BIT_IN_BAND_RING_TONE; 1.728 + } 1.729 +#else 1.730 + uint32_t brsf = 0; 1.731 +#endif // MOZ_B2G_RIL 1.732 + 1.733 + SendCommand(RESPONSE_BRSF, brsf); 1.734 + } else if (msg.Find("AT+CIND=?") != -1) { 1.735 + // Asking for CIND range 1.736 + SendCommand(RESPONSE_CIND, 0); 1.737 + } else if (msg.Find("AT+CIND?") != -1) { 1.738 + // Asking for CIND value 1.739 + SendCommand(RESPONSE_CIND, 1); 1.740 + } else if (msg.Find("AT+CMER=") != -1) { 1.741 + /** 1.742 + * SLC establishment is done when AT+CMER has been received. 1.743 + * Do nothing but respond with "OK". 1.744 + */ 1.745 + ParseAtCommand(msg, 8, atCommandValues); 1.746 + 1.747 + if (atCommandValues.Length() < 4) { 1.748 + BT_WARNING("Could't get the value of command [AT+CMER=]"); 1.749 + goto respond_with_ok; 1.750 + } 1.751 + 1.752 + if (!atCommandValues[0].EqualsLiteral("3") || 1.753 + !atCommandValues[1].EqualsLiteral("0") || 1.754 + !atCommandValues[2].EqualsLiteral("0")) { 1.755 + BT_WARNING("Wrong value of CMER"); 1.756 + goto respond_with_ok; 1.757 + } 1.758 + 1.759 + mCMER = atCommandValues[3].EqualsLiteral("1"); 1.760 + 1.761 + /** 1.762 + * SLC is connected once the "indicator status update" is enabled by 1.763 + * AT+CMER command. See 4.2.1 in Bluetooth hands-free profile 1.6 1.764 + * for more details. 1.765 + */ 1.766 + if (mCMER) { 1.767 + mSlcConnected = true; 1.768 + } 1.769 + 1.770 + // If we get internal request for SCO connection, 1.771 + // setup SCO after Service Level Connection established. 1.772 + if (mConnectScoRequest) { 1.773 + mConnectScoRequest = false; 1.774 + ConnectSco(); 1.775 + } 1.776 + } else if (msg.Find("AT+CMEE=") != -1) { 1.777 + ParseAtCommand(msg, 8, atCommandValues); 1.778 + 1.779 + if (atCommandValues.IsEmpty()) { 1.780 + BT_WARNING("Could't get the value of command [AT+CMEE=]"); 1.781 + goto respond_with_ok; 1.782 + } 1.783 + 1.784 + // AT+CMEE = 0: +CME ERROR shall not be used 1.785 + // AT+CMEE = 1: use numeric <err> 1.786 + // AT+CMEE = 2: use verbose <err> 1.787 + mCMEE = !atCommandValues[0].EqualsLiteral("0"); 1.788 +#ifdef MOZ_B2G_RIL 1.789 + } else if (msg.Find("AT+COPS=") != -1) { 1.790 + ParseAtCommand(msg, 8, atCommandValues); 1.791 + 1.792 + if (atCommandValues.Length() != 2) { 1.793 + BT_WARNING("Could't get the value of command [AT+COPS=]"); 1.794 + goto respond_with_ok; 1.795 + } 1.796 + 1.797 + // Handsfree only support AT+COPS=3,0 1.798 + if (!atCommandValues[0].EqualsLiteral("3") || 1.799 + !atCommandValues[1].EqualsLiteral("0")) { 1.800 + if (mCMEE) { 1.801 + SendCommand(RESPONSE_CME_ERROR, BluetoothCmeError::OPERATION_NOT_SUPPORTED); 1.802 + } else { 1.803 + SendLine("ERROR"); 1.804 + } 1.805 + return; 1.806 + } 1.807 + } else if (msg.Find("AT+COPS?") != -1) { 1.808 + nsAutoCString message("+COPS: "); 1.809 + message.AppendInt(mNetworkSelectionMode); 1.810 + message.AppendLiteral(",0,\""); 1.811 + message.Append(NS_ConvertUTF16toUTF8(mOperatorName)); 1.812 + message.AppendLiteral("\""); 1.813 + SendLine(message.get()); 1.814 + } else if (msg.Find("AT+VTS=") != -1) { 1.815 + ParseAtCommand(msg, 7, atCommandValues); 1.816 + 1.817 + if (atCommandValues.Length() != 1) { 1.818 + BT_WARNING("Couldn't get the value of command [AT+VTS=]"); 1.819 + goto respond_with_ok; 1.820 + } 1.821 + 1.822 + if (IsValidDtmf(atCommandValues[0].get()[0])) { 1.823 + nsAutoCString message("VTS="); 1.824 + message += atCommandValues[0].get()[0]; 1.825 + NotifyDialer(NS_ConvertUTF8toUTF16(message)); 1.826 + } 1.827 +#endif // MOZ_B2G_RIL 1.828 + } else if (msg.Find("AT+VGM=") != -1) { 1.829 + ParseAtCommand(msg, 7, atCommandValues); 1.830 + 1.831 + if (atCommandValues.IsEmpty()) { 1.832 + BT_WARNING("Couldn't get the value of command [AT+VGM]"); 1.833 + goto respond_with_ok; 1.834 + } 1.835 + 1.836 + nsresult rv; 1.837 + int vgm = atCommandValues[0].ToInteger(&rv); 1.838 + if (NS_FAILED(rv)) { 1.839 + BT_WARNING("Failed to extract microphone volume from bluetooth headset!"); 1.840 + goto respond_with_ok; 1.841 + } 1.842 + 1.843 + if (vgm < 0 || vgm > 15) { 1.844 + BT_WARNING("Received invalid VGM value"); 1.845 + goto respond_with_ok; 1.846 + } 1.847 + 1.848 + mCurrentVgm = vgm; 1.849 +#ifdef MOZ_B2G_RIL 1.850 + } else if (msg.Find("AT+CHLD=?") != -1) { 1.851 + SendLine("+CHLD: (0,1,2,3)"); 1.852 + } else if (msg.Find("AT+CHLD=") != -1) { 1.853 + ParseAtCommand(msg, 8, atCommandValues); 1.854 + 1.855 + if (atCommandValues.IsEmpty()) { 1.856 + BT_WARNING("Could't get the value of command [AT+CHLD=]"); 1.857 + goto respond_with_ok; 1.858 + } 1.859 + 1.860 + /** 1.861 + * The following three cases are supported: 1.862 + * AT+CHLD=0 - Releases all held calls or sets User Determined User Busy 1.863 + * (UDUB) for a waiting call 1.864 + * AT+CHLD=1 - Releases active calls and accepts the other (held or 1.865 + * waiting) call 1.866 + * AT+CHLD=2 - Places active calls on hold and accepts the other (held 1.867 + * or waiting) call 1.868 + * AT+CHLD=3 - Adds a held call to the conversation. 1.869 + * 1.870 + * The following cases are NOT supported yet: 1.871 + * AT+CHLD=1<idx>, AT+CHLD=2<idx>, AT+CHLD=4 1.872 + * Please see 4.33.2 in Bluetooth hands-free profile 1.6 for more 1.873 + * information. 1.874 + */ 1.875 + char chld = atCommandValues[0][0]; 1.876 + bool valid = true; 1.877 + if (atCommandValues[0].Length() > 1) { 1.878 + BT_WARNING("No index should be included in command [AT+CHLD]"); 1.879 + valid = false; 1.880 + } else if (chld == '4') { 1.881 + BT_WARNING("The value of command [AT+CHLD] is not supported"); 1.882 + valid = false; 1.883 + } else if (chld == '0') { 1.884 + // We need to rename these dialer commands for better readability 1.885 + // and expandability. 1.886 + // See bug 884190 for more information. 1.887 + NotifyDialer(NS_LITERAL_STRING("CHLD=0")); 1.888 + } else if (chld == '1') { 1.889 + NotifyDialer(NS_LITERAL_STRING("CHLD=1")); 1.890 + } else if (chld == '2') { 1.891 + NotifyDialer(NS_LITERAL_STRING("CHLD=2")); 1.892 + } else if (chld == '3') { 1.893 + NotifyDialer(NS_LITERAL_STRING("CHLD=3")); 1.894 + } else { 1.895 + BT_WARNING("Wrong value of command [AT+CHLD]"); 1.896 + valid = false; 1.897 + } 1.898 + 1.899 + if (!valid) { 1.900 + SendLine("ERROR"); 1.901 + return; 1.902 + } 1.903 +#endif // MOZ_B2G_RIL 1.904 + } else if (msg.Find("AT+VGS=") != -1) { 1.905 + // Adjust volume by headset 1.906 + mReceiveVgsFlag = true; 1.907 + ParseAtCommand(msg, 7, atCommandValues); 1.908 + 1.909 + if (atCommandValues.IsEmpty()) { 1.910 + BT_WARNING("Could't get the value of command [AT+VGS=]"); 1.911 + goto respond_with_ok; 1.912 + } 1.913 + 1.914 + nsresult rv; 1.915 + int newVgs = atCommandValues[0].ToInteger(&rv); 1.916 + if (NS_FAILED(rv)) { 1.917 + BT_WARNING("Failed to extract volume value from bluetooth headset!"); 1.918 + goto respond_with_ok; 1.919 + } 1.920 + 1.921 + if (newVgs == mCurrentVgs) { 1.922 + goto respond_with_ok; 1.923 + } 1.924 + 1.925 + if (newVgs < 0 || newVgs > 15) { 1.926 + BT_WARNING("Received invalid VGS value"); 1.927 + goto respond_with_ok; 1.928 + } 1.929 + 1.930 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.931 + if (!os) { 1.932 + BT_WARNING("Failed to get observer service!"); 1.933 + goto respond_with_ok; 1.934 + } 1.935 + 1.936 + nsString data; 1.937 + data.AppendInt(newVgs); 1.938 + os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get()); 1.939 +#ifdef MOZ_B2G_RIL 1.940 + } else if ((msg.Find("AT+BLDN") != -1) || (msg.Find("ATD>") != -1)) { 1.941 + // Dialer app of FFOS v1 does not have plan to support Memory Dailing. 1.942 + // However, in order to pass Bluetooth HFP certification, we still have to 1.943 + // make a call when we receive AT command 'ATD>n'. 1.944 + mDialingRequestProcessed = false; 1.945 + 1.946 + if (msg.Find("AT+BLDN") != -1) { 1.947 + NotifyDialer(NS_LITERAL_STRING("BLDN")); 1.948 + } else { 1.949 + NotifyDialer(NS_ConvertUTF8toUTF16(msg)); 1.950 + } 1.951 + 1.952 + MessageLoop::current()-> 1.953 + PostDelayedTask(FROM_HERE, new RespondToBLDNTask(), 1.954 + sWaitingForDialingInterval); 1.955 + 1.956 + // Don't send response 'OK' here because we'll respond later in either 1.957 + // RespondToBLDNTask or HandleCallStateChanged() 1.958 + return; 1.959 + } else if (msg.Find("ATA") != -1) { 1.960 + NotifyDialer(NS_LITERAL_STRING("ATA")); 1.961 + } else if (msg.Find("AT+CHUP") != -1) { 1.962 + NotifyDialer(NS_LITERAL_STRING("CHUP")); 1.963 + } else if (msg.Find("AT+CLCC") != -1) { 1.964 + SendCommand(RESPONSE_CLCC); 1.965 + } else if (msg.Find("ATD") != -1) { 1.966 + nsAutoCString message(msg), newMsg; 1.967 + int end = message.FindChar(';'); 1.968 + if (end < 0) { 1.969 + BT_WARNING("Could't get the value of command [ATD]"); 1.970 + goto respond_with_ok; 1.971 + } 1.972 + 1.973 + newMsg += nsDependentCSubstring(message, 0, end); 1.974 + NotifyDialer(NS_ConvertUTF8toUTF16(newMsg)); 1.975 + } else if (msg.Find("AT+CLIP=") != -1) { 1.976 + ParseAtCommand(msg, 8, atCommandValues); 1.977 + 1.978 + if (atCommandValues.IsEmpty()) { 1.979 + BT_WARNING("Could't get the value of command [AT+CLIP=]"); 1.980 + goto respond_with_ok; 1.981 + } 1.982 + 1.983 + mCLIP = atCommandValues[0].EqualsLiteral("1"); 1.984 + } else if (msg.Find("AT+CCWA=") != -1) { 1.985 + ParseAtCommand(msg, 8, atCommandValues); 1.986 + 1.987 + if (atCommandValues.IsEmpty()) { 1.988 + BT_WARNING("Could't get the value of command [AT+CCWA=]"); 1.989 + goto respond_with_ok; 1.990 + } 1.991 + 1.992 + mCCWA = atCommandValues[0].EqualsLiteral("1"); 1.993 + } else if (msg.Find("AT+CKPD") != -1) { 1.994 + if (!sStopSendingRingFlag) { 1.995 + // Bluetooth HSP spec 4.2.2 1.996 + // There is an incoming call, notify Dialer to pick up the phone call 1.997 + // and SCO will be established after we get the CallStateChanged event 1.998 + // indicating the call is answered successfully. 1.999 + NotifyDialer(NS_LITERAL_STRING("ATA")); 1.1000 + } else { 1.1001 + if (!IsScoConnected()) { 1.1002 + // Bluetooth HSP spec 4.3 1.1003 + // If there's no SCO, set up a SCO link. 1.1004 + ConnectSco(); 1.1005 + } else if (!mFirstCKPD) { 1.1006 + // Bluetooth HSP spec 4.5 1.1007 + // There are two ways to release SCO: sending CHUP to dialer or closing 1.1008 + // SCO socket directly. We notify dialer only if there is at least one 1.1009 + // active call. 1.1010 + if (mCurrentCallArray.Length() > 1) { 1.1011 + NotifyDialer(NS_LITERAL_STRING("CHUP")); 1.1012 + } else { 1.1013 + DisconnectSco(); 1.1014 + } 1.1015 + } else { 1.1016 + // Three conditions have to be matched to come in here: 1.1017 + // (1) Not sending RING indicator 1.1018 + // (2) A SCO link exists 1.1019 + // (3) This is the very first AT+CKPD=200 of this session 1.1020 + // It is the case of Figure 4.3, Bluetooth HSP spec. Do nothing. 1.1021 + BT_WARNING("AT+CKPD=200: Do nothing"); 1.1022 + } 1.1023 + } 1.1024 + 1.1025 + mFirstCKPD = false; 1.1026 + } else if (msg.Find("AT+CNUM") != -1) { 1.1027 + if (!mMsisdn.IsEmpty()) { 1.1028 + nsAutoCString message("+CNUM: ,\""); 1.1029 + message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get()); 1.1030 + message.AppendLiteral("\","); 1.1031 + message.AppendInt(TOA_UNKNOWN); 1.1032 + message.AppendLiteral(",,4"); 1.1033 + SendLine(message.get()); 1.1034 + } 1.1035 + } else if (msg.Find("AT+BIA=") != -1) { 1.1036 + ParseAtCommand(msg, 7, atCommandValues); 1.1037 + 1.1038 + for (uint8_t i = 0; i < atCommandValues.Length(); i++) { 1.1039 + CINDType indicatorType = (CINDType) (i + 1); 1.1040 + if (indicatorType >= (int)ArrayLength(sCINDItems)) { 1.1041 + // Ignore excess parameters at the end 1.1042 + break; 1.1043 + } 1.1044 + 1.1045 + if (!IsMandatoryIndicator(indicatorType)) { 1.1046 + /** 1.1047 + * Accept only following indicator states: 1.1048 + * - "1": activate 1.1049 + * - "0": deactivate 1.1050 + * - "" : maintain current state 1.1051 + * Otherwise we regard the command incorrectly formatted. 1.1052 + */ 1.1053 + if (atCommandValues[i].EqualsLiteral("1")) { 1.1054 + sCINDItems[indicatorType].activated = 1; 1.1055 + } else if (atCommandValues[i].EqualsLiteral("0")) { 1.1056 + sCINDItems[indicatorType].activated = 0; 1.1057 + } else if (!atCommandValues[i].EqualsLiteral("")) { 1.1058 + SendLine("ERROR"); 1.1059 + return; 1.1060 + } 1.1061 + } else { 1.1062 + // Ignore requests to activate/deactivate mandatory indicators 1.1063 + } 1.1064 + } 1.1065 +#endif // MOZ_B2G_RIL 1.1066 + } else { 1.1067 + nsCString warningMsg; 1.1068 + warningMsg.Append(NS_LITERAL_CSTRING("Unsupported AT command: ")); 1.1069 + warningMsg.Append(msg); 1.1070 + warningMsg.Append(NS_LITERAL_CSTRING(", reply with ERROR")); 1.1071 + BT_WARNING(warningMsg.get()); 1.1072 + 1.1073 + SendLine("ERROR"); 1.1074 + return; 1.1075 + } 1.1076 + 1.1077 +respond_with_ok: 1.1078 + // We always respond to remote device with "OK" in general cases. 1.1079 + SendLine("OK"); 1.1080 +} 1.1081 + 1.1082 +void 1.1083 +BluetoothHfpManager::Connect(const nsAString& aDeviceAddress, 1.1084 + BluetoothProfileController* aController) 1.1085 +{ 1.1086 + MOZ_ASSERT(NS_IsMainThread()); 1.1087 + MOZ_ASSERT(aController && !mController); 1.1088 + 1.1089 + BluetoothService* bs = BluetoothService::Get(); 1.1090 + if (!bs || sInShutdown) { 1.1091 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1092 + return; 1.1093 + } 1.1094 + 1.1095 + if (mSocket) { 1.1096 + if (mDeviceAddress == aDeviceAddress) { 1.1097 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED)); 1.1098 + } else { 1.1099 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_REACHED_CONNECTION_LIMIT)); 1.1100 + } 1.1101 + return; 1.1102 + } 1.1103 + 1.1104 + nsString uuid; 1.1105 + BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid); 1.1106 + 1.1107 + if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) { 1.1108 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1109 + return; 1.1110 + } 1.1111 + 1.1112 + // Stop listening because currently we only support one connection at a time. 1.1113 + if (mHandsfreeSocket) { 1.1114 + mHandsfreeSocket->Disconnect(); 1.1115 + mHandsfreeSocket = nullptr; 1.1116 + } 1.1117 + 1.1118 + if (mHeadsetSocket) { 1.1119 + mHeadsetSocket->Disconnect(); 1.1120 + mHeadsetSocket = nullptr; 1.1121 + } 1.1122 + 1.1123 + mController = aController; 1.1124 + mSocket = 1.1125 + new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true); 1.1126 +} 1.1127 + 1.1128 +bool 1.1129 +BluetoothHfpManager::Listen() 1.1130 +{ 1.1131 + MOZ_ASSERT(NS_IsMainThread()); 1.1132 + 1.1133 + if (sInShutdown) { 1.1134 + BT_WARNING("Listen called while in shutdown!"); 1.1135 + return false; 1.1136 + } 1.1137 + 1.1138 + if (mSocket) { 1.1139 + BT_WARNING("mSocket exists. Failed to listen."); 1.1140 + return false; 1.1141 + } 1.1142 + 1.1143 + if (!mHandsfreeSocket) { 1.1144 + mHandsfreeSocket = 1.1145 + new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true); 1.1146 + 1.1147 + if (!mHandsfreeSocket->Listen( 1.1148 + BluetoothReservedChannels::CHANNEL_HANDSFREE_AG)) { 1.1149 + BT_WARNING("[HFP] Can't listen on RFCOMM socket!"); 1.1150 + mHandsfreeSocket = nullptr; 1.1151 + return false; 1.1152 + } 1.1153 + } 1.1154 + 1.1155 + if (!mHeadsetSocket) { 1.1156 + mHeadsetSocket = 1.1157 + new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true); 1.1158 + 1.1159 + if (!mHeadsetSocket->Listen( 1.1160 + BluetoothReservedChannels::CHANNEL_HEADSET_AG)) { 1.1161 + BT_WARNING("[HSP] Can't listen on RFCOMM socket!"); 1.1162 + mHandsfreeSocket->Disconnect(); 1.1163 + mHandsfreeSocket = nullptr; 1.1164 + mHeadsetSocket = nullptr; 1.1165 + return false; 1.1166 + } 1.1167 + } 1.1168 + 1.1169 + return true; 1.1170 +} 1.1171 + 1.1172 +void 1.1173 +BluetoothHfpManager::Disconnect(BluetoothProfileController* aController) 1.1174 +{ 1.1175 + MOZ_ASSERT(NS_IsMainThread()); 1.1176 + 1.1177 + if (!mSocket) { 1.1178 + if (aController) { 1.1179 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED)); 1.1180 + } 1.1181 + return; 1.1182 + } 1.1183 + 1.1184 + MOZ_ASSERT(!mController); 1.1185 + 1.1186 + mController = aController; 1.1187 + mSocket->Disconnect(); 1.1188 +} 1.1189 + 1.1190 +#ifdef MOZ_B2G_RIL 1.1191 +void 1.1192 +BluetoothHfpManager::SendCCWA(const nsAString& aNumber, int aType) 1.1193 +{ 1.1194 + if (mCCWA) { 1.1195 + nsAutoCString ccwaMsg("+CCWA: \""); 1.1196 + ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber)); 1.1197 + ccwaMsg.AppendLiteral("\","); 1.1198 + ccwaMsg.AppendInt(aType); 1.1199 + SendLine(ccwaMsg.get()); 1.1200 + } 1.1201 +} 1.1202 + 1.1203 +bool 1.1204 +BluetoothHfpManager::SendCLCC(const Call& aCall, int aIndex) 1.1205 +{ 1.1206 + if (aCall.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { 1.1207 + return true; 1.1208 + } 1.1209 + 1.1210 + nsAutoCString message(RESPONSE_CLCC); 1.1211 + message.AppendInt(aIndex); 1.1212 + message.AppendLiteral(","); 1.1213 + message.AppendInt(aCall.mDirection); 1.1214 + message.AppendLiteral(","); 1.1215 + 1.1216 + int status = 0; 1.1217 + switch (aCall.mState) { 1.1218 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.1219 + if (mPhoneType == PhoneType::CDMA && aIndex == 1) { 1.1220 + status = (mCdmaSecondCall.IsActive()) ? 1 : 0; 1.1221 + } 1.1222 + message.AppendInt(status); 1.1223 + break; 1.1224 + case nsITelephonyProvider::CALL_STATE_HELD: 1.1225 + message.AppendInt(1); 1.1226 + break; 1.1227 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1228 + message.AppendInt(2); 1.1229 + break; 1.1230 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.1231 + message.AppendInt(3); 1.1232 + break; 1.1233 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.1234 + if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) { 1.1235 + message.AppendInt(4); 1.1236 + } else { 1.1237 + message.AppendInt(5); 1.1238 + } 1.1239 + break; 1.1240 + default: 1.1241 + BT_WARNING("Not handling call status for CLCC"); 1.1242 + break; 1.1243 + } 1.1244 + 1.1245 + message.AppendLiteral(",0,0,\""); 1.1246 + message.Append(NS_ConvertUTF16toUTF8(aCall.mNumber)); 1.1247 + message.AppendLiteral("\","); 1.1248 + message.AppendInt(aCall.mType); 1.1249 + 1.1250 + return SendLine(message.get()); 1.1251 +} 1.1252 +#endif // MOZ_B2G_RIL 1.1253 + 1.1254 +bool 1.1255 +BluetoothHfpManager::SendLine(const char* aMessage) 1.1256 +{ 1.1257 + MOZ_ASSERT(mSocket); 1.1258 + 1.1259 + nsAutoCString msg; 1.1260 + 1.1261 + msg.AppendLiteral(kHfpCrlf); 1.1262 + msg.Append(aMessage); 1.1263 + msg.AppendLiteral(kHfpCrlf); 1.1264 + 1.1265 + return mSocket->SendSocketData(msg); 1.1266 +} 1.1267 + 1.1268 +bool 1.1269 +BluetoothHfpManager::SendCommand(const char* aCommand, uint32_t aValue) 1.1270 +{ 1.1271 + if (!IsConnected()) { 1.1272 + BT_WARNING("Trying to SendCommand() without a SLC"); 1.1273 + return false; 1.1274 + } 1.1275 + 1.1276 + nsAutoCString message; 1.1277 + message += aCommand; 1.1278 + 1.1279 + if (!strcmp(aCommand, RESPONSE_CIEV)) { 1.1280 + if (!mCMER || !sCINDItems[aValue].activated) { 1.1281 + // Indicator status update is disabled 1.1282 + return true; 1.1283 + } 1.1284 + 1.1285 + if ((aValue < 1) || (aValue > ArrayLength(sCINDItems) - 1)) { 1.1286 + BT_WARNING("unexpected CINDType for CIEV command"); 1.1287 + return false; 1.1288 + } 1.1289 + 1.1290 + message.AppendInt(aValue); 1.1291 + message.AppendLiteral(","); 1.1292 + message.AppendInt(sCINDItems[aValue].value); 1.1293 + } else if (!strcmp(aCommand, RESPONSE_CIND)) { 1.1294 + if (!aValue) { 1.1295 + // Query for range 1.1296 + for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) { 1.1297 + message.AppendLiteral("(\""); 1.1298 + message.Append(sCINDItems[i].name); 1.1299 + message.AppendLiteral("\",("); 1.1300 + message.Append(sCINDItems[i].range); 1.1301 + message.AppendLiteral(")"); 1.1302 + if (i == (ArrayLength(sCINDItems) - 1)) { 1.1303 + message.AppendLiteral(")"); 1.1304 + break; 1.1305 + } 1.1306 + message.AppendLiteral("),"); 1.1307 + } 1.1308 + } else { 1.1309 + // Query for value 1.1310 + for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) { 1.1311 + message.AppendInt(sCINDItems[i].value); 1.1312 + if (i == (ArrayLength(sCINDItems) - 1)) { 1.1313 + break; 1.1314 + } 1.1315 + message.AppendLiteral(","); 1.1316 + } 1.1317 + } 1.1318 +#ifdef MOZ_B2G_RIL 1.1319 + } else if (!strcmp(aCommand, RESPONSE_CLCC)) { 1.1320 + bool rv = true; 1.1321 + uint32_t callNumbers = mCurrentCallArray.Length(); 1.1322 + uint32_t i; 1.1323 + for (i = 1; i < callNumbers; i++) { 1.1324 + rv &= SendCLCC(mCurrentCallArray[i], i); 1.1325 + } 1.1326 + 1.1327 + if (!mCdmaSecondCall.mNumber.IsEmpty()) { 1.1328 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1329 + MOZ_ASSERT(i == 2); 1.1330 + 1.1331 + rv &= SendCLCC(mCdmaSecondCall, 2); 1.1332 + } 1.1333 + 1.1334 + return rv; 1.1335 +#endif // MOZ_B2G_RIL 1.1336 + } else { 1.1337 + message.AppendInt(aValue); 1.1338 + } 1.1339 + 1.1340 + return SendLine(message.get()); 1.1341 +} 1.1342 + 1.1343 +#ifdef MOZ_B2G_RIL 1.1344 +void 1.1345 +BluetoothHfpManager::UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend) 1.1346 +{ 1.1347 + if (sCINDItems[aType].value != aValue) { 1.1348 + sCINDItems[aType].value = aValue; 1.1349 + if (aSend) { 1.1350 + SendCommand(RESPONSE_CIEV, aType); 1.1351 + } 1.1352 + } 1.1353 +} 1.1354 + 1.1355 +uint32_t 1.1356 +BluetoothHfpManager::FindFirstCall(uint16_t aState) 1.1357 +{ 1.1358 + uint32_t callLength = mCurrentCallArray.Length(); 1.1359 + 1.1360 + for (uint32_t i = 1; i < callLength; ++i) { 1.1361 + if (mCurrentCallArray[i].mState == aState) { 1.1362 + return i; 1.1363 + } 1.1364 + } 1.1365 + 1.1366 + return 0; 1.1367 +} 1.1368 + 1.1369 +uint32_t 1.1370 +BluetoothHfpManager::GetNumberOfCalls(uint16_t aState) 1.1371 +{ 1.1372 + uint32_t num = 0; 1.1373 + uint32_t callLength = mCurrentCallArray.Length(); 1.1374 + 1.1375 + for (uint32_t i = 1; i < callLength; ++i) { 1.1376 + if (mCurrentCallArray[i].mState == aState) { 1.1377 + ++num; 1.1378 + } 1.1379 + } 1.1380 + 1.1381 + return num; 1.1382 +} 1.1383 + 1.1384 +uint32_t 1.1385 +BluetoothHfpManager::GetNumberOfConCalls() 1.1386 +{ 1.1387 + uint32_t num = 0; 1.1388 + uint32_t callLength = mCurrentCallArray.Length(); 1.1389 + 1.1390 + for (uint32_t i = 1; i < callLength; ++i) { 1.1391 + if (mCurrentCallArray[i].mIsConference) { 1.1392 + ++num; 1.1393 + } 1.1394 + } 1.1395 + 1.1396 + return num; 1.1397 +} 1.1398 + 1.1399 +uint32_t 1.1400 +BluetoothHfpManager::GetNumberOfConCalls(uint16_t aState) 1.1401 +{ 1.1402 + uint32_t num = 0; 1.1403 + uint32_t callLength = mCurrentCallArray.Length(); 1.1404 + 1.1405 + for (uint32_t i = 1; i < callLength; ++i) { 1.1406 + if (mCurrentCallArray[i].mIsConference 1.1407 + && mCurrentCallArray[i].mState == aState) { 1.1408 + ++num; 1.1409 + } 1.1410 + } 1.1411 + 1.1412 + return num; 1.1413 +} 1.1414 + 1.1415 +void 1.1416 +BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex, 1.1417 + uint16_t aCallState, 1.1418 + const nsAString& aError, 1.1419 + const nsAString& aNumber, 1.1420 + const bool aIsOutgoing, 1.1421 + const bool aIsConference, 1.1422 + bool aSend) 1.1423 +{ 1.1424 + if (!IsConnected()) { 1.1425 + // Normal case. No need to print out warnings. 1.1426 + return; 1.1427 + } 1.1428 + 1.1429 + // aCallIndex can be UINT32_MAX for the pending outgoing call state update. 1.1430 + // aCallIndex will be updated again after real call state changes. See Bug 1.1431 + // 990467. 1.1432 + if (aCallIndex == UINT32_MAX) { 1.1433 + return; 1.1434 + } 1.1435 + 1.1436 + while (aCallIndex >= mCurrentCallArray.Length()) { 1.1437 + Call call; 1.1438 + mCurrentCallArray.AppendElement(call); 1.1439 + } 1.1440 + 1.1441 + uint16_t prevCallState = mCurrentCallArray[aCallIndex].mState; 1.1442 + mCurrentCallArray[aCallIndex].mState = aCallState; 1.1443 + mCurrentCallArray[aCallIndex].mDirection = !aIsOutgoing; 1.1444 + 1.1445 + bool prevCallIsConference = mCurrentCallArray[aCallIndex].mIsConference; 1.1446 + mCurrentCallArray[aCallIndex].mIsConference = aIsConference; 1.1447 + 1.1448 + // Same logic as implementation in ril_worker.js 1.1449 + if (aNumber.Length() && aNumber[0] == '+') { 1.1450 + mCurrentCallArray[aCallIndex].mType = TOA_INTERNATIONAL; 1.1451 + } 1.1452 + mCurrentCallArray[aCallIndex].mNumber = aNumber; 1.1453 + 1.1454 + nsRefPtr<nsRunnable> sendRingTask; 1.1455 + nsString address; 1.1456 + 1.1457 + switch (aCallState) { 1.1458 + case nsITelephonyProvider::CALL_STATE_HELD: 1.1459 + switch (prevCallState) { 1.1460 + case nsITelephonyProvider::CALL_STATE_CONNECTED: { 1.1461 + uint32_t numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED); 1.1462 + uint32_t numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD); 1.1463 + uint32_t numConCalls = GetNumberOfConCalls(); 1.1464 + 1.1465 + /** 1.1466 + * An active call becomes a held call. 1.1467 + * 1.1468 + * If this call is not a conference call, 1.1469 + * - callheld state = ONHOLD_NOACTIVE if no active call remains; 1.1470 + * - callheld state = ONHOLD_ACTIVE otherwise. 1.1471 + * If this call belongs to a conference call and all other members of 1.1472 + * the conference call have become held calls, 1.1473 + * - callheld state = ONHOLD_NOACTIVE if no active call remains; 1.1474 + * - callheld state = ONHOLD_ACTIVE otherwise. 1.1475 + * 1.1476 + * Note number of active calls may be 0 in-between state transition 1.1477 + * (c1 has become held but c2 has not become active yet), so we regard 1.1478 + * no active call remains if there is no other active/held call 1.1479 + * besides this changed call/group of conference call. 1.1480 + */ 1.1481 + if (!aIsConference) { 1.1482 + if (numActive + numHeld == 1) { 1.1483 + // A single active call is put on hold. 1.1484 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE; 1.1485 + } else { 1.1486 + // An active call is placed on hold or active/held calls swapped. 1.1487 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE; 1.1488 + } 1.1489 + SendCommand(RESPONSE_CIEV, CINDType::CALLHELD); 1.1490 + } else if (GetNumberOfConCalls(nsITelephonyProvider::CALL_STATE_HELD) 1.1491 + == numConCalls) { 1.1492 + if (numActive + numHeld == numConCalls) { 1.1493 + // An active conference call is put on hold. 1.1494 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE; 1.1495 + } else { 1.1496 + // Active calls are placed on hold or active/held calls swapped. 1.1497 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE; 1.1498 + } 1.1499 + SendCommand(RESPONSE_CIEV, CINDType::CALLHELD); 1.1500 + } 1.1501 + break; 1.1502 + } 1.1503 + case nsITelephonyProvider::CALL_STATE_DISCONNECTED: 1.1504 + // The call state changed from DISCONNECTED to HELD. It could happen 1.1505 + // when user held a call before Bluetooth got connected. 1.1506 + if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) { 1.1507 + // callheld = ONHOLD_ACTIVE if an active call already exists. 1.1508 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE; 1.1509 + SendCommand(RESPONSE_CIEV, CINDType::CALLHELD); 1.1510 + } 1.1511 + break; 1.1512 + } 1.1513 + break; 1.1514 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.1515 + if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) { 1.1516 + SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType); 1.1517 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend); 1.1518 + } else { 1.1519 + // Start sending RING indicator to HF 1.1520 + sStopSendingRingFlag = false; 1.1521 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend); 1.1522 + 1.1523 + if (mBSIR) { 1.1524 + // Setup audio connection for in-band ring tone 1.1525 + ConnectSco(); 1.1526 + } 1.1527 + 1.1528 + nsAutoString number(aNumber); 1.1529 + if (!mCLIP) { 1.1530 + number.AssignLiteral(""); 1.1531 + } 1.1532 + 1.1533 + MessageLoop::current()->PostDelayedTask( 1.1534 + FROM_HERE, 1.1535 + new SendRingIndicatorTask(number, 1.1536 + mCurrentCallArray[aCallIndex].mType), 1.1537 + sRingInterval); 1.1538 + } 1.1539 + break; 1.1540 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1541 + if (!mDialingRequestProcessed) { 1.1542 + SendLine("OK"); 1.1543 + mDialingRequestProcessed = true; 1.1544 + } 1.1545 + 1.1546 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING, aSend); 1.1547 + ConnectSco(); 1.1548 + break; 1.1549 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.1550 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend); 1.1551 + 1.1552 + // If there's an ongoing call when the headset is just connected, we have 1.1553 + // to open a sco socket here. 1.1554 + ConnectSco(); 1.1555 + break; 1.1556 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.1557 + /** 1.1558 + * A call becomes active because: 1.1559 + * - user answers an incoming call, 1.1560 + * - user dials a outgoing call and it is answered, or 1.1561 + * - SLC is connected when a call is active. 1.1562 + */ 1.1563 + switch (prevCallState) { 1.1564 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.1565 + case nsITelephonyProvider::CALL_STATE_DISCONNECTED: 1.1566 + // Incoming call, no break 1.1567 + sStopSendingRingFlag = true; 1.1568 + ConnectSco(); 1.1569 + // NO BREAK HERE. continue to next statement 1.1570 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1571 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.1572 + // Outgoing call 1.1573 + UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend); 1.1574 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend); 1.1575 + 1.1576 + if (FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) { 1.1577 + // callheld state = ONHOLD_ACTIVE if a held call already exists. 1.1578 + UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend); 1.1579 + } 1.1580 + break; 1.1581 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.1582 + // User wants to add a held call to the conversation. 1.1583 + // The original connected call becomes a conference call here. 1.1584 + if (aIsConference) { 1.1585 + UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend); 1.1586 + } 1.1587 + break; 1.1588 + case nsITelephonyProvider::CALL_STATE_HELD: 1.1589 + if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) { 1.1590 + if (aIsConference && !prevCallIsConference) { 1.1591 + // The held call was merged and becomes a conference call. 1.1592 + UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend); 1.1593 + } else if (sCINDItems[CINDType::CALLHELD].value == 1.1594 + CallHeldState::ONHOLD_NOACTIVE) { 1.1595 + // The held call(s) become connected call(s). 1.1596 + UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend); 1.1597 + } 1.1598 + } 1.1599 + break; 1.1600 + 1.1601 + default: 1.1602 + BT_WARNING("Not handling state changed"); 1.1603 + } 1.1604 + break; 1.1605 + case nsITelephonyProvider::CALL_STATE_DISCONNECTED: 1.1606 + switch (prevCallState) { 1.1607 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.1608 + // Incoming call, no break 1.1609 + sStopSendingRingFlag = true; 1.1610 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1611 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.1612 + // Outgoing call 1.1613 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend); 1.1614 + break; 1.1615 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.1616 + // No call is ongoing 1.1617 + if (sCINDItems[CINDType::CALLHELD].value == 1.1618 + CallHeldState::NO_CALLHELD) { 1.1619 + UpdateCIND(CINDType::CALL, CallState::NO_CALL, aSend); 1.1620 + } 1.1621 + break; 1.1622 + default: 1.1623 + BT_WARNING("Not handling state changed"); 1.1624 + } 1.1625 + 1.1626 + // Handle held calls separately 1.1627 + if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) { 1.1628 + UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend); 1.1629 + } else if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) { 1.1630 + UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_NOACTIVE, aSend); 1.1631 + } else { 1.1632 + UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend); 1.1633 + } 1.1634 + 1.1635 + // -1 is necessary because call 0 is an invalid (padding) call object. 1.1636 + if (mCurrentCallArray.Length() - 1 == 1.1637 + GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) { 1.1638 + // In order to let user hear busy tone via connected Bluetooth headset, 1.1639 + // we postpone the timing of dropping SCO. 1.1640 + if (!(aError.Equals(NS_LITERAL_STRING("BusyError")))) { 1.1641 + DisconnectSco(); 1.1642 + } else { 1.1643 + // Close Sco later since Dialer is still playing busy tone via HF. 1.1644 + MessageLoop::current()->PostDelayedTask(FROM_HERE, 1.1645 + new CloseScoTask(), 1.1646 + sBusyToneInterval); 1.1647 + } 1.1648 + 1.1649 + ResetCallArray(); 1.1650 + } 1.1651 + break; 1.1652 + default: 1.1653 + BT_WARNING("Not handling state changed"); 1.1654 + break; 1.1655 + } 1.1656 +} 1.1657 + 1.1658 +PhoneType 1.1659 +BluetoothHfpManager::GetPhoneType(const nsAString& aType) 1.1660 +{ 1.1661 + // FIXME: Query phone type from RIL after RIL implements new API (bug 912019) 1.1662 + if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") || 1.1663 + aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") || 1.1664 + aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") || 1.1665 + aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) { 1.1666 + return PhoneType::GSM; 1.1667 + } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") || 1.1668 + aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") || 1.1669 + aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) { 1.1670 + return PhoneType::CDMA; 1.1671 + } 1.1672 + 1.1673 + return PhoneType::NONE; 1.1674 +} 1.1675 + 1.1676 +void 1.1677 +BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber) 1.1678 +{ 1.1679 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1680 + 1.1681 + // Always regard second call as incoming call since v1.2 RIL 1.1682 + // doesn't support outgoing second call in CDMA. 1.1683 + mCdmaSecondCall.mDirection = true; 1.1684 + 1.1685 + mCdmaSecondCall.mNumber = aNumber; 1.1686 + mCdmaSecondCall.mType = (aNumber[0] == '+') ? TOA_INTERNATIONAL : 1.1687 + TOA_UNKNOWN; 1.1688 + 1.1689 + SendCCWA(aNumber, mCdmaSecondCall.mType); 1.1690 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true); 1.1691 +} 1.1692 + 1.1693 +void 1.1694 +BluetoothHfpManager::AnswerWaitingCall() 1.1695 +{ 1.1696 + MOZ_ASSERT(NS_IsMainThread()); 1.1697 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1698 + 1.1699 + // Pick up second call. First call is held now. 1.1700 + mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED; 1.1701 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true); 1.1702 + 1.1703 + sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE; 1.1704 + SendCommand(RESPONSE_CIEV, CINDType::CALLHELD); 1.1705 +} 1.1706 + 1.1707 +void 1.1708 +BluetoothHfpManager::IgnoreWaitingCall() 1.1709 +{ 1.1710 + MOZ_ASSERT(NS_IsMainThread()); 1.1711 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1712 + 1.1713 + mCdmaSecondCall.Reset(); 1.1714 + UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true); 1.1715 +} 1.1716 + 1.1717 +void 1.1718 +BluetoothHfpManager::ToggleCalls() 1.1719 +{ 1.1720 + MOZ_ASSERT(NS_IsMainThread()); 1.1721 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1722 + 1.1723 + // Toggle acitve and held calls 1.1724 + mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ? 1.1725 + nsITelephonyProvider::CALL_STATE_HELD : 1.1726 + nsITelephonyProvider::CALL_STATE_CONNECTED; 1.1727 +} 1.1728 +#endif // MOZ_B2G_RIL 1.1729 + 1.1730 +void 1.1731 +BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket) 1.1732 +{ 1.1733 + MOZ_ASSERT(aSocket); 1.1734 +#ifdef MOZ_B2G_RIL 1.1735 + MOZ_ASSERT(mListener); 1.1736 +#endif 1.1737 + 1.1738 + // Success to create a SCO socket 1.1739 + if (aSocket == mScoSocket) { 1.1740 + OnScoConnectSuccess(); 1.1741 + return; 1.1742 + } 1.1743 + 1.1744 + /** 1.1745 + * If the created connection is an inbound connection, close another server 1.1746 + * socket because currently only one SLC is allowed. After that, we need to 1.1747 + * make sure that both server socket would be nulled out. As for outbound 1.1748 + * connections, we do nothing since sockets have been already handled in 1.1749 + * function Connect(). 1.1750 + */ 1.1751 + if (aSocket == mHandsfreeSocket) { 1.1752 + MOZ_ASSERT(!mSocket); 1.1753 + mIsHsp = false; 1.1754 + mHandsfreeSocket.swap(mSocket); 1.1755 + 1.1756 + mHeadsetSocket->Disconnect(); 1.1757 + mHeadsetSocket = nullptr; 1.1758 + } else if (aSocket == mHeadsetSocket) { 1.1759 + MOZ_ASSERT(!mSocket); 1.1760 + mIsHsp = true; 1.1761 + mHeadsetSocket.swap(mSocket); 1.1762 + 1.1763 + mHandsfreeSocket->Disconnect(); 1.1764 + mHandsfreeSocket = nullptr; 1.1765 + } 1.1766 + 1.1767 +#ifdef MOZ_B2G_RIL 1.1768 + // Enumerate current calls 1.1769 + mListener->EnumerateCalls(); 1.1770 + 1.1771 + mFirstCKPD = true; 1.1772 +#endif 1.1773 + 1.1774 + // Cache device path for NotifySettings() since we can't get socket address 1.1775 + // when a headset disconnect with us 1.1776 + mSocket->GetAddress(mDeviceAddress); 1.1777 + NotifyConnectionStatusChanged( 1.1778 + NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); 1.1779 + 1.1780 + ListenSco(); 1.1781 + 1.1782 + OnConnect(EmptyString()); 1.1783 +} 1.1784 + 1.1785 +void 1.1786 +BluetoothHfpManager::OnSocketConnectError(BluetoothSocket* aSocket) 1.1787 +{ 1.1788 + // Failed to create a SCO socket 1.1789 + if (aSocket == mScoSocket) { 1.1790 + OnScoConnectError(); 1.1791 + return; 1.1792 + } 1.1793 + 1.1794 + mHandsfreeSocket = nullptr; 1.1795 + mHeadsetSocket = nullptr; 1.1796 + 1.1797 + OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); 1.1798 +} 1.1799 + 1.1800 +void 1.1801 +BluetoothHfpManager::OnSocketDisconnect(BluetoothSocket* aSocket) 1.1802 +{ 1.1803 + MOZ_ASSERT(aSocket); 1.1804 + 1.1805 + if (aSocket == mScoSocket) { 1.1806 + // SCO socket is closed 1.1807 + OnScoDisconnect(); 1.1808 + return; 1.1809 + } 1.1810 + 1.1811 + if (aSocket != mSocket) { 1.1812 + // Do nothing when a listening server socket is closed. 1.1813 + return; 1.1814 + } 1.1815 + 1.1816 + DisconnectSco(); 1.1817 + 1.1818 + NotifyConnectionStatusChanged( 1.1819 + NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); 1.1820 + OnDisconnect(EmptyString()); 1.1821 + 1.1822 + Reset(); 1.1823 +} 1.1824 + 1.1825 +void 1.1826 +BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress) 1.1827 +{ 1.1828 + // UpdateSdpRecord() is not called so this callback function should not 1.1829 + // be invoked. 1.1830 + MOZ_ASSUME_UNREACHABLE("UpdateSdpRecords() should be called somewhere"); 1.1831 +} 1.1832 + 1.1833 +void 1.1834 +BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress, 1.1835 + const nsAString& aServiceUuid, 1.1836 + int aChannel) 1.1837 +{ 1.1838 + MOZ_ASSERT(NS_IsMainThread()); 1.1839 + MOZ_ASSERT(!aDeviceAddress.IsEmpty()); 1.1840 + 1.1841 + BluetoothService* bs = BluetoothService::Get(); 1.1842 + NS_ENSURE_TRUE_VOID(bs); 1.1843 + 1.1844 + if (aChannel < 0) { 1.1845 + // If we can't find Handsfree server channel number on the remote device, 1.1846 + // try to create HSP connection instead. 1.1847 + nsString hspUuid; 1.1848 + BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, hspUuid); 1.1849 + 1.1850 + if (aServiceUuid.Equals(hspUuid)) { 1.1851 + OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND)); 1.1852 + } else if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, 1.1853 + hspUuid, this))) { 1.1854 + OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1855 + } else { 1.1856 + mIsHsp = true; 1.1857 + } 1.1858 + 1.1859 + return; 1.1860 + } 1.1861 + 1.1862 + MOZ_ASSERT(mSocket); 1.1863 + 1.1864 + if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) { 1.1865 + OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); 1.1866 + } 1.1867 +} 1.1868 + 1.1869 +void 1.1870 +BluetoothHfpManager::OnScoConnectSuccess() 1.1871 +{ 1.1872 + // For active connection request, we need to reply the DOMRequest 1.1873 + if (mScoRunnable) { 1.1874 + DispatchBluetoothReply(mScoRunnable, 1.1875 + BluetoothValue(true), EmptyString()); 1.1876 + mScoRunnable = nullptr; 1.1877 + } 1.1878 + 1.1879 + NotifyConnectionStatusChanged( 1.1880 + NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID)); 1.1881 + 1.1882 + mScoSocketStatus = mScoSocket->GetConnectionStatus(); 1.1883 +} 1.1884 + 1.1885 +void 1.1886 +BluetoothHfpManager::OnScoConnectError() 1.1887 +{ 1.1888 + if (mScoRunnable) { 1.1889 + NS_NAMED_LITERAL_STRING(replyError, "Failed to create SCO socket!"); 1.1890 + DispatchBluetoothReply(mScoRunnable, BluetoothValue(), replyError); 1.1891 + 1.1892 + mScoRunnable = nullptr; 1.1893 + } 1.1894 + 1.1895 + ListenSco(); 1.1896 +} 1.1897 + 1.1898 +void 1.1899 +BluetoothHfpManager::OnScoDisconnect() 1.1900 +{ 1.1901 + if (mScoSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) { 1.1902 + ListenSco(); 1.1903 + NotifyConnectionStatusChanged( 1.1904 + NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID)); 1.1905 + } 1.1906 +} 1.1907 + 1.1908 +bool 1.1909 +BluetoothHfpManager::IsConnected() 1.1910 +{ 1.1911 + if (mSocket) { 1.1912 + return mSocket->GetConnectionStatus() == 1.1913 + SocketConnectionStatus::SOCKET_CONNECTED; 1.1914 + } 1.1915 + 1.1916 + return false; 1.1917 +} 1.1918 + 1.1919 +void 1.1920 +BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress) 1.1921 +{ 1.1922 + return mSocket->GetAddress(aDeviceAddress); 1.1923 +} 1.1924 + 1.1925 +bool 1.1926 +BluetoothHfpManager::ConnectSco(BluetoothReplyRunnable* aRunnable) 1.1927 +{ 1.1928 + MOZ_ASSERT(NS_IsMainThread()); 1.1929 + 1.1930 + NS_ENSURE_TRUE(!sInShutdown, false); 1.1931 + NS_ENSURE_TRUE(IsConnected(), false); 1.1932 + 1.1933 + SocketConnectionStatus status = mScoSocket->GetConnectionStatus(); 1.1934 + if (status == SocketConnectionStatus::SOCKET_CONNECTED || 1.1935 + status == SocketConnectionStatus::SOCKET_CONNECTING || 1.1936 + (mScoRunnable && (mScoRunnable != aRunnable))) { 1.1937 + BT_WARNING("SCO connection exists or is being established"); 1.1938 + return false; 1.1939 + } 1.1940 + 1.1941 + // If we are not using HSP, we have to make sure Service Level Connection 1.1942 + // established before we start to set up SCO (synchronous connection). 1.1943 + if (!mSlcConnected && !mIsHsp) { 1.1944 + mConnectScoRequest = true; 1.1945 + BT_WARNING("ConnectSco called before Service Level Connection established"); 1.1946 + return false; 1.1947 + } 1.1948 + 1.1949 + // Stop listening 1.1950 + mScoSocket->Disconnect(); 1.1951 + 1.1952 + mScoSocket->Connect(NS_ConvertUTF16toUTF8(mDeviceAddress), -1); 1.1953 + mScoSocketStatus = mScoSocket->GetConnectionStatus(); 1.1954 + 1.1955 + mScoRunnable = aRunnable; 1.1956 + return true; 1.1957 +} 1.1958 + 1.1959 +bool 1.1960 +BluetoothHfpManager::DisconnectSco() 1.1961 +{ 1.1962 + if (!IsScoConnected()) { 1.1963 + BT_WARNING("SCO has been already disconnected."); 1.1964 + return false; 1.1965 + } 1.1966 + 1.1967 + mScoSocket->Disconnect(); 1.1968 + return true; 1.1969 +} 1.1970 + 1.1971 +bool 1.1972 +BluetoothHfpManager::ListenSco() 1.1973 +{ 1.1974 + MOZ_ASSERT(NS_IsMainThread()); 1.1975 + 1.1976 + if (sInShutdown) { 1.1977 + BT_WARNING("ListenSco called while in shutdown!"); 1.1978 + return false; 1.1979 + } 1.1980 + 1.1981 + if (mScoSocket->GetConnectionStatus() == 1.1982 + SocketConnectionStatus::SOCKET_LISTENING) { 1.1983 + BT_WARNING("SCO socket has been already listening"); 1.1984 + return false; 1.1985 + } 1.1986 + 1.1987 + mScoSocket->Disconnect(); 1.1988 + 1.1989 + if (!mScoSocket->Listen(-1)) { 1.1990 + BT_WARNING("Can't listen on SCO socket!"); 1.1991 + return false; 1.1992 + } 1.1993 + 1.1994 + mScoSocketStatus = mScoSocket->GetConnectionStatus(); 1.1995 + return true; 1.1996 +} 1.1997 + 1.1998 +bool 1.1999 +BluetoothHfpManager::IsScoConnected() 1.2000 +{ 1.2001 + if (mScoSocket) { 1.2002 + return mScoSocket->GetConnectionStatus() == 1.2003 + SocketConnectionStatus::SOCKET_CONNECTED; 1.2004 + } 1.2005 + return false; 1.2006 +} 1.2007 + 1.2008 +void 1.2009 +BluetoothHfpManager::OnConnect(const nsAString& aErrorStr) 1.2010 +{ 1.2011 + MOZ_ASSERT(NS_IsMainThread()); 1.2012 + 1.2013 + // When we failed to create a socket, restart listening. 1.2014 + if (!aErrorStr.IsEmpty()) { 1.2015 + mSocket = nullptr; 1.2016 + Listen(); 1.2017 + } 1.2018 + 1.2019 + /** 1.2020 + * On the one hand, notify the controller that we've done for outbound 1.2021 + * connections. On the other hand, we do nothing for inbound connections. 1.2022 + */ 1.2023 + NS_ENSURE_TRUE_VOID(mController); 1.2024 + 1.2025 + nsRefPtr<BluetoothProfileController> controller = mController.forget(); 1.2026 + controller->NotifyCompletion(aErrorStr); 1.2027 +} 1.2028 + 1.2029 +void 1.2030 +BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr) 1.2031 +{ 1.2032 + MOZ_ASSERT(NS_IsMainThread()); 1.2033 + 1.2034 + // Start listening 1.2035 + mSocket = nullptr; 1.2036 + Listen(); 1.2037 + 1.2038 + /** 1.2039 + * On the one hand, notify the controller that we've done for outbound 1.2040 + * connections. On the other hand, we do nothing for inbound connections. 1.2041 + */ 1.2042 + NS_ENSURE_TRUE_VOID(mController); 1.2043 + 1.2044 + nsRefPtr<BluetoothProfileController> controller = mController.forget(); 1.2045 + controller->NotifyCompletion(aErrorStr); 1.2046 +} 1.2047 + 1.2048 +NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver) 1.2049 +