1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1429 @@ 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 +#include "BluetoothProfileController.h" 1.14 +#include "BluetoothUtils.h" 1.15 + 1.16 +#include "jsapi.h" 1.17 +#include "mozilla/dom/bluetooth/BluetoothTypes.h" 1.18 +#include "mozilla/Services.h" 1.19 +#include "mozilla/StaticPtr.h" 1.20 +#include "nsContentUtils.h" 1.21 +#include "nsIAudioManager.h" 1.22 +#include "nsIDOMIccInfo.h" 1.23 +#include "nsIDOMMobileConnection.h" 1.24 +#include "nsIIccProvider.h" 1.25 +#include "nsIMobileConnectionProvider.h" 1.26 +#include "nsIObserverService.h" 1.27 +#include "nsISettingsService.h" 1.28 +#include "nsITelephonyProvider.h" 1.29 +#include "nsRadioInterfaceLayer.h" 1.30 +#include "nsServiceManagerUtils.h" 1.31 +#include "nsThreadUtils.h" 1.32 + 1.33 +#define MOZSETTINGS_CHANGED_ID "mozsettings-changed" 1.34 +#define AUDIO_VOLUME_BT_SCO_ID "audio.volume.bt_sco" 1.35 + 1.36 +/** 1.37 + * Dispatch task with arguments to main thread. 1.38 + */ 1.39 +#define BT_HF_DISPATCH_MAIN(args...) \ 1.40 + NS_DispatchToMainThread(new MainThreadTask(args)) 1.41 + 1.42 +/** 1.43 + * Process bluedroid callbacks with corresponding handlers. 1.44 + */ 1.45 +#define BT_HF_PROCESS_CB(func, args...) \ 1.46 + do { \ 1.47 + NS_ENSURE_TRUE_VOID(sBluetoothHfpManager); \ 1.48 + sBluetoothHfpManager->func(args); \ 1.49 + } while(0) 1.50 + 1.51 +using namespace mozilla; 1.52 +using namespace mozilla::ipc; 1.53 +USING_BLUETOOTH_NAMESPACE 1.54 + 1.55 +namespace { 1.56 + StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager; 1.57 + static const bthf_interface_t* sBluetoothHfpInterface = nullptr; 1.58 + 1.59 + bool sInShutdown = false; 1.60 + 1.61 + // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a 1.62 + // magic number. The mechanism should be revised once we can get call history. 1.63 + static int sWaitingForDialingInterval = 2000; //unit: ms 1.64 + 1.65 + // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the 1.66 + // time window set in Dialer and the extra '0.7' second is a magic number. 1.67 + // The mechanism should be revised once we know the exact time at which 1.68 + // Dialer stops playing. 1.69 + static int sBusyToneInterval = 3700; //unit: ms 1.70 +} // anonymous namespace 1.71 + 1.72 +// Main thread task commands 1.73 +enum MainThreadTaskCmd { 1.74 + NOTIFY_CONN_STATE_CHANGED, 1.75 + NOTIFY_DIALER, 1.76 + NOTIFY_SCO_VOLUME_CHANGED, 1.77 + POST_TASK_RESPOND_TO_BLDN, 1.78 + POST_TASK_CLOSE_SCO 1.79 +}; 1.80 + 1.81 +static void 1.82 +ConnectionStateCallback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr) 1.83 +{ 1.84 + BT_HF_PROCESS_CB(ProcessConnectionState, state, bd_addr); 1.85 +} 1.86 + 1.87 +static void 1.88 +AudioStateCallback(bthf_audio_state_t state, bt_bdaddr_t* bd_addr) 1.89 +{ 1.90 + BT_HF_PROCESS_CB(ProcessAudioState, state, bd_addr); 1.91 +} 1.92 + 1.93 +static void 1.94 +VoiceRecognitionCallback(bthf_vr_state_t state) 1.95 +{ 1.96 + // No support 1.97 +} 1.98 + 1.99 +static void 1.100 +AnswerCallCallback() 1.101 +{ 1.102 + BT_HF_PROCESS_CB(ProcessAnswerCall); 1.103 +} 1.104 + 1.105 +static void 1.106 +HangupCallCallback() 1.107 +{ 1.108 + BT_HF_PROCESS_CB(ProcessHangupCall); 1.109 +} 1.110 + 1.111 +static void 1.112 +VolumeControlCallback(bthf_volume_type_t type, int volume) 1.113 +{ 1.114 + BT_HF_PROCESS_CB(ProcessVolumeControl, type, volume); 1.115 +} 1.116 + 1.117 +static void 1.118 +DialCallCallback(char *number) 1.119 +{ 1.120 + BT_HF_PROCESS_CB(ProcessDialCall, number); 1.121 +} 1.122 + 1.123 +static void 1.124 +DtmfCmdCallback(char dtmf) 1.125 +{ 1.126 + BT_HF_PROCESS_CB(ProcessDtmfCmd, dtmf); 1.127 +} 1.128 + 1.129 +static void 1.130 +NoiceReductionCallback(bthf_nrec_t nrec) 1.131 +{ 1.132 + // No support 1.133 +} 1.134 + 1.135 +static void 1.136 +AtChldCallback(bthf_chld_type_t chld) 1.137 +{ 1.138 + BT_HF_PROCESS_CB(ProcessAtChld, chld); 1.139 +} 1.140 + 1.141 +static void 1.142 +AtCnumCallback() 1.143 +{ 1.144 + BT_HF_PROCESS_CB(ProcessAtCnum); 1.145 +} 1.146 + 1.147 +static void 1.148 +AtCindCallback() 1.149 +{ 1.150 + BT_HF_PROCESS_CB(ProcessAtCind); 1.151 +} 1.152 + 1.153 +static void 1.154 +AtCopsCallback() 1.155 +{ 1.156 + BT_HF_PROCESS_CB(ProcessAtCops); 1.157 +} 1.158 + 1.159 +static void 1.160 +AtClccCallback() 1.161 +{ 1.162 + BT_HF_PROCESS_CB(ProcessAtClcc); 1.163 +} 1.164 + 1.165 +static void 1.166 +UnknownAtCallback(char *at_string) 1.167 +{ 1.168 + BT_HF_PROCESS_CB(ProcessUnknownAt, at_string); 1.169 +} 1.170 + 1.171 +static void 1.172 +KeyPressedCallback() 1.173 +{ 1.174 + BT_HF_PROCESS_CB(ProcessKeyPressed); 1.175 +} 1.176 + 1.177 +static bthf_callbacks_t sBluetoothHfpCallbacks = { 1.178 + sizeof(sBluetoothHfpCallbacks), 1.179 + ConnectionStateCallback, 1.180 + AudioStateCallback, 1.181 + VoiceRecognitionCallback, 1.182 + AnswerCallCallback, 1.183 + HangupCallCallback, 1.184 + VolumeControlCallback, 1.185 + DialCallCallback, 1.186 + DtmfCmdCallback, 1.187 + NoiceReductionCallback, 1.188 + AtChldCallback, 1.189 + AtCnumCallback, 1.190 + AtCindCallback, 1.191 + AtCopsCallback, 1.192 + AtClccCallback, 1.193 + UnknownAtCallback, 1.194 + KeyPressedCallback 1.195 +}; 1.196 + 1.197 +static bool 1.198 +IsValidDtmf(const char aChar) { 1.199 + // Valid DTMF: [*#0-9ABCD] 1.200 + return (aChar == '*' || aChar == '#') || 1.201 + (aChar >= '0' && aChar <= '9') || 1.202 + (aChar >= 'A' && aChar <= 'D'); 1.203 +} 1.204 + 1.205 +class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback 1.206 +{ 1.207 +public: 1.208 + NS_DECL_ISUPPORTS 1.209 + 1.210 + NS_IMETHOD 1.211 + Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) 1.212 + { 1.213 + MOZ_ASSERT(NS_IsMainThread()); 1.214 + 1.215 + JSContext *cx = nsContentUtils::GetCurrentJSContext(); 1.216 + NS_ENSURE_TRUE(cx, NS_OK); 1.217 + 1.218 + if (!aResult.isNumber()) { 1.219 + BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!"); 1.220 + return NS_OK; 1.221 + } 1.222 + 1.223 + BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); 1.224 + hfp->mCurrentVgs = aResult.toNumber(); 1.225 + 1.226 + return NS_OK; 1.227 + } 1.228 + 1.229 + NS_IMETHOD 1.230 + HandleError(const nsAString& aName) 1.231 + { 1.232 + BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'"); 1.233 + return NS_OK; 1.234 + } 1.235 +}; 1.236 + 1.237 +class BluetoothHfpManager::CloseScoTask : public Task 1.238 +{ 1.239 +private: 1.240 + void Run() MOZ_OVERRIDE 1.241 + { 1.242 + MOZ_ASSERT(sBluetoothHfpManager); 1.243 + sBluetoothHfpManager->DisconnectSco(); 1.244 + } 1.245 +}; 1.246 + 1.247 +class BluetoothHfpManager::RespondToBLDNTask : public Task 1.248 +{ 1.249 +private: 1.250 + void Run() MOZ_OVERRIDE 1.251 + { 1.252 + MOZ_ASSERT(sBluetoothHfpManager); 1.253 + 1.254 + if (!sBluetoothHfpManager->mDialingRequestProcessed) { 1.255 + sBluetoothHfpManager->mDialingRequestProcessed = true; 1.256 + sBluetoothHfpManager->SendResponse(BTHF_AT_RESPONSE_ERROR); 1.257 + } 1.258 + } 1.259 +}; 1.260 + 1.261 +class BluetoothHfpManager::MainThreadTask : public nsRunnable 1.262 +{ 1.263 +public: 1.264 + MainThreadTask(const int aCommand, 1.265 + const nsAString& aParameter = EmptyString()) 1.266 + : mCommand(aCommand), mParameter(aParameter) 1.267 + { 1.268 + } 1.269 + 1.270 + nsresult Run() 1.271 + { 1.272 + MOZ_ASSERT(NS_IsMainThread()); 1.273 + MOZ_ASSERT(sBluetoothHfpManager); 1.274 + 1.275 + switch (mCommand) { 1.276 + case MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED: 1.277 + sBluetoothHfpManager->NotifyConnectionStateChanged(mParameter); 1.278 + break; 1.279 + case MainThreadTaskCmd::NOTIFY_DIALER: 1.280 + sBluetoothHfpManager->NotifyDialer(mParameter); 1.281 + break; 1.282 + case MainThreadTaskCmd::NOTIFY_SCO_VOLUME_CHANGED: 1.283 + { 1.284 + nsCOMPtr<nsIObserverService> os = 1.285 + mozilla::services::GetObserverService(); 1.286 + NS_ENSURE_TRUE(os, NS_OK); 1.287 + 1.288 + os->NotifyObservers(nullptr, "bluetooth-volume-change", 1.289 + mParameter.get()); 1.290 + } 1.291 + break; 1.292 + case MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN: 1.293 + MessageLoop::current()-> 1.294 + PostDelayedTask(FROM_HERE, new RespondToBLDNTask(), 1.295 + sWaitingForDialingInterval); 1.296 + break; 1.297 + case MainThreadTaskCmd::POST_TASK_CLOSE_SCO: 1.298 + MessageLoop::current()-> 1.299 + PostDelayedTask(FROM_HERE, new CloseScoTask(), 1.300 + sBusyToneInterval); 1.301 + break; 1.302 + default: 1.303 + BT_WARNING("MainThreadTask: Unknown command %d", mCommand); 1.304 + break; 1.305 + } 1.306 + 1.307 + return NS_OK; 1.308 + } 1.309 + 1.310 +private: 1.311 + int mCommand; 1.312 + nsString mParameter; 1.313 +}; 1.314 + 1.315 +NS_IMPL_ISUPPORTS(BluetoothHfpManager::GetVolumeTask, 1.316 + nsISettingsServiceCallback); 1.317 + 1.318 +/** 1.319 + * Call 1.320 + */ 1.321 +Call::Call() 1.322 +{ 1.323 + Reset(); 1.324 +} 1.325 + 1.326 +void 1.327 +Call::Set(const nsAString& aNumber, const bool aIsOutgoing) 1.328 +{ 1.329 + mNumber = aNumber; 1.330 + mDirection = (aIsOutgoing) ? BTHF_CALL_DIRECTION_OUTGOING : 1.331 + BTHF_CALL_DIRECTION_INCOMING; 1.332 + // Same logic as implementation in ril_worker.js 1.333 + if (aNumber.Length() && aNumber[0] == '+') { 1.334 + mType = BTHF_CALL_ADDRTYPE_INTERNATIONAL; 1.335 + } 1.336 +} 1.337 + 1.338 +void 1.339 +Call::Reset() 1.340 +{ 1.341 + mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED; 1.342 + mDirection = BTHF_CALL_DIRECTION_OUTGOING; 1.343 + mNumber.Truncate(); 1.344 + mType = BTHF_CALL_ADDRTYPE_UNKNOWN; 1.345 +} 1.346 + 1.347 +bool 1.348 +Call::IsActive() 1.349 +{ 1.350 + return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED); 1.351 +} 1.352 + 1.353 +/** 1.354 + * BluetoothHfpManager 1.355 + */ 1.356 +BluetoothHfpManager::BluetoothHfpManager() : mPhoneType(PhoneType::NONE) 1.357 +{ 1.358 + Reset(); 1.359 +} 1.360 + 1.361 +void 1.362 +BluetoothHfpManager::ResetCallArray() 1.363 +{ 1.364 + mCurrentCallArray.Clear(); 1.365 + // Append a call object at the beginning of mCurrentCallArray since call 1.366 + // index from RIL starts at 1. 1.367 + Call call; 1.368 + mCurrentCallArray.AppendElement(call); 1.369 + 1.370 + if (mPhoneType == PhoneType::CDMA) { 1.371 + mCdmaSecondCall.Reset(); 1.372 + } 1.373 +} 1.374 + 1.375 +void 1.376 +BluetoothHfpManager::Reset() 1.377 +{ 1.378 + mReceiveVgsFlag = false; 1.379 + mDialingRequestProcessed = true; 1.380 + 1.381 + mConnectionState = BTHF_CONNECTION_STATE_DISCONNECTED; 1.382 + mPrevConnectionState = BTHF_CONNECTION_STATE_DISCONNECTED; 1.383 + mAudioState = BTHF_AUDIO_STATE_DISCONNECTED; 1.384 + 1.385 + // Phone & Device CIND 1.386 + ResetCallArray(); 1.387 + mBattChg = 5; 1.388 + mService = 0; 1.389 + mRoam = 0; 1.390 + mSignal = 0; 1.391 + 1.392 + mController = nullptr; 1.393 +} 1.394 + 1.395 +bool 1.396 +BluetoothHfpManager::Init() 1.397 +{ 1.398 + MOZ_ASSERT(NS_IsMainThread()); 1.399 + 1.400 + NS_ENSURE_TRUE(InitHfpInterface(), false); 1.401 + 1.402 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.403 + NS_ENSURE_TRUE(obs, false); 1.404 + 1.405 + if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) || 1.406 + NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) { 1.407 + BT_WARNING("Failed to add observers!"); 1.408 + return false; 1.409 + } 1.410 + 1.411 + hal::RegisterBatteryObserver(this); 1.412 + 1.413 + mListener = new BluetoothRilListener(); 1.414 + NS_ENSURE_TRUE(mListener->Listen(true), false); 1.415 + 1.416 + nsCOMPtr<nsISettingsService> settings = 1.417 + do_GetService("@mozilla.org/settingsService;1"); 1.418 + NS_ENSURE_TRUE(settings, false); 1.419 + 1.420 + nsCOMPtr<nsISettingsServiceLock> settingsLock; 1.421 + nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock)); 1.422 + NS_ENSURE_SUCCESS(rv, false); 1.423 + 1.424 + nsRefPtr<GetVolumeTask> callback = new GetVolumeTask(); 1.425 + rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback); 1.426 + NS_ENSURE_SUCCESS(rv, false); 1.427 + 1.428 + return true; 1.429 +} 1.430 + 1.431 +bool 1.432 +BluetoothHfpManager::InitHfpInterface() 1.433 +{ 1.434 + const bt_interface_t* btInf = GetBluetoothInterface(); 1.435 + NS_ENSURE_TRUE(btInf, false); 1.436 + 1.437 + if (sBluetoothHfpInterface) { 1.438 + sBluetoothHfpInterface->cleanup(); 1.439 + sBluetoothHfpInterface = nullptr; 1.440 + } 1.441 + 1.442 + bthf_interface_t *interface = (bthf_interface_t *) 1.443 + btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID); 1.444 + NS_ENSURE_TRUE(interface, false); 1.445 + 1.446 + NS_ENSURE_TRUE(BT_STATUS_SUCCESS == 1.447 + interface->init(&sBluetoothHfpCallbacks), false); 1.448 + sBluetoothHfpInterface = interface; 1.449 + 1.450 + return true; 1.451 +} 1.452 + 1.453 +BluetoothHfpManager::~BluetoothHfpManager() 1.454 +{ 1.455 + if (!mListener->Listen(false)) { 1.456 + BT_WARNING("Failed to stop listening RIL"); 1.457 + } 1.458 + mListener = nullptr; 1.459 + 1.460 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.461 + NS_ENSURE_TRUE_VOID(obs); 1.462 + 1.463 + if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) || 1.464 + NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) { 1.465 + BT_WARNING("Failed to remove observers!"); 1.466 + } 1.467 + 1.468 + hal::UnregisterBatteryObserver(this); 1.469 + DeinitHfpInterface(); 1.470 +} 1.471 + 1.472 +void 1.473 +BluetoothHfpManager::DeinitHfpInterface() 1.474 +{ 1.475 + NS_ENSURE_TRUE_VOID(GetBluetoothInterface()); 1.476 + 1.477 + if (sBluetoothHfpInterface) { 1.478 + sBluetoothHfpInterface->cleanup(); 1.479 + sBluetoothHfpInterface = nullptr; 1.480 + } 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 +NS_IMETHODIMP 1.506 +BluetoothHfpManager::Observe(nsISupports* aSubject, 1.507 + const char* aTopic, 1.508 + const char16_t* aData) 1.509 +{ 1.510 + if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) { 1.511 + HandleVolumeChanged(nsDependentString(aData)); 1.512 + } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.513 + HandleShutdown(); 1.514 + } else { 1.515 + MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!"); 1.516 + return NS_ERROR_UNEXPECTED; 1.517 + } 1.518 + 1.519 + return NS_OK; 1.520 +} 1.521 + 1.522 +void 1.523 +BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo) 1.524 +{ 1.525 + // Range of battery level: [0, 1], double 1.526 + // Range of CIND::BATTCHG: [0, 5], int 1.527 + mBattChg = (int) ceil(aBatteryInfo.level() * 5.0); 1.528 + UpdateDeviceCIND(); 1.529 +} 1.530 + 1.531 +void 1.532 +BluetoothHfpManager::ProcessConnectionState(bthf_connection_state_t aState, 1.533 + bt_bdaddr_t* aBdAddress) 1.534 +{ 1.535 + BT_LOGR("state %d", aState); 1.536 + 1.537 + mPrevConnectionState = mConnectionState; 1.538 + mConnectionState = aState; 1.539 + 1.540 + if (aState == BTHF_CONNECTION_STATE_SLC_CONNECTED) { 1.541 + BdAddressTypeToString(aBdAddress, mDeviceAddress); 1.542 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED, 1.543 + NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); 1.544 + } else if (aState == BTHF_CONNECTION_STATE_DISCONNECTED) { 1.545 + DisconnectSco(); 1.546 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED, 1.547 + NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); 1.548 + } 1.549 +} 1.550 + 1.551 +void 1.552 +BluetoothHfpManager::ProcessAudioState(bthf_audio_state_t aState, 1.553 + bt_bdaddr_t* aBdAddress) 1.554 +{ 1.555 + BT_LOGR("state %d", aState); 1.556 + 1.557 + mAudioState = aState; 1.558 + 1.559 + if (aState == BTHF_AUDIO_STATE_CONNECTED || 1.560 + aState == BTHF_AUDIO_STATE_DISCONNECTED) { 1.561 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED, 1.562 + NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID)); 1.563 + } 1.564 +} 1.565 + 1.566 +void 1.567 +BluetoothHfpManager::ProcessAnswerCall() 1.568 +{ 1.569 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.570 + NS_LITERAL_STRING("ATA")); 1.571 +} 1.572 + 1.573 +void 1.574 +BluetoothHfpManager::ProcessHangupCall() 1.575 +{ 1.576 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.577 + NS_LITERAL_STRING("CHUP")); 1.578 +} 1.579 + 1.580 +void 1.581 +BluetoothHfpManager::ProcessVolumeControl(bthf_volume_type_t aType, 1.582 + int aVolume) 1.583 +{ 1.584 + NS_ENSURE_TRUE_VOID(aVolume >= 0 && aVolume <= 15); 1.585 + 1.586 + if (aType == BTHF_VOLUME_TYPE_MIC) { 1.587 + mCurrentVgm = aVolume; 1.588 + } else if (aType == BTHF_VOLUME_TYPE_SPK) { 1.589 + mReceiveVgsFlag = true; 1.590 + 1.591 + if (aVolume == mCurrentVgs) { 1.592 + // Keep current volume 1.593 + return; 1.594 + } 1.595 + 1.596 + nsString data; 1.597 + data.AppendInt(aVolume); 1.598 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_SCO_VOLUME_CHANGED, data); 1.599 + } 1.600 +} 1.601 + 1.602 +void 1.603 +BluetoothHfpManager::ProcessDtmfCmd(char aDtmf) 1.604 +{ 1.605 + NS_ENSURE_TRUE_VOID(IsValidDtmf(aDtmf)); 1.606 + 1.607 + nsAutoCString message("VTS="); 1.608 + message += aDtmf; 1.609 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.610 + NS_ConvertUTF8toUTF16(message)); 1.611 +} 1.612 + 1.613 +void 1.614 +BluetoothHfpManager::ProcessAtChld(bthf_chld_type_t aChld) 1.615 +{ 1.616 + nsAutoCString message("CHLD="); 1.617 + message.AppendInt((int)aChld); 1.618 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.619 + NS_ConvertUTF8toUTF16(message)); 1.620 + 1.621 + SendResponse(BTHF_AT_RESPONSE_OK); 1.622 +} 1.623 + 1.624 +void BluetoothHfpManager::ProcessDialCall(char *aNumber) 1.625 +{ 1.626 + nsAutoCString message(aNumber); 1.627 + 1.628 + // There are three cases based on aNumber, 1.629 + // 1) Empty value: Redial, BLDN 1.630 + // 2) >xxx: Memory dial, ATD>xxx 1.631 + // 3) xxx: Normal dial, ATDxxx 1.632 + // We need to respond OK/Error for dial requests for every case listed above, 1.633 + // 1) and 2): Respond in either RespondToBLDNTask or 1.634 + // HandleCallStateChanged() 1.635 + // 3): Respond here 1.636 + if (message.IsEmpty()) { 1.637 + mDialingRequestProcessed = false; 1.638 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.639 + NS_LITERAL_STRING("BLDN")); 1.640 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN); 1.641 + } else if (message[0] == '>') { 1.642 + mDialingRequestProcessed = false; 1.643 + nsAutoCString newMsg("ATD"); 1.644 + newMsg += StringHead(message, message.Length() - 1); 1.645 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.646 + NS_ConvertUTF8toUTF16(newMsg)); 1.647 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN); 1.648 + } else { 1.649 + nsAutoCString newMsg("ATD"); 1.650 + newMsg += StringHead(message, message.Length() - 1); 1.651 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.652 + NS_ConvertUTF8toUTF16(newMsg)); 1.653 + SendResponse(BTHF_AT_RESPONSE_OK); 1.654 + } 1.655 +} 1.656 + 1.657 +void 1.658 +BluetoothHfpManager::ProcessAtCnum() 1.659 +{ 1.660 + if (!mMsisdn.IsEmpty()) { 1.661 + nsAutoCString message("+CNUM: ,\""); 1.662 + message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get()); 1.663 + message.AppendLiteral("\","); 1.664 + message.AppendInt(BTHF_CALL_ADDRTYPE_UNKNOWN); 1.665 + message.AppendLiteral(",,4"); 1.666 + 1.667 + SendLine(message.get()); 1.668 + } 1.669 + 1.670 + SendResponse(BTHF_AT_RESPONSE_OK); 1.671 +} 1.672 + 1.673 +void 1.674 +BluetoothHfpManager::ProcessAtCind() 1.675 +{ 1.676 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.677 + 1.678 + int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED); 1.679 + int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD); 1.680 + 1.681 + bt_status_t status = sBluetoothHfpInterface->cind_response( 1.682 + mService, 1.683 + numActive, 1.684 + numHeld, 1.685 + ConvertToBthfCallState(GetCallSetupState()), 1.686 + mSignal, 1.687 + mRoam, 1.688 + mBattChg); 1.689 + NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS); 1.690 +} 1.691 + 1.692 +void 1.693 +BluetoothHfpManager::ProcessAtCops() 1.694 +{ 1.695 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.696 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.697 + sBluetoothHfpInterface->cops_response( 1.698 + NS_ConvertUTF16toUTF8(mOperatorName).get())); 1.699 +} 1.700 + 1.701 +void 1.702 +BluetoothHfpManager::ProcessAtClcc() 1.703 +{ 1.704 + uint32_t callNumbers = mCurrentCallArray.Length(); 1.705 + uint32_t i; 1.706 + for (i = 1; i < callNumbers; i++) { 1.707 + SendCLCC(mCurrentCallArray[i], i); 1.708 + } 1.709 + 1.710 + if (!mCdmaSecondCall.mNumber.IsEmpty()) { 1.711 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.712 + MOZ_ASSERT(i == 2); 1.713 + 1.714 + SendCLCC(mCdmaSecondCall, 2); 1.715 + } 1.716 + 1.717 + SendResponse(BTHF_AT_RESPONSE_OK); 1.718 +} 1.719 + 1.720 +void 1.721 +BluetoothHfpManager::ProcessUnknownAt(char *aAtString) 1.722 +{ 1.723 + BT_LOGR("[%s]", aAtString); 1.724 + 1.725 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.726 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.727 + sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_ERROR, 0)); 1.728 +} 1.729 + 1.730 +void 1.731 +BluetoothHfpManager::ProcessKeyPressed() 1.732 +{ 1.733 + bool hasActiveCall = 1.734 + (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED) > 0); 1.735 + 1.736 + // Refer to AOSP HeadsetStateMachine.processKeyPressed 1.737 + if (FindFirstCall(nsITelephonyProvider::CALL_STATE_INCOMING) 1.738 + && !hasActiveCall) { 1.739 + /** 1.740 + * Bluetooth HSP spec 4.2.2 1.741 + * There is an incoming call, notify Dialer to pick up the phone call 1.742 + * and SCO will be established after we get the CallStateChanged event 1.743 + * indicating the call is answered successfully. 1.744 + */ 1.745 + ProcessAnswerCall(); 1.746 + } else if (hasActiveCall) { 1.747 + if (!IsScoConnected()) { 1.748 + /** 1.749 + * Bluetooth HSP spec 4.3 1.750 + * If there's no SCO, set up a SCO link. 1.751 + */ 1.752 + ConnectSco(); 1.753 + } else { 1.754 + /** 1.755 + * Bluetooth HSP spec 4.5 1.756 + * There are two ways to release SCO: sending CHUP to dialer or closing 1.757 + * SCO socket directly. We notify dialer only if there is at least one 1.758 + * active call. 1.759 + */ 1.760 + ProcessHangupCall(); 1.761 + } 1.762 + } else { 1.763 + // BLDN 1.764 + mDialingRequestProcessed = false; 1.765 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_DIALER, 1.766 + NS_LITERAL_STRING("BLDN")); 1.767 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN); 1.768 + } 1.769 +} 1.770 + 1.771 +void 1.772 +BluetoothHfpManager::NotifyConnectionStateChanged(const nsAString& aType) 1.773 +{ 1.774 + MOZ_ASSERT(NS_IsMainThread()); 1.775 + 1.776 + // Notify Gecko observers 1.777 + nsCOMPtr<nsIObserverService> obs = 1.778 + do_GetService("@mozilla.org/observer-service;1"); 1.779 + NS_ENSURE_TRUE_VOID(obs); 1.780 + 1.781 + if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(), 1.782 + mDeviceAddress.get()))) { 1.783 + BT_WARNING("Failed to notify observsers!"); 1.784 + } 1.785 + 1.786 + // Dispatch an event of status change 1.787 + bool status; 1.788 + nsAutoString eventName; 1.789 + if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) { 1.790 + status = IsConnected(); 1.791 + eventName.AssignLiteral(HFP_STATUS_CHANGED_ID); 1.792 + } else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) { 1.793 + status = IsScoConnected(); 1.794 + eventName.AssignLiteral(SCO_STATUS_CHANGED_ID); 1.795 + } else { 1.796 + MOZ_ASSERT(false); 1.797 + return; 1.798 + } 1.799 + 1.800 + DispatchStatusChangedEvent(eventName, mDeviceAddress, status); 1.801 + 1.802 + // Notify profile controller 1.803 + if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) { 1.804 + if (IsConnected()) { 1.805 + MOZ_ASSERT(mListener); 1.806 + 1.807 + // Enumerate current calls 1.808 + mListener->EnumerateCalls(); 1.809 + 1.810 + OnConnect(EmptyString()); 1.811 + } else if (mConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) { 1.812 + mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); 1.813 + if (mPrevConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) { 1.814 + // Bug 979160: This implies the outgoing connection failure. 1.815 + // When the outgoing hfp connection fails, state changes to disconnected 1.816 + // state. Since bluedroid would not report connecting state, but only 1.817 + // report connected/disconnected. 1.818 + OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); 1.819 + } else { 1.820 + OnDisconnect(EmptyString()); 1.821 + } 1.822 + Reset(); 1.823 + } 1.824 + } 1.825 +} 1.826 + 1.827 +void 1.828 +BluetoothHfpManager::NotifyDialer(const nsAString& aCommand) 1.829 +{ 1.830 + NS_NAMED_LITERAL_STRING(type, "bluetooth-dialer-command"); 1.831 + InfallibleTArray<BluetoothNamedValue> parameters; 1.832 + 1.833 + BT_APPEND_NAMED_VALUE(parameters, "command", nsString(aCommand)); 1.834 + 1.835 + BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); 1.836 +} 1.837 + 1.838 +void 1.839 +BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData) 1.840 +{ 1.841 + MOZ_ASSERT(NS_IsMainThread()); 1.842 + 1.843 + // The string that we're interested in will be a JSON string that looks like: 1.844 + // {"key":"volumeup", "value":10} 1.845 + // {"key":"volumedown", "value":2} 1.846 + JSContext* cx = nsContentUtils::GetSafeJSContext(); 1.847 + NS_ENSURE_TRUE_VOID(cx); 1.848 + 1.849 + JS::Rooted<JS::Value> val(cx); 1.850 + NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val)); 1.851 + NS_ENSURE_TRUE_VOID(val.isObject()); 1.852 + 1.853 + JS::Rooted<JSObject*> obj(cx, &val.toObject()); 1.854 + JS::Rooted<JS::Value> key(cx); 1.855 + if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) { 1.856 + return; 1.857 + } 1.858 + 1.859 + bool match; 1.860 + if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) || 1.861 + !match) { 1.862 + return; 1.863 + } 1.864 + 1.865 + JS::Rooted<JS::Value> value(cx); 1.866 + if (!JS_GetProperty(cx, obj, "value", &value) || 1.867 + !value.isNumber()) { 1.868 + return; 1.869 + } 1.870 + 1.871 + mCurrentVgs = value.toNumber(); 1.872 + 1.873 + // Adjust volume by headset and we don't have to send volume back to headset 1.874 + if (mReceiveVgsFlag) { 1.875 + mReceiveVgsFlag = false; 1.876 + return; 1.877 + } 1.878 + 1.879 + // Only send volume back when there's a connected headset 1.880 + if (IsConnected()) { 1.881 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.882 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.883 + sBluetoothHfpInterface->volume_control(BTHF_VOLUME_TYPE_SPK, 1.884 + mCurrentVgs)); 1.885 + } 1.886 +} 1.887 + 1.888 +void 1.889 +BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId) 1.890 +{ 1.891 + nsCOMPtr<nsIMobileConnectionProvider> connection = 1.892 + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); 1.893 + NS_ENSURE_TRUE_VOID(connection); 1.894 + 1.895 + nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo; 1.896 + connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo)); 1.897 + NS_ENSURE_TRUE_VOID(voiceInfo); 1.898 + 1.899 + nsString type; 1.900 + voiceInfo->GetType(type); 1.901 + mPhoneType = GetPhoneType(type); 1.902 + 1.903 + bool roaming; 1.904 + voiceInfo->GetRoaming(&roaming); 1.905 + mRoam = (roaming) ? 1 : 0; 1.906 + 1.907 + // Service 1.908 + nsString regState; 1.909 + voiceInfo->GetState(regState); 1.910 + mService = (regState.EqualsLiteral("registered")) ? 1 : 0; 1.911 + 1.912 + // Signal 1.913 + JSContext* cx = nsContentUtils::GetSafeJSContext(); 1.914 + NS_ENSURE_TRUE_VOID(cx); 1.915 + JS::Rooted<JS::Value> value(cx); 1.916 + voiceInfo->GetRelSignalStrength(&value); 1.917 + NS_ENSURE_TRUE_VOID(value.isNumber()); 1.918 + mSignal = (int)ceil(value.toNumber() / 20.0); 1.919 + 1.920 + UpdateDeviceCIND(); 1.921 + 1.922 + // Operator name 1.923 + nsCOMPtr<nsIDOMMozMobileNetworkInfo> network; 1.924 + voiceInfo->GetNetwork(getter_AddRefs(network)); 1.925 + NS_ENSURE_TRUE_VOID(network); 1.926 + network->GetLongName(mOperatorName); 1.927 + 1.928 + // According to GSM 07.07, "<format> indicates if the format is alphanumeric 1.929 + // or numeric; long alphanumeric format can be upto 16 characters long and 1.930 + // short format up to 8 characters (refer GSM MoU SE.13 [9])..." 1.931 + // However, we found that the operator name may sometimes be longer than 16 1.932 + // characters. After discussion, we decided to fix this here but not in RIL 1.933 + // or modem. 1.934 + // 1.935 + // Please see Bug 871366 for more information. 1.936 + if (mOperatorName.Length() > 16) { 1.937 + BT_WARNING("The operator name was longer than 16 characters. We cut it."); 1.938 + mOperatorName.Left(mOperatorName, 16); 1.939 + } 1.940 +} 1.941 + 1.942 +void 1.943 +BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId) 1.944 +{ 1.945 + nsCOMPtr<nsIIccProvider> icc = 1.946 + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); 1.947 + NS_ENSURE_TRUE_VOID(icc); 1.948 + 1.949 + nsCOMPtr<nsIDOMMozIccInfo> iccInfo; 1.950 + icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo)); 1.951 + NS_ENSURE_TRUE_VOID(iccInfo); 1.952 + 1.953 + nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo); 1.954 + NS_ENSURE_TRUE_VOID(gsmIccInfo); 1.955 + gsmIccInfo->GetMsisdn(mMsisdn); 1.956 +} 1.957 + 1.958 +void 1.959 +BluetoothHfpManager::HandleShutdown() 1.960 +{ 1.961 + MOZ_ASSERT(NS_IsMainThread()); 1.962 + sInShutdown = true; 1.963 + Disconnect(nullptr); 1.964 + DisconnectSco(); 1.965 + sBluetoothHfpManager = nullptr; 1.966 +} 1.967 + 1.968 +void 1.969 +BluetoothHfpManager::SendCLCC(Call& aCall, int aIndex) 1.970 +{ 1.971 + NS_ENSURE_TRUE_VOID(aCall.mState != 1.972 + nsITelephonyProvider::CALL_STATE_DISCONNECTED); 1.973 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.974 + 1.975 + bthf_call_state_t callState = ConvertToBthfCallState(aCall.mState); 1.976 + 1.977 + if (mPhoneType == PhoneType::CDMA && aIndex == 1 && aCall.IsActive()) { 1.978 + callState = (mCdmaSecondCall.IsActive()) ? BTHF_CALL_STATE_HELD : 1.979 + BTHF_CALL_STATE_ACTIVE; 1.980 + } 1.981 + 1.982 + if (callState == BTHF_CALL_STATE_INCOMING && 1.983 + FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) { 1.984 + callState = BTHF_CALL_STATE_WAITING; 1.985 + } 1.986 + 1.987 + bt_status_t status = sBluetoothHfpInterface->clcc_response( 1.988 + aIndex, 1.989 + aCall.mDirection, 1.990 + callState, 1.991 + BTHF_CALL_TYPE_VOICE, 1.992 + BTHF_CALL_MPTY_TYPE_SINGLE, 1.993 + NS_ConvertUTF16toUTF8(aCall.mNumber).get(), 1.994 + aCall.mType); 1.995 + NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS); 1.996 +} 1.997 + 1.998 +void 1.999 +BluetoothHfpManager::SendLine(const char* aMessage) 1.1000 +{ 1.1001 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.1002 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.1003 + sBluetoothHfpInterface->formatted_at_response(aMessage)); 1.1004 +} 1.1005 + 1.1006 +void 1.1007 +BluetoothHfpManager::SendResponse(bthf_at_response_t aResponseCode) 1.1008 +{ 1.1009 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.1010 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.1011 + sBluetoothHfpInterface->at_response(aResponseCode, 0)); 1.1012 +} 1.1013 + 1.1014 +void 1.1015 +BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex) 1.1016 +{ 1.1017 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.1018 + 1.1019 + int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED); 1.1020 + int numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD); 1.1021 + bthf_call_state_t callSetupState = 1.1022 + ConvertToBthfCallState(GetCallSetupState()); 1.1023 + nsAutoCString number = 1.1024 + NS_ConvertUTF16toUTF8(mCurrentCallArray[aCallIndex].mNumber); 1.1025 + bthf_call_addrtype_t type = mCurrentCallArray[aCallIndex].mType; 1.1026 + 1.1027 + BT_LOGR("[%d] state %d => BTHF: active[%d] held[%d] setupstate[%d]", 1.1028 + aCallIndex, mCurrentCallArray[aCallIndex].mState, 1.1029 + numActive, numHeld, callSetupState); 1.1030 + 1.1031 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.1032 + sBluetoothHfpInterface->phone_state_change( 1.1033 + numActive, numHeld, callSetupState, number.get(), type)); 1.1034 +} 1.1035 + 1.1036 +void 1.1037 +BluetoothHfpManager::UpdateDeviceCIND() 1.1038 +{ 1.1039 + NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface); 1.1040 + NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS == 1.1041 + sBluetoothHfpInterface->device_status_notification( 1.1042 + (bthf_network_state_t) mService, 1.1043 + (bthf_service_type_t) mRoam, 1.1044 + mSignal, 1.1045 + mBattChg)); 1.1046 +} 1.1047 + 1.1048 +uint32_t 1.1049 +BluetoothHfpManager::FindFirstCall(uint16_t aState) 1.1050 +{ 1.1051 + uint32_t callLength = mCurrentCallArray.Length(); 1.1052 + 1.1053 + for (uint32_t i = 1; i < callLength; ++i) { 1.1054 + if (mCurrentCallArray[i].mState == aState) { 1.1055 + return i; 1.1056 + } 1.1057 + } 1.1058 + 1.1059 + return 0; 1.1060 +} 1.1061 + 1.1062 +uint32_t 1.1063 +BluetoothHfpManager::GetNumberOfCalls(uint16_t aState) 1.1064 +{ 1.1065 + uint32_t num = 0; 1.1066 + uint32_t callLength = mCurrentCallArray.Length(); 1.1067 + 1.1068 + for (uint32_t i = 1; i < callLength; ++i) { 1.1069 + if (mCurrentCallArray[i].mState == aState) { 1.1070 + ++num; 1.1071 + } 1.1072 + } 1.1073 + 1.1074 + return num; 1.1075 +} 1.1076 + 1.1077 +uint16_t 1.1078 +BluetoothHfpManager::GetCallSetupState() 1.1079 +{ 1.1080 + uint32_t callLength = mCurrentCallArray.Length(); 1.1081 + 1.1082 + for (uint32_t i = 1; i < callLength; ++i) { 1.1083 + switch (mCurrentCallArray[i].mState) { 1.1084 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.1085 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1086 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.1087 + return mCurrentCallArray[i].mState; 1.1088 + default: 1.1089 + break; 1.1090 + } 1.1091 + } 1.1092 + 1.1093 + return nsITelephonyProvider::CALL_STATE_DISCONNECTED; 1.1094 +} 1.1095 + 1.1096 +bthf_call_state_t 1.1097 +BluetoothHfpManager::ConvertToBthfCallState(int aCallState) 1.1098 +{ 1.1099 + bthf_call_state_t state; 1.1100 + 1.1101 + // Refer to AOSP BluetoothPhoneService.convertCallState 1.1102 + if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) { 1.1103 + state = BTHF_CALL_STATE_INCOMING; 1.1104 + } else if (aCallState == nsITelephonyProvider::CALL_STATE_DIALING) { 1.1105 + state = BTHF_CALL_STATE_DIALING; 1.1106 + } else if (aCallState == nsITelephonyProvider::CALL_STATE_ALERTING) { 1.1107 + state = BTHF_CALL_STATE_ALERTING; 1.1108 + } else if (aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED) { 1.1109 + state = BTHF_CALL_STATE_ACTIVE; 1.1110 + } else if (aCallState == nsITelephonyProvider::CALL_STATE_HELD) { 1.1111 + state = BTHF_CALL_STATE_HELD; 1.1112 + } else { // disconnected 1.1113 + state = BTHF_CALL_STATE_IDLE; 1.1114 + } 1.1115 + 1.1116 + return state; 1.1117 +} 1.1118 + 1.1119 +bool 1.1120 +BluetoothHfpManager::IsTransitionState(uint16_t aCallState, bool aIsConference) 1.1121 +{ 1.1122 + /** 1.1123 + * Regard this callstate change as during CHLD=2 transition state if 1.1124 + * - the call becomes active, and numActive > 1 1.1125 + * - the call becomes held, and numHeld > 1 or an incoming call exists 1.1126 + * 1.1127 + * TODO: 1.1128 + * 1) handle CHLD=1 transition state 1.1129 + * 2) handle conference call cases 1.1130 + */ 1.1131 + if (!aIsConference) { 1.1132 + switch (aCallState) { 1.1133 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.1134 + return (GetNumberOfCalls(aCallState) > 1); 1.1135 + case nsITelephonyProvider::CALL_STATE_HELD: 1.1136 + return (GetNumberOfCalls(aCallState) > 1 || 1.1137 + FindFirstCall(nsITelephonyProvider::CALL_STATE_INCOMING)); 1.1138 + default: 1.1139 + break; 1.1140 + } 1.1141 + } 1.1142 + 1.1143 + return false; 1.1144 +} 1.1145 + 1.1146 +void 1.1147 +BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex, 1.1148 + uint16_t aCallState, 1.1149 + const nsAString& aError, 1.1150 + const nsAString& aNumber, 1.1151 + const bool aIsOutgoing, 1.1152 + const bool aIsConference, 1.1153 + bool aSend) 1.1154 +{ 1.1155 + // aCallIndex can be UINT32_MAX for the pending outgoing call state update. 1.1156 + // aCallIndex will be updated again after real call state changes. See Bug 1.1157 + // 990467. 1.1158 + if (aCallIndex == UINT32_MAX) { 1.1159 + return; 1.1160 + } 1.1161 + 1.1162 + // Update call state only 1.1163 + while (aCallIndex >= mCurrentCallArray.Length()) { 1.1164 + Call call; 1.1165 + mCurrentCallArray.AppendElement(call); 1.1166 + } 1.1167 + mCurrentCallArray[aCallIndex].mState = aCallState; 1.1168 + 1.1169 + // Return if SLC is disconnected 1.1170 + if (!IsConnected()) { 1.1171 + return; 1.1172 + } 1.1173 + 1.1174 + // Update call information besides call state 1.1175 + mCurrentCallArray[aCallIndex].Set(aNumber, aIsOutgoing); 1.1176 + 1.1177 + // Notify bluedroid of phone state change if this 1.1178 + // call state change is not during transition state 1.1179 + if (!IsTransitionState(aCallState, aIsConference)) { 1.1180 + UpdatePhoneCIND(aCallIndex); 1.1181 + } 1.1182 + 1.1183 + switch (aCallState) { 1.1184 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.1185 + // We've send Dialer a dialing request and this is the response. 1.1186 + if (!mDialingRequestProcessed) { 1.1187 + SendResponse(BTHF_AT_RESPONSE_OK); 1.1188 + mDialingRequestProcessed = true; 1.1189 + } 1.1190 + break; 1.1191 + case nsITelephonyProvider::CALL_STATE_DISCONNECTED: 1.1192 + // -1 is necessary because call 0 is an invalid (padding) call object. 1.1193 + if (mCurrentCallArray.Length() - 1 == 1.1194 + GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) { 1.1195 + // In order to let user hear busy tone via connected Bluetooth headset, 1.1196 + // we postpone the timing of dropping SCO. 1.1197 + if (aError.Equals(NS_LITERAL_STRING("BusyError"))) { 1.1198 + // FIXME: UpdatePhoneCIND later since it causes SCO close but 1.1199 + // Dialer is still playing busy tone via HF. 1.1200 + BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_CLOSE_SCO); 1.1201 + } 1.1202 + 1.1203 + ResetCallArray(); 1.1204 + } 1.1205 + break; 1.1206 + default: 1.1207 + break; 1.1208 + } 1.1209 +} 1.1210 + 1.1211 +PhoneType 1.1212 +BluetoothHfpManager::GetPhoneType(const nsAString& aType) 1.1213 +{ 1.1214 + // FIXME: Query phone type from RIL after RIL implements new API (bug 912019) 1.1215 + if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") || 1.1216 + aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") || 1.1217 + aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") || 1.1218 + aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) { 1.1219 + return PhoneType::GSM; 1.1220 + } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") || 1.1221 + aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") || 1.1222 + aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) { 1.1223 + return PhoneType::CDMA; 1.1224 + } 1.1225 + 1.1226 + return PhoneType::NONE; 1.1227 +} 1.1228 + 1.1229 +void 1.1230 +BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber) 1.1231 +{ 1.1232 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1233 + 1.1234 + // Always regard second call as incoming call since v1.2 RIL 1.1235 + // doesn't support outgoing second call in CDMA. 1.1236 + mCdmaSecondCall.Set(aNumber, false); 1.1237 + 1.1238 + // FIXME: check CDMA + bluedroid 1.1239 + //UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true); 1.1240 +} 1.1241 + 1.1242 +void 1.1243 +BluetoothHfpManager::AnswerWaitingCall() 1.1244 +{ 1.1245 + MOZ_ASSERT(NS_IsMainThread()); 1.1246 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1247 + 1.1248 + // Pick up second call. First call is held now. 1.1249 + mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED; 1.1250 + // FIXME: check CDMA + bluedroid 1.1251 + //UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true); 1.1252 + 1.1253 + //sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE; 1.1254 + //SendCommand("+CIEV: ", CINDType::CALLHELD); 1.1255 +} 1.1256 + 1.1257 +void 1.1258 +BluetoothHfpManager::IgnoreWaitingCall() 1.1259 +{ 1.1260 + MOZ_ASSERT(NS_IsMainThread()); 1.1261 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1262 + 1.1263 + mCdmaSecondCall.Reset(); 1.1264 + // FIXME: check CDMA + bluedroid 1.1265 + //UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true); 1.1266 +} 1.1267 + 1.1268 +void 1.1269 +BluetoothHfpManager::ToggleCalls() 1.1270 +{ 1.1271 + MOZ_ASSERT(NS_IsMainThread()); 1.1272 + MOZ_ASSERT(mPhoneType == PhoneType::CDMA); 1.1273 + 1.1274 + // Toggle acitve and held calls 1.1275 + mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ? 1.1276 + nsITelephonyProvider::CALL_STATE_HELD : 1.1277 + nsITelephonyProvider::CALL_STATE_CONNECTED; 1.1278 +} 1.1279 + 1.1280 +bool 1.1281 +BluetoothHfpManager::ConnectSco() 1.1282 +{ 1.1283 + MOZ_ASSERT(NS_IsMainThread()); 1.1284 + 1.1285 + NS_ENSURE_TRUE(!sInShutdown, false); 1.1286 + NS_ENSURE_TRUE(IsConnected() && !IsScoConnected(), false); 1.1287 + NS_ENSURE_TRUE(sBluetoothHfpInterface, false); 1.1288 + 1.1289 + bt_bdaddr_t deviceBdAddress; 1.1290 + StringToBdAddressType(mDeviceAddress, &deviceBdAddress); 1.1291 + NS_ENSURE_TRUE(BT_STATUS_SUCCESS == 1.1292 + sBluetoothHfpInterface->connect_audio(&deviceBdAddress), false); 1.1293 + 1.1294 + return true; 1.1295 +} 1.1296 + 1.1297 +bool 1.1298 +BluetoothHfpManager::DisconnectSco() 1.1299 +{ 1.1300 + NS_ENSURE_TRUE(IsScoConnected(), false); 1.1301 + NS_ENSURE_TRUE(sBluetoothHfpInterface, false); 1.1302 + 1.1303 + bt_bdaddr_t deviceBdAddress; 1.1304 + StringToBdAddressType(mDeviceAddress, &deviceBdAddress); 1.1305 + NS_ENSURE_TRUE(BT_STATUS_SUCCESS == 1.1306 + sBluetoothHfpInterface->disconnect_audio(&deviceBdAddress), false); 1.1307 + 1.1308 + return true; 1.1309 +} 1.1310 + 1.1311 +bool 1.1312 +BluetoothHfpManager::IsScoConnected() 1.1313 +{ 1.1314 + return (mAudioState == BTHF_AUDIO_STATE_CONNECTED); 1.1315 +} 1.1316 + 1.1317 +bool 1.1318 +BluetoothHfpManager::IsConnected() 1.1319 +{ 1.1320 + return (mConnectionState == BTHF_CONNECTION_STATE_SLC_CONNECTED); 1.1321 +} 1.1322 + 1.1323 +void 1.1324 +BluetoothHfpManager::Connect(const nsAString& aDeviceAddress, 1.1325 + BluetoothProfileController* aController) 1.1326 +{ 1.1327 + MOZ_ASSERT(NS_IsMainThread()); 1.1328 + MOZ_ASSERT(aController && !mController); 1.1329 + 1.1330 + if (sInShutdown) { 1.1331 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1332 + return; 1.1333 + } 1.1334 + 1.1335 + if (!sBluetoothHfpInterface) { 1.1336 + BT_LOGR("sBluetoothHfpInterface is null"); 1.1337 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1338 + return; 1.1339 + } 1.1340 + 1.1341 + bt_bdaddr_t deviceBdAddress; 1.1342 + StringToBdAddressType(aDeviceAddress, &deviceBdAddress); 1.1343 + 1.1344 + bt_status_t result = sBluetoothHfpInterface->connect(&deviceBdAddress); 1.1345 + if (BT_STATUS_SUCCESS != result) { 1.1346 + BT_LOGR("Failed to connect: %x", result); 1.1347 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); 1.1348 + return; 1.1349 + } 1.1350 + 1.1351 + mDeviceAddress = aDeviceAddress; 1.1352 + mController = aController; 1.1353 +} 1.1354 + 1.1355 +void 1.1356 +BluetoothHfpManager::Disconnect(BluetoothProfileController* aController) 1.1357 +{ 1.1358 + MOZ_ASSERT(NS_IsMainThread()); 1.1359 + MOZ_ASSERT(!mController); 1.1360 + 1.1361 + if (!sBluetoothHfpInterface) { 1.1362 + BT_LOGR("sBluetoothHfpInterface is null"); 1.1363 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); 1.1364 + return; 1.1365 + } 1.1366 + 1.1367 + bt_bdaddr_t deviceBdAddress; 1.1368 + StringToBdAddressType(mDeviceAddress, &deviceBdAddress); 1.1369 + 1.1370 + bt_status_t result = sBluetoothHfpInterface->disconnect(&deviceBdAddress); 1.1371 + if (BT_STATUS_SUCCESS != result) { 1.1372 + BT_LOGR("Failed to disconnect: %x", result); 1.1373 + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED)); 1.1374 + return; 1.1375 + } 1.1376 + 1.1377 + mController = aController; 1.1378 +} 1.1379 + 1.1380 +void 1.1381 +BluetoothHfpManager::OnConnect(const nsAString& aErrorStr) 1.1382 +{ 1.1383 + MOZ_ASSERT(NS_IsMainThread()); 1.1384 + 1.1385 + /** 1.1386 + * On the one hand, notify the controller that we've done for outbound 1.1387 + * connections. On the other hand, we do nothing for inbound connections. 1.1388 + */ 1.1389 + NS_ENSURE_TRUE_VOID(mController); 1.1390 + 1.1391 + mController->NotifyCompletion(aErrorStr); 1.1392 + mController = nullptr; 1.1393 +} 1.1394 + 1.1395 +void 1.1396 +BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr) 1.1397 +{ 1.1398 + MOZ_ASSERT(NS_IsMainThread()); 1.1399 + 1.1400 + /** 1.1401 + * On the one hand, notify the controller that we've done for outbound 1.1402 + * connections. On the other hand, we do nothing for inbound connections. 1.1403 + */ 1.1404 + NS_ENSURE_TRUE_VOID(mController); 1.1405 + 1.1406 + mController->NotifyCompletion(aErrorStr); 1.1407 + mController = nullptr; 1.1408 +} 1.1409 + 1.1410 +void 1.1411 +BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress) 1.1412 +{ 1.1413 + // Bluedroid handles this part 1.1414 + MOZ_ASSERT(false); 1.1415 +} 1.1416 + 1.1417 +void 1.1418 +BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress, 1.1419 + const nsAString& aServiceUuid, 1.1420 + int aChannel) 1.1421 +{ 1.1422 + // Bluedroid handles this part 1.1423 + MOZ_ASSERT(false); 1.1424 +} 1.1425 + 1.1426 +void 1.1427 +BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress) 1.1428 +{ 1.1429 + aDeviceAddress = mDeviceAddress; 1.1430 +} 1.1431 + 1.1432 +NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver)