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