|
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 /* |
|
4 ** Copyright 2006, The Android Open Source Project |
|
5 ** |
|
6 ** Licensed under the Apache License, Version 2.0 (the "License"); |
|
7 ** you may not use this file except in compliance with the License. |
|
8 ** You may obtain a copy of the License at |
|
9 ** |
|
10 ** http://www.apache.org/licenses/LICENSE-2.0 |
|
11 ** |
|
12 ** Unless required by applicable law or agreed to in writing, software |
|
13 ** distributed under the License is distributed on an "AS IS" BASIS, |
|
14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 ** See the License for the specific language governing permissions and |
|
16 ** limitations under the License. |
|
17 */ |
|
18 |
|
19 #include "base/basictypes.h" |
|
20 #include "BluetoothDBusService.h" |
|
21 #include "BluetoothA2dpManager.h" |
|
22 #include "BluetoothHfpManager.h" |
|
23 #include "BluetoothHidManager.h" |
|
24 #include "BluetoothOppManager.h" |
|
25 #include "BluetoothProfileController.h" |
|
26 #include "BluetoothReplyRunnable.h" |
|
27 #include "BluetoothUnixSocketConnector.h" |
|
28 #include "BluetoothUtils.h" |
|
29 #include "BluetoothUuid.h" |
|
30 |
|
31 #include <cstdio> |
|
32 #include <dbus/dbus.h> |
|
33 |
|
34 #include "nsAutoPtr.h" |
|
35 #include "nsThreadUtils.h" |
|
36 #include "nsDebug.h" |
|
37 #include "nsDataHashtable.h" |
|
38 #include "mozilla/ArrayUtils.h" |
|
39 #include "mozilla/Atomics.h" |
|
40 #include "mozilla/dom/bluetooth/BluetoothTypes.h" |
|
41 #include "mozilla/Hal.h" |
|
42 #include "mozilla/ipc/UnixSocket.h" |
|
43 #include "mozilla/ipc/DBusUtils.h" |
|
44 #include "mozilla/ipc/RawDBusConnection.h" |
|
45 #include "mozilla/LazyIdleThread.h" |
|
46 #include "mozilla/Mutex.h" |
|
47 #include "mozilla/NullPtr.h" |
|
48 #include "mozilla/StaticMutex.h" |
|
49 #include "mozilla/unused.h" |
|
50 |
|
51 #if defined(MOZ_WIDGET_GONK) |
|
52 #include "cutils/properties.h" |
|
53 #include <dlfcn.h> |
|
54 #endif |
|
55 |
|
56 /** |
|
57 * Some rules for dealing with memory in DBus: |
|
58 * - A DBusError only needs to be deleted if it's been set, not just |
|
59 * initialized. This is why LOG_AND_FREE... is called only when an error is |
|
60 * set, and the macro cleans up the error itself. |
|
61 * - A DBusMessage needs to be unrefed when it is newed explicitly. DBusMessages |
|
62 * from signals do not need to be unrefed, as they will be cleaned by DBus |
|
63 * after DBUS_HANDLER_RESULT_HANDLED is returned from the filter. |
|
64 */ |
|
65 |
|
66 using namespace mozilla; |
|
67 using namespace mozilla::ipc; |
|
68 USING_BLUETOOTH_NAMESPACE |
|
69 |
|
70 #define B2G_AGENT_CAPABILITIES "DisplayYesNo" |
|
71 #define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager" |
|
72 #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" |
|
73 #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" |
|
74 #define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent" |
|
75 #define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink" |
|
76 #define DBUS_CTL_IFACE BLUEZ_DBUS_BASE_IFC ".Control" |
|
77 #define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input" |
|
78 #define BLUEZ_DBUS_BASE_PATH "/org/bluez" |
|
79 #define BLUEZ_DBUS_BASE_IFC "org.bluez" |
|
80 #define BLUEZ_ERROR_IFC "org.bluez.Error" |
|
81 |
|
82 #define ERR_A2DP_IS_DISCONNECTED "A2dpIsDisconnected" |
|
83 #define ERR_AVRCP_IS_DISCONNECTED "AvrcpIsDisconnected" |
|
84 |
|
85 /** |
|
86 * To not lock Bluetooth switch button on Settings UI because of any accident, |
|
87 * we will force disabling Bluetooth 5 seconds after the user requesting to |
|
88 * turn off Bluetooth. |
|
89 */ |
|
90 #define TIMEOUT_FORCE_TO_DISABLE_BT 5 |
|
91 |
|
92 #define BT_LAZY_THREAD_TIMEOUT_MS 3000 |
|
93 |
|
94 #ifdef MOZ_WIDGET_GONK |
|
95 class Bluedroid |
|
96 { |
|
97 struct ScopedDlHandleTraits |
|
98 { |
|
99 typedef void* type; |
|
100 static void* empty() |
|
101 { |
|
102 return nullptr; |
|
103 } |
|
104 static void release(void* handle) |
|
105 { |
|
106 if (!handle) { |
|
107 return; |
|
108 } |
|
109 int res = dlclose(handle); |
|
110 if (res) { |
|
111 BT_WARNING("Failed to close libbluedroid.so: %s", dlerror()); |
|
112 } |
|
113 } |
|
114 }; |
|
115 |
|
116 public: |
|
117 Bluedroid() |
|
118 : m_bt_enable(nullptr) |
|
119 , m_bt_disable(nullptr) |
|
120 , m_bt_is_enabled(nullptr) |
|
121 {} |
|
122 |
|
123 bool Enable() |
|
124 { |
|
125 MOZ_ASSERT(!NS_IsMainThread()); // BT thread |
|
126 |
|
127 if (!mHandle && !Init()) { |
|
128 return false; |
|
129 } else if (m_bt_is_enabled() == 1) { |
|
130 return true; |
|
131 } |
|
132 // 0 == success, -1 == error |
|
133 return !m_bt_enable(); |
|
134 } |
|
135 |
|
136 bool Disable() |
|
137 { |
|
138 MOZ_ASSERT(!NS_IsMainThread()); // BT thread |
|
139 |
|
140 if (!IsEnabled()) { |
|
141 return true; |
|
142 } |
|
143 // 0 == success, -1 == error |
|
144 return !m_bt_disable(); |
|
145 } |
|
146 |
|
147 bool IsEnabled() const |
|
148 { |
|
149 MOZ_ASSERT(!NS_IsMainThread()); // BT thread |
|
150 |
|
151 if (!mHandle) { |
|
152 return false; |
|
153 } |
|
154 // 1 == enabled, 0 == disabled, -1 == error |
|
155 return m_bt_is_enabled() > 0; |
|
156 } |
|
157 |
|
158 private: |
|
159 bool Init() |
|
160 { |
|
161 MOZ_ASSERT(!mHandle); |
|
162 |
|
163 Scoped<ScopedDlHandleTraits> handle(dlopen("libbluedroid.so", RTLD_LAZY)); |
|
164 if (!handle) { |
|
165 BT_WARNING("Failed to open libbluedroid.so: %s", dlerror()); |
|
166 return false; |
|
167 } |
|
168 int (*bt_enable)() = (int (*)())dlsym(handle, "bt_enable"); |
|
169 if (!bt_enable) { |
|
170 BT_WARNING("Failed to lookup bt_enable: %s", dlerror()); |
|
171 return false; |
|
172 } |
|
173 int (*bt_disable)() = (int (*)())dlsym(handle, "bt_disable"); |
|
174 if (!bt_disable) { |
|
175 BT_WARNING("Failed to lookup bt_disable: %s", dlerror()); |
|
176 return false; |
|
177 } |
|
178 int (*bt_is_enabled)() = (int (*)())dlsym(handle, "bt_is_enabled"); |
|
179 if (!bt_is_enabled) { |
|
180 BT_WARNING("Failed to lookup bt_is_enabled: %s", dlerror()); |
|
181 return false; |
|
182 } |
|
183 |
|
184 m_bt_enable = bt_enable; |
|
185 m_bt_disable = bt_disable; |
|
186 m_bt_is_enabled = bt_is_enabled; |
|
187 mHandle.reset(handle.forget()); |
|
188 |
|
189 return true; |
|
190 } |
|
191 |
|
192 Scoped<ScopedDlHandleTraits> mHandle; |
|
193 int (* m_bt_enable)(void); |
|
194 int (* m_bt_disable)(void); |
|
195 int (* m_bt_is_enabled)(void); |
|
196 }; |
|
197 |
|
198 // |
|
199 // BT-thread-only variables |
|
200 // |
|
201 // The variables below must only be accessed from within the BT thread. |
|
202 // |
|
203 |
|
204 static class Bluedroid sBluedroid; |
|
205 #endif |
|
206 |
|
207 // |
|
208 // Read-only constants |
|
209 // |
|
210 // The constants below are read-only and may be accessed from any |
|
211 // thread. Most of the contain DBus state or settings, so keep them |
|
212 // on the I/O thread if somehow possible. |
|
213 // |
|
214 |
|
215 typedef struct { |
|
216 const char* name; |
|
217 int type; |
|
218 } Properties; |
|
219 |
|
220 static const Properties sDeviceProperties[] = { |
|
221 {"Address", DBUS_TYPE_STRING}, |
|
222 {"Name", DBUS_TYPE_STRING}, |
|
223 {"Icon", DBUS_TYPE_STRING}, |
|
224 {"Class", DBUS_TYPE_UINT32}, |
|
225 {"UUIDs", DBUS_TYPE_ARRAY}, |
|
226 {"Paired", DBUS_TYPE_BOOLEAN}, |
|
227 {"Connected", DBUS_TYPE_BOOLEAN}, |
|
228 {"Trusted", DBUS_TYPE_BOOLEAN}, |
|
229 {"Blocked", DBUS_TYPE_BOOLEAN}, |
|
230 {"Alias", DBUS_TYPE_STRING}, |
|
231 {"Nodes", DBUS_TYPE_ARRAY}, |
|
232 {"Adapter", DBUS_TYPE_OBJECT_PATH}, |
|
233 {"LegacyPairing", DBUS_TYPE_BOOLEAN}, |
|
234 {"RSSI", DBUS_TYPE_INT16}, |
|
235 {"TX", DBUS_TYPE_UINT32}, |
|
236 {"Type", DBUS_TYPE_STRING}, |
|
237 {"Broadcaster", DBUS_TYPE_BOOLEAN}, |
|
238 {"Services", DBUS_TYPE_ARRAY} |
|
239 }; |
|
240 |
|
241 static const Properties sAdapterProperties[] = { |
|
242 {"Address", DBUS_TYPE_STRING}, |
|
243 {"Name", DBUS_TYPE_STRING}, |
|
244 {"Class", DBUS_TYPE_UINT32}, |
|
245 {"Powered", DBUS_TYPE_BOOLEAN}, |
|
246 {"Discoverable", DBUS_TYPE_BOOLEAN}, |
|
247 {"DiscoverableTimeout", DBUS_TYPE_UINT32}, |
|
248 {"Pairable", DBUS_TYPE_BOOLEAN}, |
|
249 {"PairableTimeout", DBUS_TYPE_UINT32}, |
|
250 {"Discovering", DBUS_TYPE_BOOLEAN}, |
|
251 {"Devices", DBUS_TYPE_ARRAY}, |
|
252 {"UUIDs", DBUS_TYPE_ARRAY}, |
|
253 {"Type", DBUS_TYPE_STRING} |
|
254 }; |
|
255 |
|
256 static const Properties sManagerProperties[] = { |
|
257 {"Adapters", DBUS_TYPE_ARRAY}, |
|
258 }; |
|
259 |
|
260 static const Properties sSinkProperties[] = { |
|
261 {"State", DBUS_TYPE_STRING}, |
|
262 {"Connected", DBUS_TYPE_BOOLEAN}, |
|
263 {"Playing", DBUS_TYPE_BOOLEAN} |
|
264 }; |
|
265 |
|
266 static const Properties sControlProperties[] = { |
|
267 {"Connected", DBUS_TYPE_BOOLEAN} |
|
268 }; |
|
269 |
|
270 static const Properties sInputProperties[] = { |
|
271 {"Connected", DBUS_TYPE_BOOLEAN} |
|
272 }; |
|
273 |
|
274 static const char* const sBluetoothDBusIfaces[] = { |
|
275 DBUS_MANAGER_IFACE, |
|
276 DBUS_ADAPTER_IFACE, |
|
277 DBUS_DEVICE_IFACE |
|
278 }; |
|
279 |
|
280 static const char* const sBluetoothDBusSignals[] = { |
|
281 "type='signal',interface='org.freedesktop.DBus'", |
|
282 "type='signal',interface='org.bluez.Adapter'", |
|
283 "type='signal',interface='org.bluez.Manager'", |
|
284 "type='signal',interface='org.bluez.Device'", |
|
285 "type='signal',interface='org.bluez.Input'", |
|
286 "type='signal',interface='org.bluez.Network'", |
|
287 "type='signal',interface='org.bluez.NetworkServer'", |
|
288 "type='signal',interface='org.bluez.HealthDevice'", |
|
289 "type='signal',interface='org.bluez.AudioSink'", |
|
290 "type='signal',interface='org.bluez.Control'" |
|
291 }; |
|
292 |
|
293 // Only A2DP and HID are authorized. |
|
294 static const BluetoothServiceClass sAuthorizedServiceClass[] = { |
|
295 BluetoothServiceClass::A2DP, |
|
296 BluetoothServiceClass::HID |
|
297 }; |
|
298 |
|
299 /** |
|
300 * The adapter name may not be ready whenever event 'AdapterAdded' is received, |
|
301 * so we'd like to wait for a bit. Only used on main thread. |
|
302 */ |
|
303 static const int sWaitingForAdapterNameInterval = 1000; // unit: ms |
|
304 |
|
305 // |
|
306 // main-thread-only variables |
|
307 // |
|
308 // The variables below must be accessed from within the main thread. |
|
309 // |
|
310 |
|
311 // A queue for connect/disconnect request. See Bug 913372 for details. |
|
312 static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray; |
|
313 |
|
314 // |
|
315 // I/O-thread-only variables |
|
316 // |
|
317 // The variables below must be accessed from within the I/O thread. |
|
318 // |
|
319 |
|
320 // The DBus connection to the BlueZ daemon |
|
321 static StaticAutoPtr<RawDBusConnection> sDBusConnection; |
|
322 |
|
323 // Keep the pairing requests. |
|
324 static unsigned int sIsPairing = 0; |
|
325 |
|
326 static nsDataHashtable<nsStringHashKey, DBusMessage* >* sPairingReqTable; |
|
327 |
|
328 // The object path of the adapter that should |
|
329 // be updated after switching Bluetooth. |
|
330 static nsString sAdapterPath; |
|
331 |
|
332 // |
|
333 // The variables below are currently accessed from within multiple |
|
334 // threads and should be moved to one specific thread if possible. |
|
335 // |
|
336 // TODO: Concurrency control is implemented by locking. Maybe there's |
|
337 // a non-blocking way to implement this. |
|
338 // |
|
339 |
|
340 // Disconnect all profiles before turning off Bluetooth. Please see Bug 891257 |
|
341 // for more details. |sStopBluetoothMonitor| protects access to this variable. |
|
342 static int sConnectedDeviceCount = 0; |
|
343 static StaticAutoPtr<Monitor> sStopBluetoothMonitor; |
|
344 |
|
345 // Protects against bug 969447. |
|
346 static StaticAutoPtr<Monitor> sGetPropertyMonitor; |
|
347 |
|
348 typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&); |
|
349 typedef bool (*FilterFunc)(const BluetoothValue&); |
|
350 |
|
351 static void |
|
352 DispatchToDBusThread(Task* task) |
|
353 { |
|
354 XRE_GetIOMessageLoop()->PostTask(FROM_HERE, task); |
|
355 } |
|
356 |
|
357 static nsresult |
|
358 DispatchToBtThread(nsIRunnable* aRunnable) |
|
359 { |
|
360 /* Due to the fact that the startup and shutdown of the Bluetooth |
|
361 * system can take an indefinite amount of time, a separate thread |
|
362 * is used for running blocking calls. The thread is not intended |
|
363 * for regular Bluetooth operations though. |
|
364 */ |
|
365 static StaticRefPtr<LazyIdleThread> sBluetoothThread; |
|
366 |
|
367 MOZ_ASSERT(NS_IsMainThread()); |
|
368 |
|
369 if (!sBluetoothThread) { |
|
370 sBluetoothThread = new LazyIdleThread(BT_LAZY_THREAD_TIMEOUT_MS, |
|
371 NS_LITERAL_CSTRING("BluetoothDBusService"), |
|
372 LazyIdleThread::ManualShutdown); |
|
373 } |
|
374 return sBluetoothThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); |
|
375 } |
|
376 |
|
377 BluetoothDBusService::BluetoothDBusService() |
|
378 { |
|
379 sGetPropertyMonitor = new Monitor("BluetoothService.sGetPropertyMonitor"); |
|
380 sStopBluetoothMonitor = new Monitor("BluetoothService.sStopBluetoothMonitor"); |
|
381 } |
|
382 |
|
383 BluetoothDBusService::~BluetoothDBusService() |
|
384 { |
|
385 sStopBluetoothMonitor = nullptr; |
|
386 sGetPropertyMonitor = nullptr; |
|
387 } |
|
388 |
|
389 static bool |
|
390 GetConnectedDevicesFilter(const BluetoothValue& aValue) |
|
391 { |
|
392 // We don't have to filter device here |
|
393 return true; |
|
394 } |
|
395 |
|
396 static bool |
|
397 GetPairedDevicesFilter(const BluetoothValue& aValue) |
|
398 { |
|
399 // Check property 'Paired' and only paired device will be returned |
|
400 if (aValue.type() != BluetoothValue::TArrayOfBluetoothNamedValue) { |
|
401 BT_WARNING("Not a BluetoothNamedValue array!"); |
|
402 return false; |
|
403 } |
|
404 |
|
405 const InfallibleTArray<BluetoothNamedValue>& deviceProperties = |
|
406 aValue.get_ArrayOfBluetoothNamedValue(); |
|
407 uint32_t length = deviceProperties.Length(); |
|
408 for (uint32_t p = 0; p < length; ++p) { |
|
409 if (deviceProperties[p].name().EqualsLiteral("Paired")) { |
|
410 return deviceProperties[p].value().get_bool(); |
|
411 } |
|
412 } |
|
413 |
|
414 return false; |
|
415 } |
|
416 |
|
417 class DistributeBluetoothSignalTask : public nsRunnable |
|
418 { |
|
419 public: |
|
420 DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) |
|
421 : mSignal(aSignal) |
|
422 { |
|
423 } |
|
424 |
|
425 nsresult Run() |
|
426 { |
|
427 MOZ_ASSERT(NS_IsMainThread()); |
|
428 |
|
429 BluetoothService* bs = BluetoothService::Get(); |
|
430 NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); |
|
431 bs->DistributeSignal(mSignal); |
|
432 |
|
433 return NS_OK; |
|
434 } |
|
435 |
|
436 private: |
|
437 BluetoothSignal mSignal; |
|
438 }; |
|
439 |
|
440 class ControlPropertyChangedHandler : public nsRunnable |
|
441 { |
|
442 public: |
|
443 ControlPropertyChangedHandler(const BluetoothSignal& aSignal) |
|
444 : mSignal(aSignal) |
|
445 { |
|
446 } |
|
447 |
|
448 nsresult Run() |
|
449 { |
|
450 MOZ_ASSERT(NS_IsMainThread()); |
|
451 if (mSignal.value().type() != BluetoothValue::TArrayOfBluetoothNamedValue) { |
|
452 BT_WARNING("Wrong value type for ControlPropertyChangedHandler"); |
|
453 return NS_ERROR_FAILURE; |
|
454 } |
|
455 |
|
456 InfallibleTArray<BluetoothNamedValue>& arr = |
|
457 mSignal.value().get_ArrayOfBluetoothNamedValue(); |
|
458 MOZ_ASSERT(arr[0].name().EqualsLiteral("Connected")); |
|
459 MOZ_ASSERT(arr[0].value().type() == BluetoothValue::Tbool); |
|
460 bool connected = arr[0].value().get_bool(); |
|
461 |
|
462 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
463 NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); |
|
464 a2dp->SetAvrcpConnected(connected); |
|
465 return NS_OK; |
|
466 } |
|
467 |
|
468 private: |
|
469 BluetoothSignal mSignal; |
|
470 }; |
|
471 |
|
472 class SinkPropertyChangedHandler : public nsRunnable |
|
473 { |
|
474 public: |
|
475 SinkPropertyChangedHandler(const BluetoothSignal& aSignal) |
|
476 : mSignal(aSignal) |
|
477 { |
|
478 } |
|
479 |
|
480 NS_IMETHOD |
|
481 Run() |
|
482 { |
|
483 MOZ_ASSERT(NS_IsMainThread()); |
|
484 MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); |
|
485 MOZ_ASSERT(mSignal.value().type() == |
|
486 BluetoothValue::TArrayOfBluetoothNamedValue); |
|
487 |
|
488 // Replace object path with device address |
|
489 nsString address = GetAddressFromObjectPath(mSignal.path()); |
|
490 mSignal.path() = address; |
|
491 |
|
492 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
493 NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); |
|
494 a2dp->HandleSinkPropertyChanged(mSignal); |
|
495 return NS_OK; |
|
496 } |
|
497 |
|
498 private: |
|
499 BluetoothSignal mSignal; |
|
500 }; |
|
501 |
|
502 class InputPropertyChangedHandler : public nsRunnable |
|
503 { |
|
504 public: |
|
505 InputPropertyChangedHandler(const BluetoothSignal& aSignal) |
|
506 : mSignal(aSignal) |
|
507 { |
|
508 } |
|
509 |
|
510 NS_IMETHOD |
|
511 Run() |
|
512 { |
|
513 MOZ_ASSERT(NS_IsMainThread()); |
|
514 MOZ_ASSERT(mSignal.name().EqualsLiteral("PropertyChanged")); |
|
515 MOZ_ASSERT(mSignal.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue); |
|
516 |
|
517 // Replace object path with device address |
|
518 nsString address = GetAddressFromObjectPath(mSignal.path()); |
|
519 mSignal.path() = address; |
|
520 |
|
521 BluetoothHidManager* hid = BluetoothHidManager::Get(); |
|
522 NS_ENSURE_TRUE(hid, NS_ERROR_FAILURE); |
|
523 hid->HandleInputPropertyChanged(mSignal); |
|
524 return NS_OK; |
|
525 } |
|
526 |
|
527 private: |
|
528 BluetoothSignal mSignal; |
|
529 }; |
|
530 |
|
531 class TryFiringAdapterAddedTask : public Task |
|
532 { |
|
533 public: |
|
534 void Run() MOZ_OVERRIDE |
|
535 { |
|
536 MOZ_ASSERT(NS_IsMainThread()); |
|
537 |
|
538 BluetoothService* bs = BluetoothService::Get(); |
|
539 NS_ENSURE_TRUE_VOID(bs); |
|
540 |
|
541 bs->AdapterAddedReceived(); |
|
542 bs->TryFiringAdapterAdded(); |
|
543 } |
|
544 }; |
|
545 |
|
546 class TryFiringAdapterAddedRunnable : public nsRunnable |
|
547 { |
|
548 public: |
|
549 TryFiringAdapterAddedRunnable(bool aDelay) |
|
550 : mDelay(aDelay) |
|
551 { } |
|
552 |
|
553 nsresult Run() |
|
554 { |
|
555 MOZ_ASSERT(NS_IsMainThread()); |
|
556 |
|
557 if (mDelay) { |
|
558 MessageLoop::current()-> |
|
559 PostDelayedTask(FROM_HERE, new TryFiringAdapterAddedTask(), |
|
560 sWaitingForAdapterNameInterval); |
|
561 } else { |
|
562 MessageLoop::current()-> |
|
563 PostTask(FROM_HERE, new TryFiringAdapterAddedTask()); |
|
564 } |
|
565 |
|
566 return NS_OK; |
|
567 } |
|
568 |
|
569 private: |
|
570 bool mDelay; |
|
571 }; |
|
572 |
|
573 static bool |
|
574 IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr) |
|
575 { |
|
576 if (aErr && dbus_error_is_set(aErr)) { |
|
577 aErrorStr = NS_ConvertUTF8toUTF16(aErr->message); |
|
578 LOG_AND_FREE_DBUS_ERROR(aErr); |
|
579 return true; |
|
580 } |
|
581 |
|
582 DBusError err; |
|
583 dbus_error_init(&err); |
|
584 if (dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_ERROR) { |
|
585 const char* error_msg; |
|
586 if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_STRING, |
|
587 &error_msg, DBUS_TYPE_INVALID) || |
|
588 !error_msg) { |
|
589 if (dbus_error_is_set(&err)) { |
|
590 aErrorStr = NS_ConvertUTF8toUTF16(err.message); |
|
591 LOG_AND_FREE_DBUS_ERROR(&err); |
|
592 return true; |
|
593 } else { |
|
594 aErrorStr.AssignLiteral("Unknown Error"); |
|
595 return true; |
|
596 } |
|
597 } else { |
|
598 aErrorStr = NS_ConvertUTF8toUTF16(error_msg); |
|
599 return true; |
|
600 } |
|
601 } |
|
602 return false; |
|
603 } |
|
604 |
|
605 static void |
|
606 UnpackObjectPathMessage(DBusMessage* aMsg, DBusError* aErr, |
|
607 BluetoothValue& aValue, nsAString& aErrorStr) |
|
608 { |
|
609 DBusError err; |
|
610 dbus_error_init(&err); |
|
611 if (!IsDBusMessageError(aMsg, aErr, aErrorStr)) { |
|
612 MOZ_ASSERT(dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN, |
|
613 "Got dbus callback that's not a METHOD_RETURN!"); |
|
614 const char* object_path; |
|
615 if (!dbus_message_get_args(aMsg, &err, DBUS_TYPE_OBJECT_PATH, |
|
616 &object_path, DBUS_TYPE_INVALID) || |
|
617 !object_path) { |
|
618 if (dbus_error_is_set(&err)) { |
|
619 aErrorStr = NS_ConvertUTF8toUTF16(err.message); |
|
620 LOG_AND_FREE_DBUS_ERROR(&err); |
|
621 } |
|
622 } else { |
|
623 aValue = NS_ConvertUTF8toUTF16(object_path); |
|
624 } |
|
625 } |
|
626 } |
|
627 |
|
628 class PrepareProfileManagersRunnable : public nsRunnable |
|
629 { |
|
630 public: |
|
631 nsresult Run() |
|
632 { |
|
633 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
634 if (!hfp || !hfp->Listen()) { |
|
635 BT_WARNING("Failed to start listening for BluetoothHfpManager!"); |
|
636 return NS_ERROR_FAILURE; |
|
637 } |
|
638 |
|
639 BluetoothOppManager* opp = BluetoothOppManager::Get(); |
|
640 if (!opp || !opp->Listen()) { |
|
641 BT_WARNING("Failed to start listening for BluetoothOppManager!"); |
|
642 return NS_ERROR_FAILURE; |
|
643 } |
|
644 |
|
645 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
646 NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE); |
|
647 a2dp->Reset(); |
|
648 |
|
649 return NS_OK; |
|
650 } |
|
651 }; |
|
652 |
|
653 static void |
|
654 RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, |
|
655 UnpackFunc aFunc) |
|
656 { |
|
657 #ifdef MOZ_WIDGET_GONK |
|
658 // Due to the fact that we're running two dbus loops on desktop implicitly by |
|
659 // being gtk based, sometimes we'll get signals/reply coming in on the main |
|
660 // thread. There's not a lot we can do about that for the time being and it |
|
661 // (technically) shouldn't hurt anything. However, on gonk, die. |
|
662 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
663 #endif |
|
664 nsRefPtr<BluetoothReplyRunnable> replyRunnable = |
|
665 dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable)); |
|
666 |
|
667 MOZ_ASSERT(replyRunnable, "Callback reply runnable is null!"); |
|
668 |
|
669 nsAutoString replyError; |
|
670 BluetoothValue v; |
|
671 aFunc(aMsg, nullptr, v, replyError); |
|
672 |
|
673 // Bug 941462. When blueZ replys 'I/O error', we treat it as 'internal error'. |
|
674 // This usually happned when the first pairing request has not yet finished, |
|
675 // the second pairing request issued immediately. |
|
676 if (replyError.EqualsLiteral("I/O error")) { |
|
677 replyError.AssignLiteral(ERR_INTERNAL_ERROR); |
|
678 } |
|
679 |
|
680 DispatchBluetoothReply(replyRunnable, v, replyError); |
|
681 } |
|
682 |
|
683 static void |
|
684 GetObjectPathCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) |
|
685 { |
|
686 if (sIsPairing) { |
|
687 RunDBusCallback(aMsg, aBluetoothReplyRunnable, |
|
688 UnpackObjectPathMessage); |
|
689 sIsPairing--; |
|
690 } |
|
691 } |
|
692 |
|
693 static void |
|
694 UnpackVoidMessage(DBusMessage* aMsg, DBusError* aErr, BluetoothValue& aValue, |
|
695 nsAString& aErrorStr) |
|
696 { |
|
697 DBusError err; |
|
698 dbus_error_init(&err); |
|
699 if (!IsDBusMessageError(aMsg, aErr, aErrorStr) && |
|
700 dbus_message_get_type(aMsg) == DBUS_MESSAGE_TYPE_METHOD_RETURN && |
|
701 !dbus_message_get_args(aMsg, &err, DBUS_TYPE_INVALID)) { |
|
702 if (dbus_error_is_set(&err)) { |
|
703 aErrorStr = NS_ConvertUTF8toUTF16(err.message); |
|
704 LOG_AND_FREE_DBUS_ERROR(&err); |
|
705 } |
|
706 } |
|
707 aValue = aErrorStr.IsEmpty(); |
|
708 } |
|
709 |
|
710 static void |
|
711 GetVoidCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable) |
|
712 { |
|
713 RunDBusCallback(aMsg, aBluetoothReplyRunnable, |
|
714 UnpackVoidMessage); |
|
715 } |
|
716 |
|
717 class ReplyErrorToProfileManager : public nsRunnable |
|
718 { |
|
719 public: |
|
720 ReplyErrorToProfileManager(BluetoothServiceClass aServiceClass, |
|
721 bool aConnect, |
|
722 const nsAString& aErrorString) |
|
723 : mServiceClass(aServiceClass) |
|
724 , mConnect(aConnect) |
|
725 , mErrorString(aErrorString) |
|
726 { |
|
727 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
728 } |
|
729 |
|
730 nsresult Run() |
|
731 { |
|
732 MOZ_ASSERT(NS_IsMainThread()); |
|
733 |
|
734 BluetoothProfileManagerBase* profile; |
|
735 if (mServiceClass == BluetoothServiceClass::HID) { |
|
736 profile = BluetoothHidManager::Get(); |
|
737 } else if (mServiceClass == BluetoothServiceClass::A2DP) { |
|
738 profile = BluetoothA2dpManager::Get(); |
|
739 } else { |
|
740 MOZ_ASSERT(false); |
|
741 return NS_ERROR_FAILURE; |
|
742 } |
|
743 |
|
744 if (mConnect) { |
|
745 profile->OnConnect(mErrorString); |
|
746 } else { |
|
747 profile->OnDisconnect(mErrorString); |
|
748 } |
|
749 |
|
750 return NS_OK; |
|
751 } |
|
752 |
|
753 private: |
|
754 BluetoothServiceClass mServiceClass; |
|
755 bool mConnect; |
|
756 nsString mErrorString; |
|
757 }; |
|
758 |
|
759 static void |
|
760 CheckDBusReply(DBusMessage* aMsg, void* aServiceClass, bool aConnect) |
|
761 { |
|
762 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
763 |
|
764 NS_ENSURE_TRUE_VOID(aMsg); |
|
765 |
|
766 BluetoothValue v; |
|
767 nsAutoString replyError; |
|
768 UnpackVoidMessage(aMsg, nullptr, v, replyError); |
|
769 |
|
770 nsAutoPtr<BluetoothServiceClass> serviceClass( |
|
771 static_cast<BluetoothServiceClass*>(aServiceClass)); |
|
772 |
|
773 if (!replyError.IsEmpty()) { |
|
774 NS_DispatchToMainThread( |
|
775 new ReplyErrorToProfileManager(*serviceClass, aConnect, replyError)); |
|
776 } |
|
777 } |
|
778 |
|
779 static void |
|
780 InputConnectCallback(DBusMessage* aMsg, void* aParam) |
|
781 { |
|
782 CheckDBusReply(aMsg, aParam, true); |
|
783 } |
|
784 |
|
785 static void |
|
786 InputDisconnectCallback(DBusMessage* aMsg, void* aParam) |
|
787 { |
|
788 CheckDBusReply(aMsg, aParam, false); |
|
789 } |
|
790 |
|
791 static void |
|
792 SinkConnectCallback(DBusMessage* aMsg, void* aParam) |
|
793 { |
|
794 CheckDBusReply(aMsg, aParam, true); |
|
795 } |
|
796 |
|
797 static void |
|
798 SinkDisconnectCallback(DBusMessage* aMsg, void* aParam) |
|
799 { |
|
800 CheckDBusReply(aMsg, aParam, false); |
|
801 } |
|
802 |
|
803 static bool |
|
804 HasAudioService(uint32_t aCodValue) |
|
805 { |
|
806 return ((aCodValue & 0x200000) == 0x200000); |
|
807 } |
|
808 |
|
809 static bool |
|
810 ContainsIcon(const InfallibleTArray<BluetoothNamedValue>& aProperties) |
|
811 { |
|
812 for (uint8_t i = 0; i < aProperties.Length(); i++) { |
|
813 if (aProperties[i].name().EqualsLiteral("Icon")) { |
|
814 return true; |
|
815 } |
|
816 } |
|
817 return false; |
|
818 } |
|
819 |
|
820 static bool |
|
821 GetProperty(DBusMessageIter aIter, const Properties* aPropertyTypes, |
|
822 int aPropertyTypeLen, int* aPropIndex, |
|
823 InfallibleTArray<BluetoothNamedValue>& aProperties) |
|
824 { |
|
825 /** |
|
826 * Ensure GetProperty runs in critical section otherwise |
|
827 * crash due to timing issue occurs when BT is enabled. |
|
828 * |
|
829 * TODO: Revise GetProperty to solve the crash |
|
830 */ |
|
831 MonitorAutoLock lock(*sGetPropertyMonitor); |
|
832 |
|
833 DBusMessageIter prop_val, array_val_iter; |
|
834 char* property = nullptr; |
|
835 uint32_t array_type; |
|
836 int i, expectedType, receivedType; |
|
837 |
|
838 if (dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_STRING) { |
|
839 return false; |
|
840 } |
|
841 |
|
842 dbus_message_iter_get_basic(&aIter, &property); |
|
843 |
|
844 if (!dbus_message_iter_next(&aIter) || |
|
845 dbus_message_iter_get_arg_type(&aIter) != DBUS_TYPE_VARIANT) { |
|
846 return false; |
|
847 } |
|
848 |
|
849 for (i = 0; i < aPropertyTypeLen; i++) { |
|
850 if (!strncmp(property, aPropertyTypes[i].name, strlen(property))) { |
|
851 break; |
|
852 } |
|
853 } |
|
854 |
|
855 if (i == aPropertyTypeLen) { |
|
856 BT_LOGR("unknown property: %s", property); |
|
857 return false; |
|
858 } |
|
859 |
|
860 nsAutoString propertyName; |
|
861 propertyName.AssignASCII(aPropertyTypes[i].name); |
|
862 *aPropIndex = i; |
|
863 |
|
864 // Preprocessing |
|
865 dbus_message_iter_recurse(&aIter, &prop_val); |
|
866 expectedType = aPropertyTypes[*aPropIndex].type; |
|
867 receivedType = dbus_message_iter_get_arg_type(&prop_val); |
|
868 |
|
869 /** |
|
870 * Bug 857896. Since device property "Connected" could be a boolean value or |
|
871 * an 2-byte array, we need to check the value type here and convert the |
|
872 * first byte into a boolean manually. |
|
873 */ |
|
874 bool convert = false; |
|
875 if (propertyName.EqualsLiteral("Connected") && |
|
876 receivedType == DBUS_TYPE_ARRAY) { |
|
877 MOZ_ASSERT(aPropertyTypes == sDeviceProperties); |
|
878 convert = true; |
|
879 } |
|
880 |
|
881 if ((receivedType != expectedType) && !convert) { |
|
882 BT_WARNING("Iterator not type we expect! Property name: %s," |
|
883 "Property Type Expected: %d, Property Type Received: %d", |
|
884 NS_ConvertUTF16toUTF8(propertyName).get(), expectedType, receivedType); |
|
885 return false; |
|
886 } |
|
887 |
|
888 // Extract data |
|
889 BluetoothValue propertyValue; |
|
890 switch (receivedType) { |
|
891 case DBUS_TYPE_STRING: |
|
892 case DBUS_TYPE_OBJECT_PATH: |
|
893 const char* c; |
|
894 dbus_message_iter_get_basic(&prop_val, &c); |
|
895 propertyValue = NS_ConvertUTF8toUTF16(c); |
|
896 break; |
|
897 case DBUS_TYPE_UINT32: |
|
898 case DBUS_TYPE_INT16: |
|
899 uint32_t i; |
|
900 dbus_message_iter_get_basic(&prop_val, &i); |
|
901 propertyValue = i; |
|
902 break; |
|
903 case DBUS_TYPE_BOOLEAN: |
|
904 bool b; |
|
905 dbus_message_iter_get_basic(&prop_val, &b); |
|
906 propertyValue = b; |
|
907 break; |
|
908 case DBUS_TYPE_ARRAY: |
|
909 dbus_message_iter_recurse(&prop_val, &array_val_iter); |
|
910 array_type = dbus_message_iter_get_arg_type(&array_val_iter); |
|
911 if (array_type == DBUS_TYPE_OBJECT_PATH || |
|
912 array_type == DBUS_TYPE_STRING) { |
|
913 InfallibleTArray<nsString> arr; |
|
914 do { |
|
915 const char* tmp; |
|
916 dbus_message_iter_get_basic(&array_val_iter, &tmp); |
|
917 nsAutoString s; |
|
918 s = NS_ConvertUTF8toUTF16(tmp); |
|
919 arr.AppendElement(s); |
|
920 } while (dbus_message_iter_next(&array_val_iter)); |
|
921 propertyValue = arr; |
|
922 } else if (array_type == DBUS_TYPE_BYTE) { |
|
923 InfallibleTArray<uint8_t> arr; |
|
924 do { |
|
925 uint8_t tmp; |
|
926 dbus_message_iter_get_basic(&array_val_iter, &tmp); |
|
927 arr.AppendElement(tmp); |
|
928 } while (dbus_message_iter_next(&array_val_iter)); |
|
929 propertyValue = arr; |
|
930 } else { |
|
931 // This happens when the array is 0-length. Apparently we get a |
|
932 // DBUS_TYPE_INVALID type. |
|
933 propertyValue = InfallibleTArray<nsString>(); |
|
934 } |
|
935 break; |
|
936 default: |
|
937 NS_NOTREACHED("Cannot find dbus message type!"); |
|
938 } |
|
939 |
|
940 // Postprocessing |
|
941 if (convert) { |
|
942 MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfuint8_t); |
|
943 |
|
944 bool b = propertyValue.get_ArrayOfuint8_t()[0]; |
|
945 propertyValue = BluetoothValue(b); |
|
946 } else if (propertyName.EqualsLiteral("Devices")) { |
|
947 MOZ_ASSERT(aPropertyTypes == sAdapterProperties); |
|
948 MOZ_ASSERT(propertyValue.type() == BluetoothValue::TArrayOfnsString); |
|
949 |
|
950 uint32_t length = propertyValue.get_ArrayOfnsString().Length(); |
|
951 for (uint32_t i= 0; i < length; i++) { |
|
952 nsString& data = propertyValue.get_ArrayOfnsString()[i]; |
|
953 data = GetAddressFromObjectPath(data); |
|
954 } |
|
955 } |
|
956 |
|
957 aProperties.AppendElement(BluetoothNamedValue(propertyName, propertyValue)); |
|
958 return true; |
|
959 } |
|
960 |
|
961 static void |
|
962 ParseProperties(DBusMessageIter* aIter, |
|
963 BluetoothValue& aValue, |
|
964 nsAString& aErrorStr, |
|
965 const Properties* aPropertyTypes, |
|
966 const int aPropertyTypeLen) |
|
967 { |
|
968 DBusMessageIter dict_entry, dict; |
|
969 int prop_index = -1; |
|
970 |
|
971 MOZ_ASSERT(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_ARRAY, |
|
972 "Trying to parse a property from sth. that's not an array"); |
|
973 |
|
974 dbus_message_iter_recurse(aIter, &dict); |
|
975 InfallibleTArray<BluetoothNamedValue> props; |
|
976 do { |
|
977 MOZ_ASSERT(dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY, |
|
978 "Trying to parse a property from sth. that's not an dict!"); |
|
979 dbus_message_iter_recurse(&dict, &dict_entry); |
|
980 |
|
981 if (!GetProperty(dict_entry, aPropertyTypes, aPropertyTypeLen, &prop_index, |
|
982 props)) { |
|
983 aErrorStr.AssignLiteral("Can't Create Property!"); |
|
984 BT_WARNING("Can't create property!"); |
|
985 return; |
|
986 } |
|
987 } while (dbus_message_iter_next(&dict)); |
|
988 |
|
989 aValue = props; |
|
990 } |
|
991 |
|
992 static bool |
|
993 UnpackPropertiesMessage(DBusMessage* aMsg, DBusError* aErr, |
|
994 BluetoothValue& aValue, const char* aIface) |
|
995 { |
|
996 MOZ_ASSERT(aMsg); |
|
997 |
|
998 const Properties* propertyTypes; |
|
999 int propertyTypesLength; |
|
1000 |
|
1001 nsAutoString errorStr; |
|
1002 if (IsDBusMessageError(aMsg, aErr, errorStr) || |
|
1003 dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { |
|
1004 BT_WARNING("dbus message has an error."); |
|
1005 return false; |
|
1006 } |
|
1007 |
|
1008 DBusMessageIter iter; |
|
1009 if (!dbus_message_iter_init(aMsg, &iter)) { |
|
1010 BT_WARNING("Cannot create dbus message iter!"); |
|
1011 return false; |
|
1012 } |
|
1013 |
|
1014 if (!strcmp(aIface, DBUS_DEVICE_IFACE)) { |
|
1015 propertyTypes = sDeviceProperties; |
|
1016 propertyTypesLength = ArrayLength(sDeviceProperties); |
|
1017 } else if (!strcmp(aIface, DBUS_ADAPTER_IFACE)) { |
|
1018 propertyTypes = sAdapterProperties; |
|
1019 propertyTypesLength = ArrayLength(sAdapterProperties); |
|
1020 } else if (!strcmp(aIface, DBUS_MANAGER_IFACE)) { |
|
1021 propertyTypes = sManagerProperties; |
|
1022 propertyTypesLength = ArrayLength(sManagerProperties); |
|
1023 } else { |
|
1024 return false; |
|
1025 } |
|
1026 |
|
1027 ParseProperties(&iter, aValue, errorStr, propertyTypes, |
|
1028 propertyTypesLength); |
|
1029 return true; |
|
1030 } |
|
1031 |
|
1032 static void |
|
1033 ParsePropertyChange(DBusMessage* aMsg, BluetoothValue& aValue, |
|
1034 nsAString& aErrorStr, const Properties* aPropertyTypes, |
|
1035 const int aPropertyTypeLen) |
|
1036 { |
|
1037 DBusMessageIter iter; |
|
1038 DBusError err; |
|
1039 int prop_index = -1; |
|
1040 InfallibleTArray<BluetoothNamedValue> props; |
|
1041 |
|
1042 dbus_error_init(&err); |
|
1043 if (!dbus_message_iter_init(aMsg, &iter)) { |
|
1044 BT_WARNING("Can't create iterator!"); |
|
1045 return; |
|
1046 } |
|
1047 |
|
1048 if (!GetProperty(iter, aPropertyTypes, aPropertyTypeLen, |
|
1049 &prop_index, props)) { |
|
1050 BT_WARNING("Can't get property!"); |
|
1051 aErrorStr.AssignLiteral("Can't get property!"); |
|
1052 return; |
|
1053 } |
|
1054 aValue = props; |
|
1055 } |
|
1056 |
|
1057 class AppendDeviceNameReplyHandler: public DBusReplyHandler |
|
1058 { |
|
1059 public: |
|
1060 AppendDeviceNameReplyHandler(const nsCString& aIface, |
|
1061 const nsString& aDevicePath, |
|
1062 const BluetoothSignal& aSignal) |
|
1063 : mIface(aIface) |
|
1064 , mDevicePath(aDevicePath) |
|
1065 , mSignal(aSignal) |
|
1066 { |
|
1067 MOZ_ASSERT(!mIface.IsEmpty()); |
|
1068 MOZ_ASSERT(!mDevicePath.IsEmpty()); |
|
1069 } |
|
1070 |
|
1071 void Handle(DBusMessage* aReply) MOZ_OVERRIDE |
|
1072 { |
|
1073 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1074 |
|
1075 if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { |
|
1076 return; |
|
1077 } |
|
1078 |
|
1079 // Get device properties from result of GetProperties |
|
1080 |
|
1081 DBusError err; |
|
1082 dbus_error_init(&err); |
|
1083 |
|
1084 BluetoothValue deviceProperties; |
|
1085 |
|
1086 bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, |
|
1087 mIface.get()); |
|
1088 if (!success) { |
|
1089 BT_WARNING("Failed to get device properties"); |
|
1090 return; |
|
1091 } |
|
1092 |
|
1093 // First we replace object path with device address. |
|
1094 |
|
1095 InfallibleTArray<BluetoothNamedValue>& parameters = |
|
1096 mSignal.value().get_ArrayOfBluetoothNamedValue(); |
|
1097 nsString address = |
|
1098 GetAddressFromObjectPath(mDevicePath); |
|
1099 parameters[0].name().AssignLiteral("address"); |
|
1100 parameters[0].value() = address; |
|
1101 |
|
1102 // Then we append the device's name to the original signal's data. |
|
1103 |
|
1104 InfallibleTArray<BluetoothNamedValue>& properties = |
|
1105 deviceProperties.get_ArrayOfBluetoothNamedValue(); |
|
1106 uint32_t i; |
|
1107 for (i = 0; i < properties.Length(); i++) { |
|
1108 if (properties[i].name().EqualsLiteral("Name")) { |
|
1109 properties[i].name().AssignLiteral("name"); |
|
1110 parameters.AppendElement(properties[i]); |
|
1111 break; |
|
1112 } |
|
1113 } |
|
1114 MOZ_ASSERT_IF(i == properties.Length(), "failed to get device name"); |
|
1115 |
|
1116 nsRefPtr<DistributeBluetoothSignalTask> task = |
|
1117 new DistributeBluetoothSignalTask(mSignal); |
|
1118 NS_DispatchToMainThread(task); |
|
1119 } |
|
1120 |
|
1121 private: |
|
1122 nsCString mIface; |
|
1123 nsString mDevicePath; |
|
1124 BluetoothSignal mSignal; |
|
1125 }; |
|
1126 |
|
1127 static void |
|
1128 AppendDeviceName(BluetoothSignal& aSignal) |
|
1129 { |
|
1130 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1131 MOZ_ASSERT(sDBusConnection); |
|
1132 |
|
1133 BluetoothValue v = aSignal.value(); |
|
1134 if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue || |
|
1135 v.get_ArrayOfBluetoothNamedValue().Length() == 0) { |
|
1136 BT_WARNING("Invalid argument type for AppendDeviceNameRunnable"); |
|
1137 return; |
|
1138 } |
|
1139 const InfallibleTArray<BluetoothNamedValue>& arr = |
|
1140 v.get_ArrayOfBluetoothNamedValue(); |
|
1141 |
|
1142 // Device object path should be put in the first element |
|
1143 if (!arr[0].name().EqualsLiteral("path") || |
|
1144 arr[0].value().type() != BluetoothValue::TnsString) { |
|
1145 BT_WARNING("Invalid object path for AppendDeviceNameRunnable"); |
|
1146 return; |
|
1147 } |
|
1148 |
|
1149 nsString devicePath = arr[0].value().get_nsString(); |
|
1150 |
|
1151 nsRefPtr<AppendDeviceNameReplyHandler> handler = |
|
1152 new AppendDeviceNameReplyHandler(nsCString(DBUS_DEVICE_IFACE), |
|
1153 devicePath, aSignal); |
|
1154 |
|
1155 bool success = sDBusConnection->SendWithReply( |
|
1156 AppendDeviceNameReplyHandler::Callback, handler.get(), 1000, |
|
1157 BLUEZ_DBUS_BASE_IFC, NS_ConvertUTF16toUTF8(devicePath).get(), |
|
1158 DBUS_DEVICE_IFACE, "GetProperties", DBUS_TYPE_INVALID); |
|
1159 |
|
1160 NS_ENSURE_TRUE_VOID(success); |
|
1161 |
|
1162 unused << handler.forget(); // picked up by callback handler |
|
1163 } |
|
1164 |
|
1165 class SetPairingConfirmationTask : public Task |
|
1166 { |
|
1167 public: |
|
1168 SetPairingConfirmationTask(const nsAString& aDeviceAddress, |
|
1169 bool aConfirm, |
|
1170 BluetoothReplyRunnable* aRunnable) |
|
1171 : mDeviceAddress(aDeviceAddress) |
|
1172 , mConfirm(aConfirm) |
|
1173 , mRunnable(aRunnable) |
|
1174 { |
|
1175 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
1176 } |
|
1177 |
|
1178 void Run() MOZ_OVERRIDE |
|
1179 { |
|
1180 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1181 MOZ_ASSERT(sDBusConnection); |
|
1182 |
|
1183 nsAutoString errorStr; |
|
1184 BluetoothValue v = true; |
|
1185 DBusMessage *msg; |
|
1186 |
|
1187 if (!sPairingReqTable->Get(mDeviceAddress, &msg) && mRunnable) { |
|
1188 BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); |
|
1189 errorStr.AssignLiteral("Couldn't get original request message."); |
|
1190 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
1191 |
|
1192 return; |
|
1193 } |
|
1194 |
|
1195 DBusMessage *reply; |
|
1196 |
|
1197 if (mConfirm) { |
|
1198 reply = dbus_message_new_method_return(msg); |
|
1199 } else { |
|
1200 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", |
|
1201 "User rejected confirmation"); |
|
1202 } |
|
1203 |
|
1204 if (!reply) { |
|
1205 BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); |
|
1206 dbus_message_unref(msg); |
|
1207 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
1208 if (mRunnable) { |
|
1209 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
1210 } |
|
1211 return; |
|
1212 } |
|
1213 |
|
1214 bool result = sDBusConnection->Send(reply); |
|
1215 if (!result) { |
|
1216 errorStr.AssignLiteral("Can't send message!"); |
|
1217 } |
|
1218 |
|
1219 dbus_message_unref(msg); |
|
1220 dbus_message_unref(reply); |
|
1221 sPairingReqTable->Remove(mDeviceAddress); |
|
1222 if (mRunnable) { |
|
1223 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
1224 } |
|
1225 } |
|
1226 |
|
1227 private: |
|
1228 nsString mDeviceAddress; |
|
1229 bool mConfirm; |
|
1230 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
1231 }; |
|
1232 |
|
1233 static DBusHandlerResult |
|
1234 AgentEventFilter(DBusConnection *conn, DBusMessage *msg, void *data) |
|
1235 { |
|
1236 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { |
|
1237 BT_WARNING("%s: agent handler not interested (not a method call).\n", |
|
1238 __FUNCTION__); |
|
1239 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1240 } |
|
1241 |
|
1242 DBusError err; |
|
1243 dbus_error_init(&err); |
|
1244 |
|
1245 BT_LOGD("%s: %s, %s", __FUNCTION__, |
|
1246 dbus_message_get_path(msg), |
|
1247 dbus_message_get_member(msg)); |
|
1248 |
|
1249 nsString signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(msg)); |
|
1250 nsString signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(msg)); |
|
1251 nsString errorStr; |
|
1252 BluetoothValue v; |
|
1253 InfallibleTArray<BluetoothNamedValue> parameters; |
|
1254 bool isPairingReq = false; |
|
1255 BluetoothSignal signal(signalName, signalPath, v); |
|
1256 char *objectPath; |
|
1257 |
|
1258 // The following descriptions of each signal are retrieved from: |
|
1259 // |
|
1260 // http://maemo.org/api_refs/5.0/beta/bluez/agent.html |
|
1261 // |
|
1262 if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Cancel")) { |
|
1263 // This method gets called to indicate that the agent request failed before |
|
1264 // a reply was returned. |
|
1265 |
|
1266 // Return directly |
|
1267 DBusMessage *reply = dbus_message_new_method_return(msg); |
|
1268 |
|
1269 if (!reply) { |
|
1270 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
1271 goto handle_error; |
|
1272 } |
|
1273 |
|
1274 dbus_connection_send(conn, reply, nullptr); |
|
1275 dbus_message_unref(reply); |
|
1276 v = parameters; |
|
1277 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Authorize")) { |
|
1278 // This method gets called when the service daemon needs to authorize a |
|
1279 // connection/service request. |
|
1280 const char *uuid; |
|
1281 if (!dbus_message_get_args(msg, nullptr, |
|
1282 DBUS_TYPE_OBJECT_PATH, &objectPath, |
|
1283 DBUS_TYPE_STRING, &uuid, |
|
1284 DBUS_TYPE_INVALID)) { |
|
1285 errorStr.AssignLiteral("Invalid arguments for Authorize() method"); |
|
1286 goto handle_error; |
|
1287 } |
|
1288 |
|
1289 NS_ConvertUTF8toUTF16 uuidStr(uuid); |
|
1290 BluetoothServiceClass serviceClass = |
|
1291 BluetoothUuidHelper::GetBluetoothServiceClass(uuidStr); |
|
1292 if (serviceClass == BluetoothServiceClass::UNKNOWN) { |
|
1293 errorStr.AssignLiteral("Failed to get service class"); |
|
1294 goto handle_error; |
|
1295 } |
|
1296 |
|
1297 DBusMessage* reply = nullptr; |
|
1298 uint32_t i; |
|
1299 for (i = 0; i < MOZ_ARRAY_LENGTH(sAuthorizedServiceClass); i++) { |
|
1300 if (serviceClass == sAuthorizedServiceClass[i]) { |
|
1301 reply = dbus_message_new_method_return(msg); |
|
1302 break; |
|
1303 } |
|
1304 } |
|
1305 |
|
1306 // The uuid isn't authorized |
|
1307 if (i == MOZ_ARRAY_LENGTH(sAuthorizedServiceClass)) { |
|
1308 BT_WARNING("Uuid is not authorized."); |
|
1309 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", |
|
1310 "The uuid is not authorized"); |
|
1311 } |
|
1312 |
|
1313 if (!reply) { |
|
1314 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
1315 goto handle_error; |
|
1316 } |
|
1317 |
|
1318 dbus_connection_send(conn, reply, nullptr); |
|
1319 dbus_message_unref(reply); |
|
1320 return DBUS_HANDLER_RESULT_HANDLED; |
|
1321 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, |
|
1322 "RequestConfirmation")) { |
|
1323 // This method gets called when the service daemon needs to confirm a |
|
1324 // passkey for an authentication. |
|
1325 uint32_t passkey; |
|
1326 if (!dbus_message_get_args(msg, nullptr, |
|
1327 DBUS_TYPE_OBJECT_PATH, &objectPath, |
|
1328 DBUS_TYPE_UINT32, &passkey, |
|
1329 DBUS_TYPE_INVALID)) { |
|
1330 errorStr.AssignLiteral("Invalid arguments: RequestConfirmation()"); |
|
1331 goto handle_error; |
|
1332 } |
|
1333 |
|
1334 parameters.AppendElement( |
|
1335 BluetoothNamedValue(NS_LITERAL_STRING("path"), |
|
1336 NS_ConvertUTF8toUTF16(objectPath))); |
|
1337 parameters.AppendElement( |
|
1338 BluetoothNamedValue(NS_LITERAL_STRING("method"), |
|
1339 NS_LITERAL_STRING("confirmation"))); |
|
1340 parameters.AppendElement( |
|
1341 BluetoothNamedValue(NS_LITERAL_STRING("passkey"), passkey)); |
|
1342 |
|
1343 v = parameters; |
|
1344 isPairingReq = true; |
|
1345 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, |
|
1346 "RequestPinCode")) { |
|
1347 // This method gets called when the service daemon needs to get the passkey |
|
1348 // for an authentication. The return value should be a string of 1-16 |
|
1349 // characters length. The string can be alphanumeric. |
|
1350 if (!dbus_message_get_args(msg, nullptr, |
|
1351 DBUS_TYPE_OBJECT_PATH, &objectPath, |
|
1352 DBUS_TYPE_INVALID)) { |
|
1353 errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method"); |
|
1354 goto handle_error; |
|
1355 } |
|
1356 |
|
1357 parameters.AppendElement( |
|
1358 BluetoothNamedValue(NS_LITERAL_STRING("path"), |
|
1359 NS_ConvertUTF8toUTF16(objectPath))); |
|
1360 parameters.AppendElement( |
|
1361 BluetoothNamedValue(NS_LITERAL_STRING("method"), |
|
1362 NS_LITERAL_STRING("pincode"))); |
|
1363 |
|
1364 v = parameters; |
|
1365 isPairingReq = true; |
|
1366 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, |
|
1367 "RequestPasskey")) { |
|
1368 // This method gets called when the service daemon needs to get the passkey |
|
1369 // for an authentication. The return value should be a numeric value |
|
1370 // between 0-999999. |
|
1371 if (!dbus_message_get_args(msg, nullptr, |
|
1372 DBUS_TYPE_OBJECT_PATH, &objectPath, |
|
1373 DBUS_TYPE_INVALID)) { |
|
1374 errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method"); |
|
1375 goto handle_error; |
|
1376 } |
|
1377 |
|
1378 parameters.AppendElement(BluetoothNamedValue( |
|
1379 NS_LITERAL_STRING("path"), |
|
1380 NS_ConvertUTF8toUTF16(objectPath))); |
|
1381 parameters.AppendElement(BluetoothNamedValue( |
|
1382 NS_LITERAL_STRING("method"), |
|
1383 NS_LITERAL_STRING("passkey"))); |
|
1384 |
|
1385 v = parameters; |
|
1386 isPairingReq = true; |
|
1387 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) { |
|
1388 // This method gets called when the service daemon unregisters the agent. |
|
1389 // An agent can use it to do cleanup tasks. There is no need to unregister |
|
1390 // the agent, because when this method gets called it has already been |
|
1391 // unregistered. |
|
1392 DBusMessage *reply = dbus_message_new_method_return(msg); |
|
1393 |
|
1394 if (!reply) { |
|
1395 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
1396 goto handle_error; |
|
1397 } |
|
1398 |
|
1399 dbus_connection_send(conn, reply, nullptr); |
|
1400 dbus_message_unref(reply); |
|
1401 |
|
1402 // Do not send a notification to upper layer, too annoying. |
|
1403 return DBUS_HANDLER_RESULT_HANDLED; |
|
1404 } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPairingConsent")) { |
|
1405 // Directly SetPairingconfirmation for RequestPairingConsent here |
|
1406 if (!dbus_message_get_args(msg, nullptr, |
|
1407 DBUS_TYPE_OBJECT_PATH, &objectPath, |
|
1408 DBUS_TYPE_INVALID)) { |
|
1409 errorStr.AssignLiteral("Invalid arguments: RequestPairingConsent()"); |
|
1410 goto handle_error; |
|
1411 } |
|
1412 |
|
1413 nsString address = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)); |
|
1414 sPairingReqTable->Put(address, msg); |
|
1415 Task* task = new SetPairingConfirmationTask(address, true, nullptr); |
|
1416 DispatchToDBusThread(task); |
|
1417 // Increase dbus message reference counts, it will be decreased in |
|
1418 // SetPairingConfirmationTask |
|
1419 dbus_message_ref(msg); |
|
1420 // Do not send a notification to upper layer |
|
1421 return DBUS_HANDLER_RESULT_HANDLED; |
|
1422 } else { |
|
1423 #ifdef DEBUG |
|
1424 BT_WARNING("agent handler %s: Unhandled event. Ignore.", __FUNCTION__); |
|
1425 #endif |
|
1426 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1427 } |
|
1428 |
|
1429 if (!errorStr.IsEmpty()) { |
|
1430 BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); |
|
1431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1432 } |
|
1433 |
|
1434 // Update value after parsing DBus message |
|
1435 signal.value() = v; |
|
1436 |
|
1437 if (isPairingReq) { |
|
1438 sPairingReqTable->Put( |
|
1439 GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath)), msg); |
|
1440 |
|
1441 // Increase ref count here because we need this message later. |
|
1442 // It'll be unrefed when set*Internal() is called. |
|
1443 dbus_message_ref(msg); |
|
1444 |
|
1445 AppendDeviceName(signal); |
|
1446 } else { |
|
1447 NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); |
|
1448 } |
|
1449 |
|
1450 return DBUS_HANDLER_RESULT_HANDLED; |
|
1451 |
|
1452 handle_error: |
|
1453 BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); |
|
1454 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1455 } |
|
1456 |
|
1457 class RegisterAgentReplyHandler : public DBusReplyHandler |
|
1458 { |
|
1459 public: |
|
1460 RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable) |
|
1461 : mAgentVTable(aAgentVTable) |
|
1462 { |
|
1463 MOZ_ASSERT(aAgentVTable); |
|
1464 } |
|
1465 |
|
1466 void Handle(DBusMessage* aReply) |
|
1467 { |
|
1468 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1469 MOZ_ASSERT(sDBusConnection); |
|
1470 |
|
1471 if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { |
|
1472 return; |
|
1473 } |
|
1474 |
|
1475 // There is no "RegisterAgent" function defined in device interface. |
|
1476 // When we call "CreatePairedDevice", it will do device agent registration |
|
1477 // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html) |
|
1478 if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), |
|
1479 KEY_REMOTE_AGENT, |
|
1480 mAgentVTable, |
|
1481 nullptr)) { |
|
1482 BT_WARNING("%s: Can't register object path %s for remote device agent!", |
|
1483 __FUNCTION__, KEY_REMOTE_AGENT); |
|
1484 return; |
|
1485 } |
|
1486 |
|
1487 NS_DispatchToMainThread(new PrepareProfileManagersRunnable()); |
|
1488 } |
|
1489 |
|
1490 private: |
|
1491 const DBusObjectPathVTable* mAgentVTable; |
|
1492 }; |
|
1493 |
|
1494 class AddReservedServiceRecordsReplyHandler : public DBusReplyHandler |
|
1495 { |
|
1496 public: |
|
1497 void Handle(DBusMessage* aReply) |
|
1498 { |
|
1499 static const DBusObjectPathVTable sAgentVTable = { |
|
1500 nullptr, AgentEventFilter, nullptr, nullptr, nullptr, nullptr |
|
1501 }; |
|
1502 |
|
1503 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1504 |
|
1505 if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { |
|
1506 return; |
|
1507 } |
|
1508 |
|
1509 // TODO/qdot: This needs to be held for the life of the bluetooth connection |
|
1510 // so we could clean it up. For right now though, we can throw it away. |
|
1511 nsTArray<uint32_t> handles; |
|
1512 |
|
1513 ExtractHandles(aReply, handles); |
|
1514 |
|
1515 if(!RegisterAgent(&sAgentVTable)) { |
|
1516 BT_WARNING("Failed to register agent"); |
|
1517 } |
|
1518 } |
|
1519 |
|
1520 private: |
|
1521 void ExtractHandles(DBusMessage *aMessage, nsTArray<uint32_t>& aOutHandles) |
|
1522 { |
|
1523 DBusError error; |
|
1524 int length; |
|
1525 uint32_t* handles = nullptr; |
|
1526 |
|
1527 dbus_error_init(&error); |
|
1528 |
|
1529 bool success = dbus_message_get_args(aMessage, &error, |
|
1530 DBUS_TYPE_ARRAY, |
|
1531 DBUS_TYPE_UINT32, |
|
1532 &handles, &length, |
|
1533 DBUS_TYPE_INVALID); |
|
1534 if (success != TRUE) { |
|
1535 LOG_AND_FREE_DBUS_ERROR(&error); |
|
1536 return; |
|
1537 } |
|
1538 |
|
1539 if (!handles) { |
|
1540 BT_WARNING("Null array in extract_handles"); |
|
1541 return; |
|
1542 } |
|
1543 |
|
1544 for (int i = 0; i < length; ++i) { |
|
1545 aOutHandles.AppendElement(handles[i]); |
|
1546 } |
|
1547 } |
|
1548 |
|
1549 bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable) |
|
1550 { |
|
1551 const char* agentPath = KEY_LOCAL_AGENT; |
|
1552 const char* capabilities = B2G_AGENT_CAPABILITIES; |
|
1553 |
|
1554 MOZ_ASSERT(sDBusConnection); |
|
1555 |
|
1556 // Local agent means agent for Adapter, not agent for Device. Some signals |
|
1557 // will be passed to local agent, some will be passed to device agent. |
|
1558 // For example, if a remote device would like to pair with us, then the |
|
1559 // signal will be passed to local agent. If we start pairing process with |
|
1560 // calling CreatePairedDevice, we'll get signal which should be passed to |
|
1561 // device agent. |
|
1562 if (!dbus_connection_register_object_path(sDBusConnection->GetConnection(), |
|
1563 KEY_LOCAL_AGENT, |
|
1564 aAgentVTable, |
|
1565 nullptr)) { |
|
1566 BT_WARNING("%s: Can't register object path %s for agent!", |
|
1567 __FUNCTION__, KEY_LOCAL_AGENT); |
|
1568 return false; |
|
1569 } |
|
1570 |
|
1571 nsRefPtr<RegisterAgentReplyHandler> handler = |
|
1572 new RegisterAgentReplyHandler(aAgentVTable); |
|
1573 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
1574 |
|
1575 bool success = sDBusConnection->SendWithReply( |
|
1576 RegisterAgentReplyHandler::Callback, handler.get(), -1, |
|
1577 BLUEZ_DBUS_BASE_IFC, |
|
1578 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
1579 DBUS_ADAPTER_IFACE, "RegisterAgent", |
|
1580 DBUS_TYPE_OBJECT_PATH, &agentPath, |
|
1581 DBUS_TYPE_STRING, &capabilities, |
|
1582 DBUS_TYPE_INVALID); |
|
1583 |
|
1584 NS_ENSURE_TRUE(success, false); |
|
1585 |
|
1586 unused << handler.forget(); // picked up by callback handler |
|
1587 |
|
1588 return true; |
|
1589 } |
|
1590 }; |
|
1591 |
|
1592 class AddReservedServiceRecordsTask : public Task |
|
1593 { |
|
1594 public: |
|
1595 AddReservedServiceRecordsTask() |
|
1596 { } |
|
1597 |
|
1598 void Run() |
|
1599 { |
|
1600 static const dbus_uint32_t sServices[] = { |
|
1601 BluetoothServiceClass::HANDSFREE_AG, |
|
1602 BluetoothServiceClass::HEADSET_AG, |
|
1603 BluetoothServiceClass::OBJECT_PUSH |
|
1604 }; |
|
1605 |
|
1606 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1607 MOZ_ASSERT(sDBusConnection); |
|
1608 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
1609 |
|
1610 nsRefPtr<DBusReplyHandler> handler = |
|
1611 new AddReservedServiceRecordsReplyHandler(); |
|
1612 |
|
1613 const dbus_uint32_t* services = sServices; |
|
1614 |
|
1615 bool success = sDBusConnection->SendWithReply( |
|
1616 DBusReplyHandler::Callback, handler.get(), -1, |
|
1617 BLUEZ_DBUS_BASE_IFC, |
|
1618 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
1619 DBUS_ADAPTER_IFACE, "AddReservedServiceRecords", |
|
1620 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
|
1621 &services, ArrayLength(sServices), DBUS_TYPE_INVALID); |
|
1622 |
|
1623 NS_ENSURE_TRUE_VOID(success); |
|
1624 |
|
1625 unused << handler.forget(); /* picked up by callback handler */ |
|
1626 } |
|
1627 }; |
|
1628 |
|
1629 class PrepareAdapterRunnable : public nsRunnable |
|
1630 { |
|
1631 public: |
|
1632 PrepareAdapterRunnable() |
|
1633 { } |
|
1634 |
|
1635 NS_IMETHOD Run() |
|
1636 { |
|
1637 MOZ_ASSERT(NS_IsMainThread()); |
|
1638 |
|
1639 Task* task = new AddReservedServiceRecordsTask(); |
|
1640 DispatchToDBusThread(task); |
|
1641 |
|
1642 return NS_OK; |
|
1643 } |
|
1644 }; |
|
1645 |
|
1646 class RequestPlayStatusTask : public nsRunnable |
|
1647 { |
|
1648 public: |
|
1649 RequestPlayStatusTask() |
|
1650 { |
|
1651 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1652 } |
|
1653 |
|
1654 nsresult Run() |
|
1655 { |
|
1656 MOZ_ASSERT(NS_IsMainThread()); |
|
1657 |
|
1658 BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID), |
|
1659 NS_LITERAL_STRING(KEY_ADAPTER), |
|
1660 InfallibleTArray<BluetoothNamedValue>()); |
|
1661 |
|
1662 BluetoothService* bs = BluetoothService::Get(); |
|
1663 NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE); |
|
1664 bs->DistributeSignal(signal); |
|
1665 |
|
1666 return NS_OK; |
|
1667 } |
|
1668 }; |
|
1669 |
|
1670 // Called by dbus during WaitForAndDispatchEventNative() |
|
1671 // This function is called on the IOThread |
|
1672 static DBusHandlerResult |
|
1673 EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData) |
|
1674 { |
|
1675 // I/O thread |
|
1676 MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be called from Main Thread!"); |
|
1677 |
|
1678 if (dbus_message_get_type(aMsg) != DBUS_MESSAGE_TYPE_SIGNAL) { |
|
1679 BT_WARNING("%s: event handler not interested in %s (not a signal).\n", |
|
1680 __FUNCTION__, dbus_message_get_member(aMsg)); |
|
1681 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1682 } |
|
1683 |
|
1684 if (dbus_message_get_path(aMsg) == nullptr) { |
|
1685 BT_WARNING("DBusMessage %s has no bluetooth destination, ignoring\n", |
|
1686 dbus_message_get_member(aMsg)); |
|
1687 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1688 } |
|
1689 |
|
1690 DBusError err; |
|
1691 dbus_error_init(&err); |
|
1692 |
|
1693 nsAutoString signalPath; |
|
1694 nsAutoString signalName; |
|
1695 nsAutoString signalInterface; |
|
1696 |
|
1697 BT_LOGD("%s: %s, %s, %s", __FUNCTION__, |
|
1698 dbus_message_get_interface(aMsg), |
|
1699 dbus_message_get_path(aMsg), |
|
1700 dbus_message_get_member(aMsg)); |
|
1701 |
|
1702 signalInterface = NS_ConvertUTF8toUTF16(dbus_message_get_interface(aMsg)); |
|
1703 signalPath = NS_ConvertUTF8toUTF16(dbus_message_get_path(aMsg)); |
|
1704 signalName = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); |
|
1705 nsString errorStr; |
|
1706 BluetoothValue v; |
|
1707 |
|
1708 // Since the signalPath extracted from dbus message is a object path, |
|
1709 // we'd like to re-assign them to corresponding key entry in |
|
1710 // BluetoothSignalObserverTable |
|
1711 if (signalInterface.EqualsLiteral(DBUS_MANAGER_IFACE)) { |
|
1712 signalPath.AssignLiteral(KEY_MANAGER); |
|
1713 } else if (signalInterface.EqualsLiteral(DBUS_ADAPTER_IFACE)) { |
|
1714 signalPath.AssignLiteral(KEY_ADAPTER); |
|
1715 } else if (signalInterface.EqualsLiteral(DBUS_DEVICE_IFACE)){ |
|
1716 signalPath = GetAddressFromObjectPath(signalPath); |
|
1717 } |
|
1718 |
|
1719 if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceFound")) { |
|
1720 DBusMessageIter iter; |
|
1721 |
|
1722 if (!dbus_message_iter_init(aMsg, &iter)) { |
|
1723 BT_WARNING("Can't create iterator!"); |
|
1724 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1725 } |
|
1726 |
|
1727 const char* addr; |
|
1728 dbus_message_iter_get_basic(&iter, &addr); |
|
1729 |
|
1730 if (!dbus_message_iter_next(&iter)) { |
|
1731 errorStr.AssignLiteral("Unexpected message struct in msg DeviceFound"); |
|
1732 } else { |
|
1733 ParseProperties(&iter, |
|
1734 v, |
|
1735 errorStr, |
|
1736 sDeviceProperties, |
|
1737 ArrayLength(sDeviceProperties)); |
|
1738 |
|
1739 InfallibleTArray<BluetoothNamedValue>& properties = |
|
1740 v.get_ArrayOfBluetoothNamedValue(); |
|
1741 |
|
1742 // The DBus DeviceFound message actually passes back a key value object |
|
1743 // with the address as the key and the rest of the device properties as |
|
1744 // a dict value. After we parse out the properties, we need to go back |
|
1745 // and add the address to the ipdl dict we've created to make sure we |
|
1746 // have all of the information to correctly build the device. |
|
1747 nsAutoString address = NS_ConvertUTF8toUTF16(addr); |
|
1748 properties.AppendElement( |
|
1749 BluetoothNamedValue(NS_LITERAL_STRING("Address"), address)); |
|
1750 properties.AppendElement( |
|
1751 BluetoothNamedValue(NS_LITERAL_STRING("Path"), |
|
1752 GetObjectPathFromAddress(signalPath, address))); |
|
1753 |
|
1754 if (!ContainsIcon(properties)) { |
|
1755 for (uint32_t i = 0; i < properties.Length(); i++) { |
|
1756 // It is possible that property Icon missed due to CoD of major |
|
1757 // class is TOY but service class is "Audio", we need to assign |
|
1758 // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. |
|
1759 // As HFP specification defined that |
|
1760 // service class is "Audio" can be considered as HFP AG. |
|
1761 if (properties[i].name().EqualsLiteral("Class")) { |
|
1762 if (HasAudioService(properties[i].value().get_uint32_t())) { |
|
1763 v.get_ArrayOfBluetoothNamedValue().AppendElement( |
|
1764 BluetoothNamedValue(NS_LITERAL_STRING("Icon"), |
|
1765 NS_LITERAL_STRING("audio-card"))); |
|
1766 } |
|
1767 break; |
|
1768 } |
|
1769 } |
|
1770 } |
|
1771 } |
|
1772 } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, |
|
1773 "DeviceDisappeared")) { |
|
1774 const char* str; |
|
1775 if (!dbus_message_get_args(aMsg, &err, |
|
1776 DBUS_TYPE_STRING, &str, |
|
1777 DBUS_TYPE_INVALID)) { |
|
1778 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); |
|
1779 errorStr.AssignLiteral("Cannot parse device address!"); |
|
1780 } else { |
|
1781 v = NS_ConvertUTF8toUTF16(str); |
|
1782 } |
|
1783 } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, |
|
1784 "DeviceCreated")) { |
|
1785 const char* str; |
|
1786 if (!dbus_message_get_args(aMsg, &err, |
|
1787 DBUS_TYPE_OBJECT_PATH, &str, |
|
1788 DBUS_TYPE_INVALID)) { |
|
1789 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); |
|
1790 errorStr.AssignLiteral("Cannot parse device path!"); |
|
1791 } else { |
|
1792 v = NS_ConvertUTF8toUTF16(str); |
|
1793 } |
|
1794 } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, |
|
1795 "DeviceRemoved")) { |
|
1796 const char* str; |
|
1797 if (!dbus_message_get_args(aMsg, &err, |
|
1798 DBUS_TYPE_OBJECT_PATH, &str, |
|
1799 DBUS_TYPE_INVALID)) { |
|
1800 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); |
|
1801 errorStr.AssignLiteral("Cannot parse device path!"); |
|
1802 } else { |
|
1803 v = NS_ConvertUTF8toUTF16(str); |
|
1804 } |
|
1805 } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, |
|
1806 "PropertyChanged")) { |
|
1807 ParsePropertyChange(aMsg, |
|
1808 v, |
|
1809 errorStr, |
|
1810 sAdapterProperties, |
|
1811 ArrayLength(sAdapterProperties)); |
|
1812 } else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE, |
|
1813 "PropertyChanged")) { |
|
1814 ParsePropertyChange(aMsg, |
|
1815 v, |
|
1816 errorStr, |
|
1817 sDeviceProperties, |
|
1818 ArrayLength(sDeviceProperties)); |
|
1819 |
|
1820 if (v.type() == BluetoothValue::T__None) { |
|
1821 BT_WARNING("PropertyChanged event couldn't be parsed."); |
|
1822 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1823 } |
|
1824 |
|
1825 BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0]; |
|
1826 if (property.name().EqualsLiteral("Paired")) { |
|
1827 // Original approach: Broadcast system message of |
|
1828 // "bluetooth-pairedstatuschanged" from BluetoothService. |
|
1829 BluetoothValue newValue(v); |
|
1830 ToLowerCase(newValue.get_ArrayOfBluetoothNamedValue()[0].name()); |
|
1831 BluetoothSignal signal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID), |
|
1832 NS_LITERAL_STRING(KEY_LOCAL_AGENT), |
|
1833 newValue); |
|
1834 NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); |
|
1835 |
|
1836 // New approach: Dispatch event from BluetoothAdapter |
|
1837 bool status = property.value(); |
|
1838 InfallibleTArray<BluetoothNamedValue> parameters; |
|
1839 parameters.AppendElement( |
|
1840 BluetoothNamedValue(NS_LITERAL_STRING("address"), signalPath)); |
|
1841 parameters.AppendElement( |
|
1842 BluetoothNamedValue(NS_LITERAL_STRING("status"), status)); |
|
1843 signal.path() = NS_LITERAL_STRING(KEY_ADAPTER); |
|
1844 signal.value() = parameters; |
|
1845 NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal)); |
|
1846 } else if (property.name().EqualsLiteral("Connected")) { |
|
1847 MonitorAutoLock lock(*sStopBluetoothMonitor); |
|
1848 |
|
1849 if (property.value().get_bool()) { |
|
1850 ++sConnectedDeviceCount; |
|
1851 } else { |
|
1852 MOZ_ASSERT(sConnectedDeviceCount > 0); |
|
1853 if (--sConnectedDeviceCount == 0) { |
|
1854 lock.Notify(); |
|
1855 } |
|
1856 } |
|
1857 } |
|
1858 } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, "AdapterAdded")) { |
|
1859 const char* str; |
|
1860 if (!dbus_message_get_args(aMsg, &err, |
|
1861 DBUS_TYPE_OBJECT_PATH, &str, |
|
1862 DBUS_TYPE_INVALID)) { |
|
1863 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg); |
|
1864 errorStr.AssignLiteral("Cannot parse manager path!"); |
|
1865 } else { |
|
1866 v = NS_ConvertUTF8toUTF16(str); |
|
1867 sAdapterPath = v.get_nsString(); |
|
1868 NS_DispatchToMainThread(new TryFiringAdapterAddedRunnable(true)); |
|
1869 NS_DispatchToMainThread(new PrepareAdapterRunnable()); |
|
1870 |
|
1871 /** |
|
1872 * The adapter name isn't ready for the time being. Wait for the upcoming |
|
1873 * signal PropertyChanged of adapter name, and then propagate signal |
|
1874 * AdapterAdded to BluetoothManager. |
|
1875 */ |
|
1876 return DBUS_HANDLER_RESULT_HANDLED; |
|
1877 } |
|
1878 } else if (dbus_message_is_signal(aMsg, DBUS_MANAGER_IFACE, |
|
1879 "PropertyChanged")) { |
|
1880 ParsePropertyChange(aMsg, |
|
1881 v, |
|
1882 errorStr, |
|
1883 sManagerProperties, |
|
1884 ArrayLength(sManagerProperties)); |
|
1885 } else if (dbus_message_is_signal(aMsg, DBUS_SINK_IFACE, |
|
1886 "PropertyChanged")) { |
|
1887 ParsePropertyChange(aMsg, |
|
1888 v, |
|
1889 errorStr, |
|
1890 sSinkProperties, |
|
1891 ArrayLength(sSinkProperties)); |
|
1892 } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "GetPlayStatus")) { |
|
1893 NS_DispatchToMainThread(new RequestPlayStatusTask()); |
|
1894 return DBUS_HANDLER_RESULT_HANDLED; |
|
1895 } else if (dbus_message_is_signal(aMsg, DBUS_CTL_IFACE, "PropertyChanged")) { |
|
1896 ParsePropertyChange(aMsg, |
|
1897 v, |
|
1898 errorStr, |
|
1899 sControlProperties, |
|
1900 ArrayLength(sControlProperties)); |
|
1901 } else if (dbus_message_is_signal(aMsg, DBUS_INPUT_IFACE, |
|
1902 "PropertyChanged")) { |
|
1903 ParsePropertyChange(aMsg, |
|
1904 v, |
|
1905 errorStr, |
|
1906 sInputProperties, |
|
1907 ArrayLength(sInputProperties)); |
|
1908 } else { |
|
1909 errorStr = NS_ConvertUTF8toUTF16(dbus_message_get_member(aMsg)); |
|
1910 errorStr.AppendLiteral(" Signal not handled!"); |
|
1911 } |
|
1912 |
|
1913 if (!errorStr.IsEmpty()) { |
|
1914 BT_WARNING(NS_ConvertUTF16toUTF8(errorStr).get()); |
|
1915 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
|
1916 } |
|
1917 |
|
1918 BluetoothSignal signal(signalName, signalPath, v); |
|
1919 nsRefPtr<nsRunnable> task; |
|
1920 if (signalInterface.EqualsLiteral(DBUS_SINK_IFACE)) { |
|
1921 task = new SinkPropertyChangedHandler(signal); |
|
1922 } else if (signalInterface.EqualsLiteral(DBUS_CTL_IFACE)) { |
|
1923 task = new ControlPropertyChangedHandler(signal); |
|
1924 } else if (signalInterface.EqualsLiteral(DBUS_INPUT_IFACE)) { |
|
1925 task = new InputPropertyChangedHandler(signal); |
|
1926 } else { |
|
1927 task = new DistributeBluetoothSignalTask(signal); |
|
1928 } |
|
1929 |
|
1930 NS_DispatchToMainThread(task); |
|
1931 |
|
1932 return DBUS_HANDLER_RESULT_HANDLED; |
|
1933 } |
|
1934 |
|
1935 static void |
|
1936 OnDefaultAdapterReply(DBusMessage* aReply, void* aData) |
|
1937 { |
|
1938 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1939 |
|
1940 if (!aReply || dbus_message_is_error(aReply, DBUS_ERROR_TIMEOUT)) { |
|
1941 return; |
|
1942 } |
|
1943 |
|
1944 DBusError err; |
|
1945 dbus_error_init(&err); |
|
1946 |
|
1947 BluetoothValue v; |
|
1948 nsAutoString errorString; |
|
1949 |
|
1950 UnpackObjectPathMessage(aReply, &err, v, errorString); |
|
1951 |
|
1952 if (!errorString.IsEmpty()) { |
|
1953 return; |
|
1954 } |
|
1955 |
|
1956 sAdapterPath = v.get_nsString(); |
|
1957 |
|
1958 nsRefPtr<PrepareAdapterRunnable> b = new PrepareAdapterRunnable(); |
|
1959 if (NS_FAILED(NS_DispatchToMainThread(b))) { |
|
1960 BT_WARNING("Failed to dispatch to main thread!"); |
|
1961 } |
|
1962 } |
|
1963 |
|
1964 bool |
|
1965 BluetoothDBusService::IsReady() |
|
1966 { |
|
1967 if (!IsEnabled() || !sDBusConnection || IsToggling()) { |
|
1968 BT_WARNING("Bluetooth service is not ready yet!"); |
|
1969 return false; |
|
1970 } |
|
1971 return true; |
|
1972 } |
|
1973 |
|
1974 class StartDBusConnectionTask : public Task |
|
1975 { |
|
1976 public: |
|
1977 StartDBusConnectionTask(RawDBusConnection* aConnection) |
|
1978 : mConnection(aConnection) |
|
1979 { |
|
1980 MOZ_ASSERT(mConnection); |
|
1981 } |
|
1982 |
|
1983 void Run() |
|
1984 { |
|
1985 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
1986 |
|
1987 if (sDBusConnection) { |
|
1988 BT_WARNING("DBus connection has already been established."); |
|
1989 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(true); |
|
1990 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
1991 BT_WARNING("Failed to dispatch to main thread!"); |
|
1992 } |
|
1993 return; |
|
1994 } |
|
1995 |
|
1996 // Add a filter for all incoming messages_base |
|
1997 if (!dbus_connection_add_filter(mConnection->GetConnection(), |
|
1998 EventFilter, nullptr, nullptr)) { |
|
1999 BT_WARNING("Cannot create DBus Event Filter for DBus Thread!"); |
|
2000 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); |
|
2001 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2002 BT_WARNING("Failed to dispatch to main thread!"); |
|
2003 } |
|
2004 return; |
|
2005 } |
|
2006 |
|
2007 mConnection->Watch(); |
|
2008 |
|
2009 if (!sPairingReqTable) { |
|
2010 sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >; |
|
2011 } |
|
2012 |
|
2013 sDBusConnection = mConnection.forget(); |
|
2014 |
|
2015 nsRefPtr<nsRunnable> runnable = |
|
2016 new BluetoothService::ToggleBtAck(true); |
|
2017 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2018 BT_WARNING("Failed to dispatch to main thread!"); |
|
2019 return; |
|
2020 } |
|
2021 |
|
2022 /* Normally we'll receive the signal 'AdapterAdded' with the adapter object |
|
2023 * path from the DBus daemon during start up. So, there's no need to query |
|
2024 * the object path of default adapter here. However, if we restart from a |
|
2025 * crash, the default adapter might already be available, so we ask the daemon |
|
2026 * explicitly here. |
|
2027 */ |
|
2028 if (sAdapterPath.IsEmpty()) { |
|
2029 bool success = sDBusConnection->SendWithReply(OnDefaultAdapterReply, nullptr, |
|
2030 1000, BLUEZ_DBUS_BASE_IFC, "/", |
|
2031 DBUS_MANAGER_IFACE, |
|
2032 "DefaultAdapter", |
|
2033 DBUS_TYPE_INVALID); |
|
2034 if (!success) { |
|
2035 BT_WARNING("Failed to query default adapter!"); |
|
2036 } |
|
2037 } |
|
2038 } |
|
2039 |
|
2040 private: |
|
2041 nsAutoPtr<RawDBusConnection> mConnection; |
|
2042 }; |
|
2043 |
|
2044 class StartBluetoothRunnable MOZ_FINAL : public nsRunnable |
|
2045 { |
|
2046 public: |
|
2047 NS_IMETHOD Run() |
|
2048 { |
|
2049 // This could block. It should never be run on the main thread. |
|
2050 MOZ_ASSERT(!NS_IsMainThread()); // BT thread |
|
2051 |
|
2052 #ifdef MOZ_WIDGET_GONK |
|
2053 if (!sBluedroid.Enable()) { |
|
2054 BT_WARNING("Bluetooth not available."); |
|
2055 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); |
|
2056 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2057 BT_WARNING("Failed to dispatch to main thread!"); |
|
2058 } |
|
2059 return NS_ERROR_FAILURE; |
|
2060 } |
|
2061 #endif |
|
2062 |
|
2063 RawDBusConnection* connection = new RawDBusConnection(); |
|
2064 nsresult rv = connection->EstablishDBusConnection(); |
|
2065 if (NS_FAILED(rv)) { |
|
2066 BT_WARNING("Failed to establish connection to BlueZ daemon"); |
|
2067 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); |
|
2068 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2069 BT_WARNING("Failed to dispatch to main thread!"); |
|
2070 } |
|
2071 return NS_ERROR_FAILURE; |
|
2072 } |
|
2073 |
|
2074 DBusError err; |
|
2075 dbus_error_init(&err); |
|
2076 |
|
2077 // Set which messages will be processed by this dbus connection. |
|
2078 // Since we are maintaining a single thread for all the DBus bluez |
|
2079 // signals we want, register all of them in this thread at startup. |
|
2080 // The event handler will sort the destinations out as needed. The |
|
2081 // call to dbus_bus_add_match has to run on the BT thread because |
|
2082 // it can block. |
|
2083 for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { |
|
2084 dbus_bus_add_match(connection->GetConnection(), |
|
2085 sBluetoothDBusSignals[i], |
|
2086 &err); |
|
2087 if (dbus_error_is_set(&err)) { |
|
2088 LOG_AND_FREE_DBUS_ERROR(&err); |
|
2089 } |
|
2090 } |
|
2091 |
|
2092 Task* task = new StartDBusConnectionTask(connection); |
|
2093 DispatchToDBusThread(task); |
|
2094 |
|
2095 return NS_OK; |
|
2096 } |
|
2097 }; |
|
2098 |
|
2099 nsresult |
|
2100 BluetoothDBusService::StartInternal() |
|
2101 { |
|
2102 nsRefPtr<nsRunnable> runnable = new StartBluetoothRunnable(); |
|
2103 nsresult rv = DispatchToBtThread(runnable); |
|
2104 if (NS_FAILED(rv)) { |
|
2105 BT_WARNING("Failed to dispatch to BT thread!"); |
|
2106 } |
|
2107 return rv; |
|
2108 } |
|
2109 |
|
2110 class DisableBluetoothRunnable MOZ_FINAL : public nsRunnable |
|
2111 { |
|
2112 public: |
|
2113 NS_IMETHOD Run() |
|
2114 { |
|
2115 if (NS_IsMainThread()) { |
|
2116 // Clear |sControllerArray| here while we're on the main thread |
|
2117 sControllerArray.Clear(); |
|
2118 // Forward this runnable to BT thread |
|
2119 return DispatchToBtThread(this); |
|
2120 } |
|
2121 |
|
2122 #ifdef MOZ_WIDGET_GONK |
|
2123 MOZ_ASSERT(sBluedroid.IsEnabled()); |
|
2124 // Disable() return true on success, so we need to invert it |
|
2125 bool isEnabled = !sBluedroid.Disable(); |
|
2126 #else |
|
2127 bool isEnabled = false; |
|
2128 #endif |
|
2129 |
|
2130 nsRefPtr<nsRunnable> runnable = |
|
2131 new BluetoothService::ToggleBtAck(isEnabled); |
|
2132 nsresult rv = NS_DispatchToMainThread(runnable); |
|
2133 if (NS_FAILED(rv)) { |
|
2134 BT_WARNING("Failed to dispatch to main thread!"); |
|
2135 } |
|
2136 return rv; |
|
2137 } |
|
2138 }; |
|
2139 |
|
2140 class DeleteDBusConnectionTask MOZ_FINAL : public Task |
|
2141 { |
|
2142 public: |
|
2143 DeleteDBusConnectionTask() |
|
2144 { } |
|
2145 |
|
2146 void Run() |
|
2147 { |
|
2148 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2149 |
|
2150 if (!sDBusConnection) { |
|
2151 BT_WARNING("DBus connection has not been established."); |
|
2152 nsRefPtr<nsRunnable> runnable = new BluetoothService::ToggleBtAck(false); |
|
2153 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2154 BT_WARNING("Failed to dispatch to main thread!"); |
|
2155 } |
|
2156 return; |
|
2157 } |
|
2158 |
|
2159 for (uint32_t i = 0; i < ArrayLength(sBluetoothDBusSignals); ++i) { |
|
2160 dbus_bus_remove_match(sDBusConnection->GetConnection(), |
|
2161 sBluetoothDBusSignals[i], NULL); |
|
2162 } |
|
2163 |
|
2164 dbus_connection_remove_filter(sDBusConnection->GetConnection(), |
|
2165 EventFilter, nullptr); |
|
2166 |
|
2167 if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), |
|
2168 KEY_LOCAL_AGENT)) { |
|
2169 BT_WARNING("%s: Can't unregister object path %s for agent!", |
|
2170 __FUNCTION__, KEY_LOCAL_AGENT); |
|
2171 } |
|
2172 |
|
2173 if (!dbus_connection_unregister_object_path(sDBusConnection->GetConnection(), |
|
2174 KEY_REMOTE_AGENT)) { |
|
2175 BT_WARNING("%s: Can't unregister object path %s for agent!", |
|
2176 __FUNCTION__, KEY_REMOTE_AGENT); |
|
2177 } |
|
2178 |
|
2179 // unref stored DBusMessages before clearing the hashtable |
|
2180 sPairingReqTable->EnumerateRead(UnrefDBusMessage, nullptr); |
|
2181 sPairingReqTable->Clear(); |
|
2182 |
|
2183 sIsPairing = 0; |
|
2184 sConnectedDeviceCount = 0; |
|
2185 |
|
2186 // This command closes the DBus connection and all its instances |
|
2187 // of DBusWatch will be removed and free'd. |
|
2188 sDBusConnection = nullptr; |
|
2189 |
|
2190 // We can only dispatch to the BT thread if we're on the main |
|
2191 // thread. Thus we dispatch our runnable to the main thread |
|
2192 // from where it will forward itself to the BT thread. |
|
2193 nsRefPtr<nsRunnable> runnable = new DisableBluetoothRunnable(); |
|
2194 if (NS_FAILED(NS_DispatchToMainThread(runnable))) { |
|
2195 BT_WARNING("Failed to dispatch to BT thread!"); |
|
2196 } |
|
2197 } |
|
2198 |
|
2199 private: |
|
2200 static PLDHashOperator |
|
2201 UnrefDBusMessage(const nsAString& key, DBusMessage* value, void* arg) |
|
2202 { |
|
2203 dbus_message_unref(value); |
|
2204 return PL_DHASH_NEXT; |
|
2205 } |
|
2206 }; |
|
2207 |
|
2208 class StopBluetoothRunnable MOZ_FINAL : public nsRunnable |
|
2209 { |
|
2210 public: |
|
2211 NS_IMETHOD Run() |
|
2212 { |
|
2213 MOZ_ASSERT(!NS_IsMainThread()); // BT thread |
|
2214 |
|
2215 // This could block. It should never be run on the main thread. |
|
2216 MonitorAutoLock lock(*sStopBluetoothMonitor); |
|
2217 if (sConnectedDeviceCount > 0) { |
|
2218 lock.Wait(PR_SecondsToInterval(TIMEOUT_FORCE_TO_DISABLE_BT)); |
|
2219 } |
|
2220 |
|
2221 DispatchToDBusThread(new DeleteDBusConnectionTask()); |
|
2222 |
|
2223 return NS_OK; |
|
2224 } |
|
2225 }; |
|
2226 |
|
2227 nsresult |
|
2228 BluetoothDBusService::StopInternal() |
|
2229 { |
|
2230 nsRefPtr<nsRunnable> runnable = new StopBluetoothRunnable(); |
|
2231 nsresult rv = DispatchToBtThread(runnable); |
|
2232 if (NS_FAILED(rv)) { |
|
2233 BT_WARNING("Failed to dispatch to BT thread!"); |
|
2234 } |
|
2235 return rv; |
|
2236 } |
|
2237 |
|
2238 class DefaultAdapterPathReplyHandler : public DBusReplyHandler |
|
2239 { |
|
2240 public: |
|
2241 DefaultAdapterPathReplyHandler(BluetoothReplyRunnable* aRunnable) |
|
2242 : mRunnable(aRunnable) |
|
2243 { |
|
2244 MOZ_ASSERT(mRunnable); |
|
2245 } |
|
2246 |
|
2247 void Handle(DBusMessage* aReply) MOZ_OVERRIDE |
|
2248 { |
|
2249 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2250 |
|
2251 if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { |
|
2252 const char* errStr = "Timeout in DefaultAdapterPathReplyHandler"; |
|
2253 if (aReply) { |
|
2254 errStr = dbus_message_get_error_name(aReply); |
|
2255 if (!errStr) { |
|
2256 errStr = "Bluetooth DBus Error"; |
|
2257 } |
|
2258 } |
|
2259 DispatchBluetoothReply(mRunnable, BluetoothValue(), |
|
2260 NS_ConvertUTF8toUTF16(errStr)); |
|
2261 return; |
|
2262 } |
|
2263 |
|
2264 bool success; |
|
2265 nsAutoString replyError; |
|
2266 |
|
2267 if (mAdapterPath.IsEmpty()) { |
|
2268 success = HandleDefaultAdapterPathReply(aReply, replyError); |
|
2269 } else { |
|
2270 success = HandleGetPropertiesReply(aReply, replyError); |
|
2271 } |
|
2272 |
|
2273 if (!success) { |
|
2274 DispatchBluetoothReply(mRunnable, BluetoothValue(), replyError); |
|
2275 } |
|
2276 } |
|
2277 |
|
2278 protected: |
|
2279 bool HandleDefaultAdapterPathReply(DBusMessage* aReply, |
|
2280 nsAString& aReplyError) |
|
2281 { |
|
2282 BluetoothValue value; |
|
2283 DBusError error; |
|
2284 dbus_error_init(&error); |
|
2285 |
|
2286 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2287 MOZ_ASSERT(sDBusConnection); |
|
2288 |
|
2289 UnpackObjectPathMessage(aReply, &error, value, aReplyError); |
|
2290 |
|
2291 if (!aReplyError.IsEmpty()) { |
|
2292 return false; |
|
2293 } |
|
2294 |
|
2295 mAdapterPath = value.get_nsString(); |
|
2296 |
|
2297 // Acquire another reference to this reply handler |
|
2298 nsRefPtr<DefaultAdapterPathReplyHandler> handler = this; |
|
2299 |
|
2300 bool success = sDBusConnection->SendWithReply( |
|
2301 DefaultAdapterPathReplyHandler::Callback, handler.get(), 1000, |
|
2302 BLUEZ_DBUS_BASE_IFC, |
|
2303 NS_ConvertUTF16toUTF8(mAdapterPath).get(), |
|
2304 DBUS_ADAPTER_IFACE, "GetProperties", DBUS_TYPE_INVALID); |
|
2305 |
|
2306 if (!success) { |
|
2307 aReplyError = NS_LITERAL_STRING("SendWithReply failed"); |
|
2308 return false; |
|
2309 } |
|
2310 |
|
2311 unused << handler.forget(); // picked up by callback handler |
|
2312 |
|
2313 return true; |
|
2314 } |
|
2315 |
|
2316 bool HandleGetPropertiesReply(DBusMessage* aReply, |
|
2317 nsAutoString& aReplyError) |
|
2318 { |
|
2319 BluetoothValue value; |
|
2320 DBusError error; |
|
2321 dbus_error_init(&error); |
|
2322 |
|
2323 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2324 |
|
2325 bool success = UnpackPropertiesMessage(aReply, &error, value, |
|
2326 DBUS_ADAPTER_IFACE); |
|
2327 if (!success) { |
|
2328 aReplyError = NS_ConvertUTF8toUTF16(error.message); |
|
2329 return false; |
|
2330 } |
|
2331 |
|
2332 // We have to manually attach the path to the rest of the elements |
|
2333 value.get_ArrayOfBluetoothNamedValue().AppendElement( |
|
2334 BluetoothNamedValue(NS_LITERAL_STRING("Path"), mAdapterPath)); |
|
2335 |
|
2336 // Dispatch result |
|
2337 DispatchBluetoothReply(mRunnable, value, aReplyError); |
|
2338 |
|
2339 return true; |
|
2340 } |
|
2341 |
|
2342 private: |
|
2343 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2344 nsString mAdapterPath; |
|
2345 }; |
|
2346 |
|
2347 class DefaultAdapterTask : public Task |
|
2348 { |
|
2349 public: |
|
2350 DefaultAdapterTask(BluetoothReplyRunnable* aRunnable) |
|
2351 : mRunnable(aRunnable) |
|
2352 { |
|
2353 MOZ_ASSERT(mRunnable); |
|
2354 } |
|
2355 |
|
2356 void Run() MOZ_OVERRIDE |
|
2357 { |
|
2358 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2359 MOZ_ASSERT(sDBusConnection); |
|
2360 |
|
2361 nsRefPtr<DefaultAdapterPathReplyHandler> handler = |
|
2362 new DefaultAdapterPathReplyHandler(mRunnable); |
|
2363 |
|
2364 bool success = sDBusConnection->SendWithReply( |
|
2365 DefaultAdapterPathReplyHandler::Callback, |
|
2366 handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, |
|
2367 "/", DBUS_MANAGER_IFACE, "DefaultAdapter", |
|
2368 DBUS_TYPE_INVALID); |
|
2369 NS_ENSURE_TRUE_VOID(success); |
|
2370 |
|
2371 unused << handler.forget(); // picked up by callback handler |
|
2372 } |
|
2373 |
|
2374 private: |
|
2375 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2376 }; |
|
2377 |
|
2378 nsresult |
|
2379 BluetoothDBusService::GetDefaultAdapterPathInternal( |
|
2380 BluetoothReplyRunnable* aRunnable) |
|
2381 { |
|
2382 MOZ_ASSERT(NS_IsMainThread()); |
|
2383 |
|
2384 if (!IsReady()) { |
|
2385 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
2386 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
2387 return NS_OK; |
|
2388 } |
|
2389 |
|
2390 Task* task = new DefaultAdapterTask(aRunnable); |
|
2391 DispatchToDBusThread(task); |
|
2392 |
|
2393 return NS_OK; |
|
2394 } |
|
2395 |
|
2396 static void |
|
2397 OnSendDiscoveryMessageReply(DBusMessage *aReply, void *aData) |
|
2398 { |
|
2399 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2400 |
|
2401 nsAutoString errorStr; |
|
2402 |
|
2403 if (!aReply) { |
|
2404 errorStr.AssignLiteral("SendDiscovery failed"); |
|
2405 } |
|
2406 |
|
2407 nsRefPtr<BluetoothReplyRunnable> runnable = |
|
2408 dont_AddRef<BluetoothReplyRunnable>(static_cast<BluetoothReplyRunnable*>(aData)); |
|
2409 |
|
2410 DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); |
|
2411 } |
|
2412 |
|
2413 class SendDiscoveryMessageTask : public Task |
|
2414 { |
|
2415 public: |
|
2416 SendDiscoveryMessageTask(const char* aMessageName, |
|
2417 BluetoothReplyRunnable* aRunnable) |
|
2418 : mMessageName(aMessageName) |
|
2419 , mRunnable(aRunnable) |
|
2420 { |
|
2421 MOZ_ASSERT(!mMessageName.IsEmpty()); |
|
2422 MOZ_ASSERT(mRunnable); |
|
2423 } |
|
2424 |
|
2425 void Run() MOZ_OVERRIDE |
|
2426 { |
|
2427 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2428 MOZ_ASSERT(sDBusConnection); |
|
2429 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2430 |
|
2431 bool success = sDBusConnection->SendWithReply( |
|
2432 OnSendDiscoveryMessageReply, |
|
2433 static_cast<void*>(mRunnable.get()), -1, |
|
2434 BLUEZ_DBUS_BASE_IFC, |
|
2435 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
2436 DBUS_ADAPTER_IFACE, mMessageName.get(), |
|
2437 DBUS_TYPE_INVALID); |
|
2438 NS_ENSURE_TRUE_VOID(success); |
|
2439 |
|
2440 unused << mRunnable.forget(); // picked up by callback handler |
|
2441 } |
|
2442 |
|
2443 private: |
|
2444 const nsCString mMessageName; |
|
2445 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2446 }; |
|
2447 |
|
2448 nsresult |
|
2449 BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName, |
|
2450 BluetoothReplyRunnable* aRunnable) |
|
2451 { |
|
2452 MOZ_ASSERT(NS_IsMainThread()); |
|
2453 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2454 |
|
2455 if (!IsReady()) { |
|
2456 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
2457 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
2458 return NS_OK; |
|
2459 } |
|
2460 |
|
2461 Task* task = new SendDiscoveryMessageTask(aMessageName, aRunnable); |
|
2462 DispatchToDBusThread(task); |
|
2463 |
|
2464 return NS_OK; |
|
2465 } |
|
2466 |
|
2467 nsresult |
|
2468 BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress, |
|
2469 const nsAString& aMessage) |
|
2470 { |
|
2471 DBusReplyCallback callback; |
|
2472 if (aMessage.EqualsLiteral("Connect")) { |
|
2473 callback = InputConnectCallback; |
|
2474 } else if (aMessage.EqualsLiteral("Disconnect")) { |
|
2475 callback = InputDisconnectCallback; |
|
2476 } else { |
|
2477 MOZ_ASSERT(false); |
|
2478 return NS_ERROR_FAILURE; |
|
2479 } |
|
2480 |
|
2481 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2482 nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); |
|
2483 return SendAsyncDBusMessage(objectPath, DBUS_INPUT_IFACE, aMessage, callback); |
|
2484 } |
|
2485 |
|
2486 class SendAsyncDBusMessageTask : public Task |
|
2487 { |
|
2488 public: |
|
2489 SendAsyncDBusMessageTask(DBusReplyCallback aCallback, |
|
2490 BluetoothServiceClass* aServiceClass, |
|
2491 const nsACString& aObjectPath, |
|
2492 const char* aInterface, |
|
2493 const nsACString& aMessage) |
|
2494 : mCallback(aCallback) |
|
2495 , mServiceClass(aServiceClass) |
|
2496 , mObjectPath(aObjectPath) |
|
2497 , mInterface(aInterface) |
|
2498 , mMessage(aMessage) |
|
2499 { |
|
2500 MOZ_ASSERT(mServiceClass); |
|
2501 MOZ_ASSERT(!mObjectPath.IsEmpty()); |
|
2502 MOZ_ASSERT(!mInterface.IsEmpty()); |
|
2503 MOZ_ASSERT(!mMessage.IsEmpty()); |
|
2504 } |
|
2505 |
|
2506 void Run() MOZ_OVERRIDE |
|
2507 { |
|
2508 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2509 MOZ_ASSERT(sDBusConnection); |
|
2510 |
|
2511 bool success = sDBusConnection->SendWithReply( |
|
2512 mCallback, static_cast<void*>(mServiceClass), -1, |
|
2513 BLUEZ_DBUS_BASE_IFC, mObjectPath.get(), mInterface.get(), |
|
2514 mMessage.get(), DBUS_TYPE_INVALID); |
|
2515 NS_ENSURE_TRUE_VOID(success); |
|
2516 |
|
2517 mServiceClass.forget(); |
|
2518 } |
|
2519 |
|
2520 private: |
|
2521 DBusReplyCallback mCallback; |
|
2522 nsAutoPtr<BluetoothServiceClass> mServiceClass; |
|
2523 const nsCString mObjectPath; |
|
2524 const nsCString mInterface; |
|
2525 const nsCString mMessage; |
|
2526 }; |
|
2527 |
|
2528 nsresult |
|
2529 BluetoothDBusService::SendAsyncDBusMessage(const nsAString& aObjectPath, |
|
2530 const char* aInterface, |
|
2531 const nsAString& aMessage, |
|
2532 DBusReplyCallback aCallback) |
|
2533 { |
|
2534 MOZ_ASSERT(NS_IsMainThread()); |
|
2535 MOZ_ASSERT(IsEnabled()); |
|
2536 MOZ_ASSERT(aCallback); |
|
2537 MOZ_ASSERT(!aObjectPath.IsEmpty()); |
|
2538 MOZ_ASSERT(aInterface); |
|
2539 |
|
2540 nsAutoPtr<BluetoothServiceClass> serviceClass(new BluetoothServiceClass()); |
|
2541 if (!strcmp(aInterface, DBUS_SINK_IFACE)) { |
|
2542 *serviceClass = BluetoothServiceClass::A2DP; |
|
2543 } else if (!strcmp(aInterface, DBUS_INPUT_IFACE)) { |
|
2544 *serviceClass = BluetoothServiceClass::HID; |
|
2545 } else { |
|
2546 MOZ_ASSERT(false); |
|
2547 return NS_ERROR_FAILURE; |
|
2548 } |
|
2549 |
|
2550 Task* task = new SendAsyncDBusMessageTask(aCallback, |
|
2551 serviceClass.forget(), |
|
2552 NS_ConvertUTF16toUTF8(aObjectPath), |
|
2553 aInterface, |
|
2554 NS_ConvertUTF16toUTF8(aMessage)); |
|
2555 DispatchToDBusThread(task); |
|
2556 |
|
2557 return NS_OK; |
|
2558 } |
|
2559 |
|
2560 nsresult |
|
2561 BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress, |
|
2562 const nsAString& aMessage) |
|
2563 { |
|
2564 DBusReplyCallback callback; |
|
2565 if (aMessage.EqualsLiteral("Connect")) { |
|
2566 callback = SinkConnectCallback; |
|
2567 } else if (aMessage.EqualsLiteral("Disconnect")) { |
|
2568 callback = SinkDisconnectCallback; |
|
2569 } else { |
|
2570 MOZ_ASSERT(false); |
|
2571 return NS_ERROR_FAILURE; |
|
2572 } |
|
2573 |
|
2574 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2575 nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress); |
|
2576 return SendAsyncDBusMessage(objectPath, DBUS_SINK_IFACE, aMessage, callback); |
|
2577 } |
|
2578 |
|
2579 nsresult |
|
2580 BluetoothDBusService::StopDiscoveryInternal(BluetoothReplyRunnable* aRunnable) |
|
2581 { |
|
2582 return SendDiscoveryMessage("StopDiscovery", aRunnable); |
|
2583 } |
|
2584 |
|
2585 nsresult |
|
2586 BluetoothDBusService::StartDiscoveryInternal(BluetoothReplyRunnable* aRunnable) |
|
2587 { |
|
2588 return SendDiscoveryMessage("StartDiscovery", aRunnable); |
|
2589 } |
|
2590 |
|
2591 class BluetoothArrayOfDevicePropertiesReplyHandler : public DBusReplyHandler |
|
2592 { |
|
2593 public: |
|
2594 BluetoothArrayOfDevicePropertiesReplyHandler( |
|
2595 const nsTArray<nsString>& aDeviceAddresses, |
|
2596 const FilterFunc aFilterFunc, BluetoothReplyRunnable* aRunnable) |
|
2597 : mDeviceAddresses(aDeviceAddresses) |
|
2598 , mProcessedDeviceAddresses(0) |
|
2599 , mFilterFunc(aFilterFunc) |
|
2600 , mRunnable(aRunnable) |
|
2601 , mValues(InfallibleTArray<BluetoothNamedValue>()) |
|
2602 { |
|
2603 MOZ_ASSERT(mRunnable); |
|
2604 } |
|
2605 |
|
2606 void Handle(DBusMessage* aReply) MOZ_OVERRIDE |
|
2607 { |
|
2608 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2609 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2610 MOZ_ASSERT(!mObjectPath.IsEmpty()); |
|
2611 MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); |
|
2612 |
|
2613 const nsTArray<nsString>::index_type i = mProcessedDeviceAddresses++; |
|
2614 |
|
2615 if (!aReply || |
|
2616 (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) { |
|
2617 BT_WARNING("Invalid DBus message"); |
|
2618 ProcessRemainingDeviceAddresses(); |
|
2619 return; |
|
2620 } |
|
2621 |
|
2622 // Get device properties from result of GetProperties |
|
2623 |
|
2624 DBusError err; |
|
2625 dbus_error_init(&err); |
|
2626 |
|
2627 BluetoothValue deviceProperties; |
|
2628 |
|
2629 bool success = UnpackPropertiesMessage(aReply, &err, deviceProperties, |
|
2630 DBUS_DEVICE_IFACE); |
|
2631 if (!success) { |
|
2632 BT_WARNING("Failed to get device properties"); |
|
2633 ProcessRemainingDeviceAddresses(); |
|
2634 return; |
|
2635 } |
|
2636 |
|
2637 InfallibleTArray<BluetoothNamedValue>& devicePropertiesArray = |
|
2638 deviceProperties.get_ArrayOfBluetoothNamedValue(); |
|
2639 |
|
2640 // We have to manually attach the path to the rest of the elements |
|
2641 devicePropertiesArray.AppendElement( |
|
2642 BluetoothNamedValue(NS_LITERAL_STRING("Path"), mObjectPath)); |
|
2643 |
|
2644 // It is possible that property Icon missed due to CoD of major |
|
2645 // class is TOY but service class is "Audio", we need to assign |
|
2646 // Icon as audio-card. This is for PTS test TC_AG_COD_BV_02_I. |
|
2647 // As HFP specification defined that |
|
2648 // service class is "Audio" can be considered as HFP AG. |
|
2649 if (!ContainsIcon(devicePropertiesArray)) { |
|
2650 for (uint32_t j = 0; j < devicePropertiesArray.Length(); ++j) { |
|
2651 BluetoothNamedValue& deviceProperty = devicePropertiesArray[j]; |
|
2652 if (deviceProperty.name().EqualsLiteral("Class")) { |
|
2653 if (HasAudioService(deviceProperty.value().get_uint32_t())) { |
|
2654 devicePropertiesArray.AppendElement( |
|
2655 BluetoothNamedValue(NS_LITERAL_STRING("Icon"), |
|
2656 NS_LITERAL_STRING("audio-card"))); |
|
2657 } |
|
2658 break; |
|
2659 } |
|
2660 } |
|
2661 } |
|
2662 |
|
2663 if (mFilterFunc(deviceProperties)) { |
|
2664 mValues.get_ArrayOfBluetoothNamedValue().AppendElement( |
|
2665 BluetoothNamedValue(mDeviceAddresses[i], deviceProperties)); |
|
2666 } |
|
2667 |
|
2668 ProcessRemainingDeviceAddresses(); |
|
2669 } |
|
2670 |
|
2671 void ProcessRemainingDeviceAddresses() |
|
2672 { |
|
2673 if (mProcessedDeviceAddresses < mDeviceAddresses.Length()) { |
|
2674 if (!SendNextGetProperties()) { |
|
2675 DispatchBluetoothReply(mRunnable, BluetoothValue(), |
|
2676 NS_LITERAL_STRING( |
|
2677 "SendNextGetProperties failed")); |
|
2678 } |
|
2679 } else { |
|
2680 // Send resulting device properties |
|
2681 DispatchBluetoothReply(mRunnable, mValues, EmptyString()); |
|
2682 } |
|
2683 } |
|
2684 |
|
2685 protected: |
|
2686 bool SendNextGetProperties() |
|
2687 { |
|
2688 MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length()); |
|
2689 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2690 MOZ_ASSERT(sDBusConnection); |
|
2691 |
|
2692 // cache object path for reply |
|
2693 mObjectPath = GetObjectPathFromAddress(sAdapterPath, |
|
2694 mDeviceAddresses[mProcessedDeviceAddresses]); |
|
2695 |
|
2696 nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> handler = this; |
|
2697 |
|
2698 bool success = sDBusConnection->SendWithReply( |
|
2699 BluetoothArrayOfDevicePropertiesReplyHandler::Callback, |
|
2700 handler.get(), 1000, BLUEZ_DBUS_BASE_IFC, |
|
2701 NS_ConvertUTF16toUTF8(mObjectPath).get(), |
|
2702 DBUS_DEVICE_IFACE, "GetProperties", |
|
2703 DBUS_TYPE_INVALID); |
|
2704 |
|
2705 NS_ENSURE_TRUE(success, false); |
|
2706 |
|
2707 unused << handler.forget(); // picked up by callback handler |
|
2708 |
|
2709 return true; |
|
2710 } |
|
2711 |
|
2712 private: |
|
2713 nsString mObjectPath; |
|
2714 const nsTArray<nsString> mDeviceAddresses; |
|
2715 nsTArray<nsString>::size_type mProcessedDeviceAddresses; |
|
2716 const FilterFunc mFilterFunc; |
|
2717 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2718 BluetoothValue mValues; |
|
2719 }; |
|
2720 |
|
2721 class ProcessRemainingDeviceAddressesTask : public Task |
|
2722 { |
|
2723 public: |
|
2724 ProcessRemainingDeviceAddressesTask( |
|
2725 BluetoothArrayOfDevicePropertiesReplyHandler* aHandler, |
|
2726 BluetoothReplyRunnable* aRunnable) |
|
2727 : mHandler(aHandler) |
|
2728 , mRunnable(aRunnable) |
|
2729 { |
|
2730 MOZ_ASSERT(mHandler); |
|
2731 MOZ_ASSERT(mRunnable); |
|
2732 } |
|
2733 |
|
2734 void Run() MOZ_OVERRIDE |
|
2735 { |
|
2736 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2737 |
|
2738 mHandler->ProcessRemainingDeviceAddresses(); |
|
2739 } |
|
2740 |
|
2741 private: |
|
2742 nsRefPtr<BluetoothArrayOfDevicePropertiesReplyHandler> mHandler; |
|
2743 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2744 }; |
|
2745 |
|
2746 nsresult |
|
2747 BluetoothDBusService::GetConnectedDevicePropertiesInternal(uint16_t aServiceUuid, |
|
2748 BluetoothReplyRunnable* aRunnable) |
|
2749 { |
|
2750 MOZ_ASSERT(NS_IsMainThread()); |
|
2751 |
|
2752 nsAutoString errorStr; |
|
2753 BluetoothValue values = InfallibleTArray<BluetoothNamedValue>(); |
|
2754 if (!IsReady()) { |
|
2755 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
2756 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
2757 return NS_OK; |
|
2758 } |
|
2759 |
|
2760 nsTArray<nsString> deviceAddresses; |
|
2761 BluetoothProfileManagerBase* profile = |
|
2762 BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); |
|
2763 if (!profile) { |
|
2764 DispatchBluetoothReply(aRunnable, values, |
|
2765 NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE)); |
|
2766 return NS_OK; |
|
2767 } |
|
2768 |
|
2769 if (profile->IsConnected()) { |
|
2770 nsString address; |
|
2771 profile->GetAddress(address); |
|
2772 deviceAddresses.AppendElement(address); |
|
2773 } |
|
2774 |
|
2775 BluetoothArrayOfDevicePropertiesReplyHandler* handler = |
|
2776 new BluetoothArrayOfDevicePropertiesReplyHandler(deviceAddresses, |
|
2777 GetConnectedDevicesFilter, |
|
2778 aRunnable); |
|
2779 Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); |
|
2780 DispatchToDBusThread(task); |
|
2781 |
|
2782 return NS_OK; |
|
2783 } |
|
2784 |
|
2785 nsresult |
|
2786 BluetoothDBusService::GetPairedDevicePropertiesInternal( |
|
2787 const nsTArray<nsString>& aDeviceAddresses, |
|
2788 BluetoothReplyRunnable* aRunnable) |
|
2789 { |
|
2790 MOZ_ASSERT(NS_IsMainThread()); |
|
2791 |
|
2792 if (!IsReady()) { |
|
2793 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
2794 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
2795 return NS_OK; |
|
2796 } |
|
2797 |
|
2798 BluetoothArrayOfDevicePropertiesReplyHandler* handler = |
|
2799 new BluetoothArrayOfDevicePropertiesReplyHandler(aDeviceAddresses, |
|
2800 GetPairedDevicesFilter, |
|
2801 aRunnable); |
|
2802 Task* task = new ProcessRemainingDeviceAddressesTask(handler, aRunnable); |
|
2803 DispatchToDBusThread(task); |
|
2804 |
|
2805 return NS_OK; |
|
2806 } |
|
2807 |
|
2808 class SetPropertyTask : public Task |
|
2809 { |
|
2810 public: |
|
2811 SetPropertyTask(BluetoothObjectType aType, |
|
2812 const nsACString& aName, |
|
2813 BluetoothReplyRunnable* aRunnable) |
|
2814 : mType(aType) |
|
2815 , mName(aName) |
|
2816 , mRunnable(aRunnable) |
|
2817 { |
|
2818 MOZ_ASSERT(mRunnable); |
|
2819 } |
|
2820 |
|
2821 void Send(unsigned int aType, const void* aValue) |
|
2822 { |
|
2823 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2824 MOZ_ASSERT(sDBusConnection); |
|
2825 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2826 |
|
2827 DBusMessage* msg = |
|
2828 dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, |
|
2829 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
2830 sBluetoothDBusIfaces[mType], |
|
2831 "SetProperty"); |
|
2832 if (!msg) { |
|
2833 BT_WARNING("Could not allocate D-Bus message object!"); |
|
2834 return; |
|
2835 } |
|
2836 |
|
2837 const char* name = mName.get(); |
|
2838 if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, |
|
2839 DBUS_TYPE_INVALID)) { |
|
2840 BT_WARNING("Couldn't append arguments to dbus message!"); |
|
2841 return; |
|
2842 } |
|
2843 |
|
2844 DBusMessageIter value_iter, iter; |
|
2845 dbus_message_iter_init_append(msg, &iter); |
|
2846 char var_type[2] = {(char)aType, '\0'}; |
|
2847 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, |
|
2848 var_type, &value_iter) || |
|
2849 !dbus_message_iter_append_basic(&value_iter, aType, aValue) || |
|
2850 !dbus_message_iter_close_container(&iter, &value_iter)) { |
|
2851 BT_WARNING("Could not append argument to method call!"); |
|
2852 dbus_message_unref(msg); |
|
2853 return; |
|
2854 } |
|
2855 |
|
2856 // msg is unref'd as part of SendWithReply |
|
2857 bool success = sDBusConnection->SendWithReply( |
|
2858 GetVoidCallback, |
|
2859 static_cast<void*>(mRunnable), |
|
2860 1000, msg); |
|
2861 NS_ENSURE_TRUE_VOID(success); |
|
2862 |
|
2863 unused << mRunnable.forget(); // picked up by callback handler |
|
2864 } |
|
2865 |
|
2866 private: |
|
2867 BluetoothObjectType mType; |
|
2868 const nsCString mName; |
|
2869 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
2870 }; |
|
2871 |
|
2872 class SetUInt32PropertyTask : public SetPropertyTask |
|
2873 { |
|
2874 public: |
|
2875 SetUInt32PropertyTask(BluetoothObjectType aType, |
|
2876 const nsACString& aName, |
|
2877 uint32_t aValue, |
|
2878 BluetoothReplyRunnable* aRunnable) |
|
2879 : SetPropertyTask(aType, aName, aRunnable) |
|
2880 , mValue(aValue) |
|
2881 { } |
|
2882 |
|
2883 void Run() MOZ_OVERRIDE |
|
2884 { |
|
2885 Send(DBUS_TYPE_UINT32, &mValue); |
|
2886 } |
|
2887 |
|
2888 private: |
|
2889 dbus_uint32_t mValue; |
|
2890 }; |
|
2891 |
|
2892 class SetStringPropertyTask : public SetPropertyTask |
|
2893 { |
|
2894 public: |
|
2895 SetStringPropertyTask(BluetoothObjectType aType, |
|
2896 const nsACString& aName, |
|
2897 const nsACString& aValue, |
|
2898 BluetoothReplyRunnable* aRunnable) |
|
2899 : SetPropertyTask(aType, aName, aRunnable) |
|
2900 , mValue(aValue) |
|
2901 { } |
|
2902 |
|
2903 void Run() MOZ_OVERRIDE |
|
2904 { |
|
2905 const char* value = mValue.get(); |
|
2906 Send(DBUS_TYPE_STRING, &value); |
|
2907 } |
|
2908 |
|
2909 private: |
|
2910 const nsCString mValue; |
|
2911 }; |
|
2912 |
|
2913 class SetBooleanPropertyTask : public SetPropertyTask |
|
2914 { |
|
2915 public: |
|
2916 SetBooleanPropertyTask(BluetoothObjectType aType, |
|
2917 const nsACString& aName, |
|
2918 dbus_bool_t aValue, |
|
2919 BluetoothReplyRunnable* aRunnable) |
|
2920 : SetPropertyTask(aType, aName, aRunnable) |
|
2921 , mValue(aValue) |
|
2922 { |
|
2923 } |
|
2924 |
|
2925 void Run() MOZ_OVERRIDE |
|
2926 { |
|
2927 Send(DBUS_TYPE_BOOLEAN, &mValue); |
|
2928 } |
|
2929 |
|
2930 private: |
|
2931 dbus_bool_t mValue; |
|
2932 }; |
|
2933 |
|
2934 nsresult |
|
2935 BluetoothDBusService::SetProperty(BluetoothObjectType aType, |
|
2936 const BluetoothNamedValue& aValue, |
|
2937 BluetoothReplyRunnable* aRunnable) |
|
2938 { |
|
2939 MOZ_ASSERT(NS_IsMainThread()); |
|
2940 |
|
2941 if (!IsReady()) { |
|
2942 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
2943 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
2944 return NS_OK; |
|
2945 } |
|
2946 |
|
2947 Task* task; |
|
2948 |
|
2949 if (aValue.value().type() == BluetoothValue::Tuint32_t) { |
|
2950 task = new SetUInt32PropertyTask(aType, |
|
2951 NS_ConvertUTF16toUTF8(aValue.name()), |
|
2952 aValue.value().get_uint32_t(), aRunnable); |
|
2953 } else if (aValue.value().type() == BluetoothValue::TnsString) { |
|
2954 task = new SetStringPropertyTask(aType, |
|
2955 NS_ConvertUTF16toUTF8(aValue.name()), |
|
2956 NS_ConvertUTF16toUTF8(aValue.value().get_nsString()), aRunnable); |
|
2957 } else if (aValue.value().type() == BluetoothValue::Tbool) { |
|
2958 task = new SetBooleanPropertyTask(aType, |
|
2959 NS_ConvertUTF16toUTF8(aValue.name()), |
|
2960 aValue.value().get_bool(), aRunnable); |
|
2961 } else { |
|
2962 BT_WARNING("Property type not handled!"); |
|
2963 return NS_ERROR_FAILURE; |
|
2964 } |
|
2965 DispatchToDBusThread(task); |
|
2966 |
|
2967 return NS_OK; |
|
2968 } |
|
2969 |
|
2970 class CreatePairedDeviceInternalTask : public Task |
|
2971 { |
|
2972 public: |
|
2973 CreatePairedDeviceInternalTask(const nsACString& aDeviceAddress, |
|
2974 int aTimeout, |
|
2975 BluetoothReplyRunnable* aRunnable) |
|
2976 : mDeviceAddress(aDeviceAddress) |
|
2977 , mTimeout(aTimeout) |
|
2978 , mRunnable(aRunnable) |
|
2979 { |
|
2980 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
2981 MOZ_ASSERT(mRunnable); |
|
2982 } |
|
2983 |
|
2984 void Run() MOZ_OVERRIDE |
|
2985 { |
|
2986 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
2987 MOZ_ASSERT(sDBusConnection); |
|
2988 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
2989 |
|
2990 const char *deviceAddress = mDeviceAddress.get(); |
|
2991 const char *deviceAgentPath = KEY_REMOTE_AGENT; |
|
2992 const char *capabilities = B2G_AGENT_CAPABILITIES; |
|
2993 |
|
2994 // Then send CreatePairedDevice, it will register a temp device agent then |
|
2995 // unregister it after pairing process is over |
|
2996 bool success = sDBusConnection->SendWithReply( |
|
2997 GetObjectPathCallback, static_cast<void*>(mRunnable), mTimeout, |
|
2998 BLUEZ_DBUS_BASE_IFC, |
|
2999 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
3000 DBUS_ADAPTER_IFACE, |
|
3001 "CreatePairedDevice", |
|
3002 DBUS_TYPE_STRING, &deviceAddress, |
|
3003 DBUS_TYPE_OBJECT_PATH, &deviceAgentPath, |
|
3004 DBUS_TYPE_STRING, &capabilities, |
|
3005 DBUS_TYPE_INVALID); |
|
3006 NS_ENSURE_TRUE_VOID(success); |
|
3007 |
|
3008 unused << mRunnable.forget(); // picked up by callback handler |
|
3009 |
|
3010 /** |
|
3011 * FIXME: Bug 820274 |
|
3012 * |
|
3013 * If the user turns off Bluetooth in the middle of pairing process, |
|
3014 * the callback function GetObjectPathCallback may still be called |
|
3015 * while enabling next time by dbus daemon. To prevent this from |
|
3016 * happening, added a flag to distinguish if Bluetooth has been |
|
3017 * turned off. Nevertheless, we need a check if there is a better |
|
3018 * solution. |
|
3019 * |
|
3020 * Please see Bug 818696 for more information. |
|
3021 */ |
|
3022 sIsPairing++; |
|
3023 } |
|
3024 |
|
3025 private: |
|
3026 const nsCString mDeviceAddress; |
|
3027 int mTimeout; |
|
3028 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3029 }; |
|
3030 |
|
3031 nsresult |
|
3032 BluetoothDBusService::CreatePairedDeviceInternal( |
|
3033 const nsAString& aDeviceAddress, |
|
3034 int aTimeout, |
|
3035 BluetoothReplyRunnable* aRunnable) |
|
3036 { |
|
3037 Task* task = new CreatePairedDeviceInternalTask( |
|
3038 NS_ConvertUTF16toUTF8(aDeviceAddress), |
|
3039 aTimeout, aRunnable); |
|
3040 DispatchToDBusThread(task); |
|
3041 |
|
3042 return NS_OK; |
|
3043 } |
|
3044 |
|
3045 class RemoveDeviceTask : public Task |
|
3046 { |
|
3047 public: |
|
3048 RemoveDeviceTask(const nsAString& aDeviceAddress, |
|
3049 BluetoothReplyRunnable* aRunnable) |
|
3050 : mDeviceAddress(aDeviceAddress) |
|
3051 , mRunnable(aRunnable) |
|
3052 { |
|
3053 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3054 MOZ_ASSERT(mRunnable); |
|
3055 } |
|
3056 |
|
3057 void Run() MOZ_OVERRIDE |
|
3058 { |
|
3059 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3060 MOZ_ASSERT(sDBusConnection); |
|
3061 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
3062 |
|
3063 nsCString deviceObjectPath = |
|
3064 NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath, |
|
3065 mDeviceAddress)); |
|
3066 const char* cstrDeviceObjectPath = deviceObjectPath.get(); |
|
3067 |
|
3068 bool success = sDBusConnection->SendWithReply( |
|
3069 OnRemoveDeviceReply, static_cast<void*>(mRunnable.get()), -1, |
|
3070 BLUEZ_DBUS_BASE_IFC, |
|
3071 NS_ConvertUTF16toUTF8(sAdapterPath).get(), |
|
3072 DBUS_ADAPTER_IFACE, "RemoveDevice", |
|
3073 DBUS_TYPE_OBJECT_PATH, &cstrDeviceObjectPath, |
|
3074 DBUS_TYPE_INVALID); |
|
3075 NS_ENSURE_TRUE_VOID(success); |
|
3076 |
|
3077 unused << mRunnable.forget(); // picked up by callback handler |
|
3078 } |
|
3079 |
|
3080 protected: |
|
3081 static void OnRemoveDeviceReply(DBusMessage* aReply, void* aData) |
|
3082 { |
|
3083 nsAutoString errorStr; |
|
3084 |
|
3085 if (!aReply) { |
|
3086 errorStr.AssignLiteral("RemoveDevice failed"); |
|
3087 } |
|
3088 |
|
3089 nsRefPtr<BluetoothReplyRunnable> runnable = |
|
3090 dont_AddRef<BluetoothReplyRunnable>( |
|
3091 static_cast<BluetoothReplyRunnable*>(aData)); |
|
3092 |
|
3093 DispatchBluetoothReply(runnable.get(), BluetoothValue(true), errorStr); |
|
3094 } |
|
3095 |
|
3096 private: |
|
3097 const nsString mDeviceAddress; |
|
3098 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3099 }; |
|
3100 |
|
3101 nsresult |
|
3102 BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress, |
|
3103 BluetoothReplyRunnable* aRunnable) |
|
3104 { |
|
3105 MOZ_ASSERT(NS_IsMainThread()); |
|
3106 |
|
3107 if (!IsReady()) { |
|
3108 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
3109 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
3110 return NS_OK; |
|
3111 } |
|
3112 |
|
3113 Task* task = new RemoveDeviceTask(aDeviceAddress, aRunnable); |
|
3114 DispatchToDBusThread(task); |
|
3115 |
|
3116 return NS_OK; |
|
3117 } |
|
3118 |
|
3119 class SetPinCodeTask : public Task |
|
3120 { |
|
3121 public: |
|
3122 SetPinCodeTask(const nsAString& aDeviceAddress, |
|
3123 const nsACString& aPinCode, |
|
3124 BluetoothReplyRunnable* aRunnable) |
|
3125 : mDeviceAddress(aDeviceAddress) |
|
3126 , mPinCode(aPinCode) |
|
3127 , mRunnable(aRunnable) |
|
3128 { |
|
3129 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3130 MOZ_ASSERT(mRunnable); |
|
3131 } |
|
3132 |
|
3133 void Run() MOZ_OVERRIDE |
|
3134 { |
|
3135 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3136 |
|
3137 nsAutoString errorStr; |
|
3138 BluetoothValue v = true; |
|
3139 DBusMessage *msg; |
|
3140 if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { |
|
3141 BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); |
|
3142 errorStr.AssignLiteral("Couldn't get original request message."); |
|
3143 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3144 return; |
|
3145 } |
|
3146 |
|
3147 DBusMessage *reply = dbus_message_new_method_return(msg); |
|
3148 |
|
3149 if (!reply) { |
|
3150 BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); |
|
3151 dbus_message_unref(msg); |
|
3152 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
3153 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3154 return; |
|
3155 } |
|
3156 |
|
3157 const char* pinCode = mPinCode.get(); |
|
3158 |
|
3159 if (!dbus_message_append_args(reply, |
|
3160 DBUS_TYPE_STRING, &pinCode, |
|
3161 DBUS_TYPE_INVALID)) { |
|
3162 BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); |
|
3163 errorStr.AssignLiteral("Couldn't append arguments to dbus message."); |
|
3164 } else { |
|
3165 MOZ_ASSERT(sDBusConnection); |
|
3166 sDBusConnection->Send(reply); |
|
3167 } |
|
3168 |
|
3169 dbus_message_unref(msg); |
|
3170 dbus_message_unref(reply); |
|
3171 |
|
3172 sPairingReqTable->Remove(mDeviceAddress); |
|
3173 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3174 } |
|
3175 |
|
3176 private: |
|
3177 const nsString mDeviceAddress; |
|
3178 const nsCString mPinCode; |
|
3179 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3180 }; |
|
3181 |
|
3182 bool |
|
3183 BluetoothDBusService::SetPinCodeInternal(const nsAString& aDeviceAddress, |
|
3184 const nsAString& aPinCode, |
|
3185 BluetoothReplyRunnable* aRunnable) |
|
3186 { |
|
3187 Task* task = new SetPinCodeTask(aDeviceAddress, |
|
3188 NS_ConvertUTF16toUTF8(aPinCode), |
|
3189 aRunnable); |
|
3190 DispatchToDBusThread(task); |
|
3191 |
|
3192 return true; |
|
3193 } |
|
3194 |
|
3195 class SetPasskeyTask : public Task |
|
3196 { |
|
3197 public: |
|
3198 SetPasskeyTask(const nsAString& aDeviceAddress, |
|
3199 uint32_t aPasskey, |
|
3200 BluetoothReplyRunnable* aRunnable) |
|
3201 : mDeviceAddress(aDeviceAddress) |
|
3202 , mPasskey(aPasskey) |
|
3203 , mRunnable(aRunnable) |
|
3204 { |
|
3205 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3206 MOZ_ASSERT(mRunnable); |
|
3207 } |
|
3208 |
|
3209 void Run() MOZ_OVERRIDE |
|
3210 { |
|
3211 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3212 |
|
3213 nsAutoString errorStr; |
|
3214 BluetoothValue v = true; |
|
3215 DBusMessage *msg; |
|
3216 if (!sPairingReqTable->Get(mDeviceAddress, &msg)) { |
|
3217 BT_WARNING("%s: Couldn't get original request message.", __FUNCTION__); |
|
3218 errorStr.AssignLiteral("Couldn't get original request message."); |
|
3219 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3220 return; |
|
3221 } |
|
3222 |
|
3223 DBusMessage *reply = dbus_message_new_method_return(msg); |
|
3224 |
|
3225 if (!reply) { |
|
3226 BT_WARNING("%s: Memory can't be allocated for the message.", __FUNCTION__); |
|
3227 dbus_message_unref(msg); |
|
3228 errorStr.AssignLiteral("Memory can't be allocated for the message."); |
|
3229 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3230 return; |
|
3231 } |
|
3232 |
|
3233 uint32_t passkey = mPasskey; |
|
3234 |
|
3235 if (!dbus_message_append_args(reply, |
|
3236 DBUS_TYPE_UINT32, &passkey, |
|
3237 DBUS_TYPE_INVALID)) { |
|
3238 BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__); |
|
3239 errorStr.AssignLiteral("Couldn't append arguments to dbus message."); |
|
3240 } else { |
|
3241 MOZ_ASSERT(sDBusConnection); |
|
3242 sDBusConnection->Send(reply); |
|
3243 } |
|
3244 |
|
3245 dbus_message_unref(msg); |
|
3246 dbus_message_unref(reply); |
|
3247 |
|
3248 sPairingReqTable->Remove(mDeviceAddress); |
|
3249 DispatchBluetoothReply(mRunnable, v, errorStr); |
|
3250 } |
|
3251 |
|
3252 private: |
|
3253 nsString mDeviceAddress; |
|
3254 uint32_t mPasskey; |
|
3255 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3256 }; |
|
3257 |
|
3258 bool |
|
3259 BluetoothDBusService::SetPasskeyInternal(const nsAString& aDeviceAddress, |
|
3260 uint32_t aPasskey, |
|
3261 BluetoothReplyRunnable* aRunnable) |
|
3262 { |
|
3263 Task* task = new SetPasskeyTask(aDeviceAddress, |
|
3264 aPasskey, |
|
3265 aRunnable); |
|
3266 DispatchToDBusThread(task); |
|
3267 |
|
3268 return true; |
|
3269 } |
|
3270 |
|
3271 |
|
3272 bool |
|
3273 BluetoothDBusService::SetPairingConfirmationInternal( |
|
3274 const nsAString& aDeviceAddress, |
|
3275 bool aConfirm, |
|
3276 BluetoothReplyRunnable* aRunnable) |
|
3277 { |
|
3278 MOZ_ASSERT(NS_IsMainThread()); |
|
3279 |
|
3280 Task* task = new SetPairingConfirmationTask(aDeviceAddress, |
|
3281 aConfirm, |
|
3282 aRunnable); |
|
3283 DispatchToDBusThread(task); |
|
3284 |
|
3285 return true; |
|
3286 } |
|
3287 |
|
3288 static void |
|
3289 NextBluetoothProfileController() |
|
3290 { |
|
3291 MOZ_ASSERT(NS_IsMainThread()); |
|
3292 |
|
3293 // First, remove the task at the front which has been already done. |
|
3294 NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); |
|
3295 sControllerArray.RemoveElementAt(0); |
|
3296 |
|
3297 // Re-check if the task array is empty, if it's not, the next task will begin. |
|
3298 NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); |
|
3299 sControllerArray[0]->StartSession(); |
|
3300 } |
|
3301 |
|
3302 static void |
|
3303 ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress, |
|
3304 BluetoothReplyRunnable* aRunnable, |
|
3305 uint16_t aServiceUuid, uint32_t aCod = 0) |
|
3306 { |
|
3307 MOZ_ASSERT(NS_IsMainThread()); |
|
3308 MOZ_ASSERT(aRunnable); |
|
3309 |
|
3310 BluetoothProfileController* controller = |
|
3311 new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable, |
|
3312 NextBluetoothProfileController, |
|
3313 aServiceUuid, aCod); |
|
3314 sControllerArray.AppendElement(controller); |
|
3315 |
|
3316 /** |
|
3317 * If the request is the first element of the quene, start from here. Note |
|
3318 * that other request is pushed into the quene and is popped out after the |
|
3319 * first one is completed. See NextBluetoothProfileController() for details. |
|
3320 */ |
|
3321 if (sControllerArray.Length() == 1) { |
|
3322 sControllerArray[0]->StartSession(); |
|
3323 } |
|
3324 } |
|
3325 |
|
3326 void |
|
3327 BluetoothDBusService::Connect(const nsAString& aDeviceAddress, |
|
3328 uint32_t aCod, |
|
3329 uint16_t aServiceUuid, |
|
3330 BluetoothReplyRunnable* aRunnable) |
|
3331 { |
|
3332 ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod); |
|
3333 } |
|
3334 |
|
3335 void |
|
3336 BluetoothDBusService::Disconnect(const nsAString& aDeviceAddress, |
|
3337 uint16_t aServiceUuid, |
|
3338 BluetoothReplyRunnable* aRunnable) |
|
3339 { |
|
3340 ConnectDisconnect(false, aDeviceAddress, aRunnable, aServiceUuid); |
|
3341 } |
|
3342 |
|
3343 bool |
|
3344 BluetoothDBusService::IsConnected(const uint16_t aServiceUuid) |
|
3345 { |
|
3346 MOZ_ASSERT(NS_IsMainThread()); |
|
3347 |
|
3348 BluetoothProfileManagerBase* profile = |
|
3349 BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); |
|
3350 if (!profile) { |
|
3351 BT_WARNING(ERR_UNKNOWN_PROFILE); |
|
3352 return false; |
|
3353 } |
|
3354 |
|
3355 NS_ENSURE_TRUE(profile, false); |
|
3356 return profile->IsConnected(); |
|
3357 } |
|
3358 |
|
3359 #ifdef MOZ_B2G_RIL |
|
3360 void |
|
3361 BluetoothDBusService::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable) |
|
3362 { |
|
3363 MOZ_ASSERT(NS_IsMainThread()); |
|
3364 |
|
3365 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3366 hfp->AnswerWaitingCall(); |
|
3367 |
|
3368 DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); |
|
3369 } |
|
3370 |
|
3371 void |
|
3372 BluetoothDBusService::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable) |
|
3373 { |
|
3374 MOZ_ASSERT(NS_IsMainThread()); |
|
3375 |
|
3376 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3377 hfp->IgnoreWaitingCall(); |
|
3378 |
|
3379 DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); |
|
3380 } |
|
3381 |
|
3382 void |
|
3383 BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable) |
|
3384 { |
|
3385 MOZ_ASSERT(NS_IsMainThread()); |
|
3386 |
|
3387 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3388 hfp->ToggleCalls(); |
|
3389 |
|
3390 DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); |
|
3391 } |
|
3392 #endif // MOZ_B2G_RIL |
|
3393 |
|
3394 class OnUpdateSdpRecordsRunnable : public nsRunnable |
|
3395 { |
|
3396 public: |
|
3397 OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath, |
|
3398 BluetoothProfileManagerBase* aManager) |
|
3399 : mManager(aManager) |
|
3400 { |
|
3401 MOZ_ASSERT(!aObjectPath.IsEmpty()); |
|
3402 MOZ_ASSERT(aManager); |
|
3403 |
|
3404 mDeviceAddress = GetAddressFromObjectPath(aObjectPath); |
|
3405 } |
|
3406 |
|
3407 nsresult |
|
3408 Run() |
|
3409 { |
|
3410 MOZ_ASSERT(NS_IsMainThread()); |
|
3411 |
|
3412 mManager->OnUpdateSdpRecords(mDeviceAddress); |
|
3413 |
|
3414 return NS_OK; |
|
3415 } |
|
3416 |
|
3417 private: |
|
3418 nsString mDeviceAddress; |
|
3419 BluetoothProfileManagerBase* mManager; |
|
3420 }; |
|
3421 |
|
3422 class OnGetServiceChannelRunnable : public nsRunnable |
|
3423 { |
|
3424 public: |
|
3425 OnGetServiceChannelRunnable(const nsAString& aDeviceAddress, |
|
3426 const nsAString& aServiceUuid, |
|
3427 int aChannel, |
|
3428 BluetoothProfileManagerBase* aManager) |
|
3429 : mDeviceAddress(aDeviceAddress) |
|
3430 , mServiceUuid(aServiceUuid) |
|
3431 , mChannel(aChannel) |
|
3432 , mManager(aManager) |
|
3433 { |
|
3434 MOZ_ASSERT(!aDeviceAddress.IsEmpty()); |
|
3435 MOZ_ASSERT(!aServiceUuid.IsEmpty()); |
|
3436 MOZ_ASSERT(aManager); |
|
3437 } |
|
3438 |
|
3439 NS_IMETHOD Run() |
|
3440 { |
|
3441 MOZ_ASSERT(NS_IsMainThread()); |
|
3442 |
|
3443 mManager->OnGetServiceChannel(mDeviceAddress, mServiceUuid, mChannel); |
|
3444 |
|
3445 return NS_OK; |
|
3446 } |
|
3447 |
|
3448 private: |
|
3449 nsString mDeviceAddress; |
|
3450 nsString mServiceUuid; |
|
3451 int mChannel; |
|
3452 BluetoothProfileManagerBase* mManager; |
|
3453 }; |
|
3454 |
|
3455 class OnGetServiceChannelReplyHandler : public DBusReplyHandler |
|
3456 { |
|
3457 public: |
|
3458 OnGetServiceChannelReplyHandler(const nsAString& aDeviceAddress, |
|
3459 const nsAString& aServiceUUID, |
|
3460 BluetoothProfileManagerBase* aBluetoothProfileManager) |
|
3461 : mDeviceAddress(aDeviceAddress), |
|
3462 mServiceUUID(aServiceUUID), |
|
3463 mBluetoothProfileManager(aBluetoothProfileManager) |
|
3464 { |
|
3465 MOZ_ASSERT(mBluetoothProfileManager); |
|
3466 } |
|
3467 |
|
3468 void Handle(DBusMessage* aReply) |
|
3469 { |
|
3470 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3471 |
|
3472 // The default channel is an invalid value of -1. We |
|
3473 // update it if we have received a correct reply. Both |
|
3474 // cases, valid and invalid channel numbers, are handled |
|
3475 // in BluetoothProfileManagerBase::OnGetServiceChannel. |
|
3476 |
|
3477 int channel = -1; |
|
3478 |
|
3479 if (aReply && (dbus_message_get_type(aReply) != DBUS_MESSAGE_TYPE_ERROR)) { |
|
3480 channel = dbus_returns_int32(aReply); |
|
3481 } |
|
3482 |
|
3483 nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(mDeviceAddress, |
|
3484 mServiceUUID, |
|
3485 channel, |
|
3486 mBluetoothProfileManager); |
|
3487 nsresult rv = NS_DispatchToMainThread(r); |
|
3488 NS_ENSURE_SUCCESS_VOID(rv); |
|
3489 } |
|
3490 |
|
3491 private: |
|
3492 nsString mDeviceAddress; |
|
3493 nsString mServiceUUID; |
|
3494 BluetoothProfileManagerBase* mBluetoothProfileManager; |
|
3495 }; |
|
3496 |
|
3497 class GetServiceChannelTask : public Task |
|
3498 { |
|
3499 public: |
|
3500 GetServiceChannelTask(const nsAString& aDeviceAddress, |
|
3501 const nsAString& aServiceUUID, |
|
3502 BluetoothProfileManagerBase* aBluetoothProfileManager) |
|
3503 : mDeviceAddress(aDeviceAddress) |
|
3504 , mServiceUUID(aServiceUUID) |
|
3505 , mBluetoothProfileManager(aBluetoothProfileManager) |
|
3506 { |
|
3507 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3508 MOZ_ASSERT(mBluetoothProfileManager); |
|
3509 } |
|
3510 |
|
3511 void Run() MOZ_OVERRIDE |
|
3512 { |
|
3513 static const int sProtocolDescriptorList = 0x0004; |
|
3514 |
|
3515 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3516 MOZ_ASSERT(sDBusConnection); |
|
3517 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
3518 |
|
3519 nsString objectPath = |
|
3520 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); |
|
3521 |
|
3522 nsRefPtr<OnGetServiceChannelReplyHandler> handler = |
|
3523 new OnGetServiceChannelReplyHandler(mDeviceAddress, mServiceUUID, |
|
3524 mBluetoothProfileManager); |
|
3525 |
|
3526 nsCString serviceUUID = NS_ConvertUTF16toUTF8(mServiceUUID); |
|
3527 const char* cstrServiceUUID = serviceUUID.get(); |
|
3528 |
|
3529 bool success = sDBusConnection->SendWithReply( |
|
3530 OnGetServiceChannelReplyHandler::Callback, handler, -1, |
|
3531 BLUEZ_DBUS_BASE_IFC, |
|
3532 NS_ConvertUTF16toUTF8(objectPath).get(), |
|
3533 DBUS_DEVICE_IFACE, "GetServiceAttributeValue", |
|
3534 DBUS_TYPE_STRING, &cstrServiceUUID, |
|
3535 DBUS_TYPE_UINT16, &sProtocolDescriptorList, |
|
3536 DBUS_TYPE_INVALID); |
|
3537 NS_ENSURE_TRUE_VOID(success); |
|
3538 |
|
3539 unused << handler.forget(); // picked up by callback handler |
|
3540 } |
|
3541 |
|
3542 private: |
|
3543 nsString mDeviceAddress; |
|
3544 nsString mServiceUUID; |
|
3545 BluetoothProfileManagerBase* mBluetoothProfileManager; |
|
3546 }; |
|
3547 |
|
3548 nsresult |
|
3549 BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress, |
|
3550 const nsAString& aServiceUUID, |
|
3551 BluetoothProfileManagerBase* aManager) |
|
3552 { |
|
3553 MOZ_ASSERT(NS_IsMainThread()); |
|
3554 |
|
3555 if (!IsReady()) { |
|
3556 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
3557 return NS_OK; |
|
3558 } |
|
3559 |
|
3560 #ifdef MOZ_WIDGET_GONK |
|
3561 // GetServiceAttributeValue only exists in android's bluez dbus binding |
|
3562 // implementation |
|
3563 Task* task = new GetServiceChannelTask(aDeviceAddress, |
|
3564 aServiceUUID, |
|
3565 aManager); |
|
3566 DispatchToDBusThread(task); |
|
3567 #else |
|
3568 // FIXME/Bug 793977 qdot: Just set something for desktop, until we have a |
|
3569 // parser for the GetServiceAttributes xml block |
|
3570 // |
|
3571 // Even though we are on the main thread already, we need to dispatch a |
|
3572 // runnable here. OnGetServiceChannel needs mRunnable to be set, which |
|
3573 // happens after GetServiceChannel returns. |
|
3574 nsRefPtr<nsRunnable> r = new OnGetServiceChannelRunnable(aDeviceAddress, |
|
3575 aServiceUUID, |
|
3576 1, |
|
3577 aManager); |
|
3578 NS_DispatchToMainThread(r); |
|
3579 #endif |
|
3580 |
|
3581 return NS_OK; |
|
3582 } |
|
3583 |
|
3584 class UpdateSdpRecordsTask : public Task |
|
3585 { |
|
3586 public: |
|
3587 UpdateSdpRecordsTask(const nsAString& aDeviceAddress, |
|
3588 BluetoothProfileManagerBase* aBluetoothProfileManager) |
|
3589 : mDeviceAddress(aDeviceAddress) |
|
3590 , mBluetoothProfileManager(aBluetoothProfileManager) |
|
3591 { |
|
3592 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3593 MOZ_ASSERT(mBluetoothProfileManager); |
|
3594 } |
|
3595 |
|
3596 void Run() MOZ_OVERRIDE |
|
3597 { |
|
3598 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3599 MOZ_ASSERT(sDBusConnection); |
|
3600 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
3601 |
|
3602 const nsString objectPath = |
|
3603 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress); |
|
3604 |
|
3605 // I choose to use raw pointer here because this is going to be passed as an |
|
3606 // argument into SendWithReply() at once. |
|
3607 OnUpdateSdpRecordsRunnable* callbackRunnable = |
|
3608 new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager); |
|
3609 |
|
3610 sDBusConnection->SendWithReply(DiscoverServicesCallback, |
|
3611 (void*)callbackRunnable, -1, |
|
3612 BLUEZ_DBUS_BASE_IFC, |
|
3613 NS_ConvertUTF16toUTF8(objectPath).get(), |
|
3614 DBUS_DEVICE_IFACE, |
|
3615 "DiscoverServices", |
|
3616 DBUS_TYPE_STRING, &EmptyCString(), |
|
3617 DBUS_TYPE_INVALID); |
|
3618 } |
|
3619 |
|
3620 protected: |
|
3621 static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData) |
|
3622 { |
|
3623 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3624 |
|
3625 nsRefPtr<OnUpdateSdpRecordsRunnable> r( |
|
3626 static_cast<OnUpdateSdpRecordsRunnable*>(aData)); |
|
3627 NS_DispatchToMainThread(r); |
|
3628 } |
|
3629 |
|
3630 private: |
|
3631 const nsString mDeviceAddress; |
|
3632 BluetoothProfileManagerBase* mBluetoothProfileManager; |
|
3633 }; |
|
3634 |
|
3635 bool |
|
3636 BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress, |
|
3637 BluetoothProfileManagerBase* aManager) |
|
3638 { |
|
3639 MOZ_ASSERT(NS_IsMainThread()); |
|
3640 |
|
3641 Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager); |
|
3642 DispatchToDBusThread(task); |
|
3643 |
|
3644 return true; |
|
3645 } |
|
3646 |
|
3647 void |
|
3648 BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, |
|
3649 BlobParent* aBlobParent, |
|
3650 BlobChild* aBlobChild, |
|
3651 BluetoothReplyRunnable* aRunnable) |
|
3652 { |
|
3653 MOZ_ASSERT(NS_IsMainThread()); |
|
3654 |
|
3655 // Currently we only support one device sending one file at a time, |
|
3656 // so we don't need aDeviceAddress here because the target device |
|
3657 // has been determined when calling 'Connect()'. Nevertheless, keep |
|
3658 // it for future use. |
|
3659 BluetoothOppManager* opp = BluetoothOppManager::Get(); |
|
3660 nsAutoString errorStr; |
|
3661 if (!opp || !opp->SendFile(aDeviceAddress, aBlobParent)) { |
|
3662 errorStr.AssignLiteral("Calling SendFile() failed"); |
|
3663 } |
|
3664 |
|
3665 DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); |
|
3666 } |
|
3667 |
|
3668 void |
|
3669 BluetoothDBusService::SendFile(const nsAString& aDeviceAddress, |
|
3670 nsIDOMBlob* aBlob, |
|
3671 BluetoothReplyRunnable* aRunnable) |
|
3672 { |
|
3673 MOZ_ASSERT(NS_IsMainThread()); |
|
3674 |
|
3675 // Currently we only support one device sending one file at a time, |
|
3676 // so we don't need aDeviceAddress here because the target device |
|
3677 // has been determined when calling 'Connect()'. Nevertheless, keep |
|
3678 // it for future use. |
|
3679 BluetoothOppManager* opp = BluetoothOppManager::Get(); |
|
3680 nsAutoString errorStr; |
|
3681 if (!opp || !opp->SendFile(aDeviceAddress, aBlob)) { |
|
3682 errorStr.AssignLiteral("Calling SendFile() failed"); |
|
3683 } |
|
3684 |
|
3685 DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); |
|
3686 } |
|
3687 |
|
3688 void |
|
3689 BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress, |
|
3690 BluetoothReplyRunnable* aRunnable) |
|
3691 { |
|
3692 MOZ_ASSERT(NS_IsMainThread()); |
|
3693 |
|
3694 // Currently we only support one device sending one file at a time, |
|
3695 // so we don't need aDeviceAddress here because the target device |
|
3696 // has been determined when calling 'Connect()'. Nevertheless, keep |
|
3697 // it for future use. |
|
3698 BluetoothOppManager* opp = BluetoothOppManager::Get(); |
|
3699 nsAutoString errorStr; |
|
3700 if (!opp || !opp->StopSendingFile()) { |
|
3701 errorStr.AssignLiteral("Calling StopSendingFile() failed"); |
|
3702 } |
|
3703 |
|
3704 DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); |
|
3705 } |
|
3706 |
|
3707 void |
|
3708 BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress, |
|
3709 bool aConfirm, |
|
3710 BluetoothReplyRunnable* aRunnable) |
|
3711 { |
|
3712 MOZ_ASSERT(NS_IsMainThread(), "Must be called from main thread!"); |
|
3713 |
|
3714 // Currently we only support one device sending one file at a time, |
|
3715 // so we don't need aDeviceAddress here because the target device |
|
3716 // has been determined when calling 'Connect()'. Nevertheless, keep |
|
3717 // it for future use. |
|
3718 BluetoothOppManager* opp = BluetoothOppManager::Get(); |
|
3719 nsAutoString errorStr; |
|
3720 if (!opp || !opp->ConfirmReceivingFile(aConfirm)) { |
|
3721 errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed"); |
|
3722 } |
|
3723 |
|
3724 DispatchBluetoothReply(aRunnable, BluetoothValue(true), errorStr); |
|
3725 } |
|
3726 |
|
3727 void |
|
3728 BluetoothDBusService::ConnectSco(BluetoothReplyRunnable* aRunnable) |
|
3729 { |
|
3730 MOZ_ASSERT(NS_IsMainThread()); |
|
3731 |
|
3732 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3733 if (!hfp || !hfp->ConnectSco(aRunnable)) { |
|
3734 NS_NAMED_LITERAL_STRING(replyError, "Calling ConnectSco() failed"); |
|
3735 DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); |
|
3736 } |
|
3737 } |
|
3738 |
|
3739 void |
|
3740 BluetoothDBusService::DisconnectSco(BluetoothReplyRunnable* aRunnable) |
|
3741 { |
|
3742 MOZ_ASSERT(NS_IsMainThread()); |
|
3743 |
|
3744 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3745 if (!hfp || !hfp->DisconnectSco()) { |
|
3746 NS_NAMED_LITERAL_STRING(replyError, "Calling DisconnectSco() failed"); |
|
3747 DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); |
|
3748 return; |
|
3749 } |
|
3750 |
|
3751 DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); |
|
3752 } |
|
3753 |
|
3754 void |
|
3755 BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable) |
|
3756 { |
|
3757 MOZ_ASSERT(NS_IsMainThread()); |
|
3758 |
|
3759 BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); |
|
3760 if (!hfp) { |
|
3761 NS_NAMED_LITERAL_STRING(replyError, "Fail to get BluetoothHfpManager"); |
|
3762 DispatchBluetoothReply(aRunnable, BluetoothValue(), replyError); |
|
3763 return; |
|
3764 } |
|
3765 |
|
3766 DispatchBluetoothReply(aRunnable, hfp->IsScoConnected(), EmptyString()); |
|
3767 } |
|
3768 |
|
3769 class SendMetadataTask : public Task |
|
3770 { |
|
3771 public: |
|
3772 SendMetadataTask(const nsAString& aDeviceAddress, |
|
3773 const nsACString& aTitle, |
|
3774 const nsACString& aArtist, |
|
3775 const nsACString& aAlbum, |
|
3776 int64_t aMediaNumber, |
|
3777 int64_t aTotalMediaCount, |
|
3778 int64_t aDuration, |
|
3779 BluetoothReplyRunnable* aRunnable) |
|
3780 : mDeviceAddress(aDeviceAddress) |
|
3781 , mTitle(aTitle) |
|
3782 , mArtist(aArtist) |
|
3783 , mAlbum(aAlbum) |
|
3784 , mMediaNumber(aMediaNumber) |
|
3785 , mTotalMediaCount(aTotalMediaCount) |
|
3786 , mDuration(aDuration) |
|
3787 , mRunnable(aRunnable) |
|
3788 { |
|
3789 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3790 MOZ_ASSERT(mRunnable); |
|
3791 } |
|
3792 |
|
3793 void Run() MOZ_OVERRIDE |
|
3794 { |
|
3795 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3796 MOZ_ASSERT(sDBusConnection); |
|
3797 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
3798 |
|
3799 // We currently don't support genre field in music player. |
|
3800 // In order to send media metadata through AVRCP, we set genre to an empty |
|
3801 // string to match the BlueZ method "UpdateMetaData" with signature "sssssss", |
|
3802 // which takes genre field as the last parameter. |
|
3803 nsCString tempGenre = EmptyCString(); |
|
3804 nsCString tempMediaNumber = EmptyCString(); |
|
3805 nsCString tempTotalMediaCount = EmptyCString(); |
|
3806 nsCString tempDuration = EmptyCString(); |
|
3807 |
|
3808 if (mMediaNumber >= 0) { |
|
3809 tempMediaNumber.AppendInt(mMediaNumber); |
|
3810 } |
|
3811 if (mTotalMediaCount >= 0) { |
|
3812 tempTotalMediaCount.AppendInt(mTotalMediaCount); |
|
3813 } |
|
3814 if (mDuration >= 0) { |
|
3815 tempDuration.AppendInt(mDuration); |
|
3816 } |
|
3817 |
|
3818 const nsCString objectPath = NS_ConvertUTF16toUTF8( |
|
3819 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); |
|
3820 |
|
3821 const char* title = mTitle.get(); |
|
3822 const char* album = mAlbum.get(); |
|
3823 const char* artist = mArtist.get(); |
|
3824 const char* mediaNumber = tempMediaNumber.get(); |
|
3825 const char* totalMediaCount = tempTotalMediaCount.get(); |
|
3826 const char* duration = tempDuration.get(); |
|
3827 const char* genre = tempGenre.get(); |
|
3828 |
|
3829 bool success = sDBusConnection->SendWithReply( |
|
3830 GetVoidCallback, static_cast<void*>(mRunnable.get()), -1, |
|
3831 BLUEZ_DBUS_BASE_IFC, |
|
3832 objectPath.get(), |
|
3833 DBUS_CTL_IFACE, "UpdateMetaData", |
|
3834 DBUS_TYPE_STRING, &title, |
|
3835 DBUS_TYPE_STRING, &artist, |
|
3836 DBUS_TYPE_STRING, &album, |
|
3837 DBUS_TYPE_STRING, &mediaNumber, |
|
3838 DBUS_TYPE_STRING, &totalMediaCount, |
|
3839 DBUS_TYPE_STRING, &duration, |
|
3840 DBUS_TYPE_STRING, &genre, |
|
3841 DBUS_TYPE_INVALID); |
|
3842 NS_ENSURE_TRUE_VOID(success); |
|
3843 |
|
3844 unused << mRunnable.forget(); // picked up by callback handler |
|
3845 } |
|
3846 |
|
3847 private: |
|
3848 const nsString mDeviceAddress; |
|
3849 const nsCString mTitle; |
|
3850 const nsCString mArtist; |
|
3851 const nsCString mAlbum; |
|
3852 int64_t mMediaNumber; |
|
3853 int64_t mTotalMediaCount; |
|
3854 int64_t mDuration; |
|
3855 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3856 }; |
|
3857 |
|
3858 void |
|
3859 BluetoothDBusService::SendMetaData(const nsAString& aTitle, |
|
3860 const nsAString& aArtist, |
|
3861 const nsAString& aAlbum, |
|
3862 int64_t aMediaNumber, |
|
3863 int64_t aTotalMediaCount, |
|
3864 int64_t aDuration, |
|
3865 BluetoothReplyRunnable* aRunnable) |
|
3866 { |
|
3867 MOZ_ASSERT(NS_IsMainThread()); |
|
3868 |
|
3869 if (!IsReady()) { |
|
3870 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
3871 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
3872 return; |
|
3873 } |
|
3874 |
|
3875 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
3876 NS_ENSURE_TRUE_VOID(a2dp); |
|
3877 |
|
3878 if (!a2dp->IsConnected()) { |
|
3879 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
3880 NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); |
|
3881 return; |
|
3882 } else if (!a2dp->IsAvrcpConnected()) { |
|
3883 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
3884 NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); |
|
3885 return; |
|
3886 } |
|
3887 |
|
3888 nsAutoString prevTitle, prevAlbum; |
|
3889 a2dp->GetTitle(prevTitle); |
|
3890 a2dp->GetAlbum(prevAlbum); |
|
3891 |
|
3892 if (aMediaNumber != a2dp->GetMediaNumber() || |
|
3893 !aTitle.Equals(prevTitle) || |
|
3894 !aAlbum.Equals(prevAlbum)) { |
|
3895 UpdateNotification(ControlEventId::EVENT_TRACK_CHANGED, aMediaNumber); |
|
3896 } |
|
3897 |
|
3898 nsAutoString deviceAddress; |
|
3899 a2dp->GetAddress(deviceAddress); |
|
3900 |
|
3901 Task* task = new SendMetadataTask( |
|
3902 deviceAddress, |
|
3903 NS_ConvertUTF16toUTF8(aTitle), |
|
3904 NS_ConvertUTF16toUTF8(aArtist), |
|
3905 NS_ConvertUTF16toUTF8(aAlbum), |
|
3906 aMediaNumber, |
|
3907 aTotalMediaCount, |
|
3908 aDuration, |
|
3909 aRunnable); |
|
3910 DispatchToDBusThread(task); |
|
3911 |
|
3912 a2dp->UpdateMetaData(aTitle, aArtist, aAlbum, |
|
3913 aMediaNumber, aTotalMediaCount, aDuration); |
|
3914 } |
|
3915 |
|
3916 static ControlPlayStatus |
|
3917 PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus) |
|
3918 { |
|
3919 ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN; |
|
3920 if (aPlayStatus.EqualsLiteral("STOPPED")) { |
|
3921 playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED; |
|
3922 } else if (aPlayStatus.EqualsLiteral("PLAYING")) { |
|
3923 playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING; |
|
3924 } else if (aPlayStatus.EqualsLiteral("PAUSED")) { |
|
3925 playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED; |
|
3926 } else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) { |
|
3927 playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK; |
|
3928 } else if (aPlayStatus.EqualsLiteral("REV_SEEK")) { |
|
3929 playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK; |
|
3930 } else if (aPlayStatus.EqualsLiteral("ERROR")) { |
|
3931 playStatus = ControlPlayStatus::PLAYSTATUS_ERROR; |
|
3932 } |
|
3933 |
|
3934 return playStatus; |
|
3935 } |
|
3936 |
|
3937 class SendPlayStatusTask : public Task |
|
3938 { |
|
3939 public: |
|
3940 SendPlayStatusTask(const nsAString& aDeviceAddress, |
|
3941 int64_t aDuration, |
|
3942 int64_t aPosition, |
|
3943 ControlPlayStatus aPlayStatus, |
|
3944 BluetoothReplyRunnable* aRunnable) |
|
3945 : mDeviceAddress(aDeviceAddress) |
|
3946 , mDuration(aDuration) |
|
3947 , mPosition(aPosition) |
|
3948 , mPlayStatus(aPlayStatus) |
|
3949 , mRunnable(aRunnable) |
|
3950 { |
|
3951 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
3952 MOZ_ASSERT(mRunnable); |
|
3953 } |
|
3954 |
|
3955 void Run() MOZ_OVERRIDE |
|
3956 { |
|
3957 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
3958 MOZ_ASSERT(sDBusConnection); |
|
3959 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
3960 |
|
3961 const nsCString objectPath = NS_ConvertUTF16toUTF8( |
|
3962 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); |
|
3963 |
|
3964 uint32_t tempPlayStatus = mPlayStatus; |
|
3965 |
|
3966 bool success = sDBusConnection->SendWithReply( |
|
3967 GetVoidCallback, static_cast<void*>(mRunnable.get()), -1, |
|
3968 BLUEZ_DBUS_BASE_IFC, |
|
3969 objectPath.get(), |
|
3970 DBUS_CTL_IFACE, "UpdatePlayStatus", |
|
3971 DBUS_TYPE_UINT32, &mDuration, |
|
3972 DBUS_TYPE_UINT32, &mPosition, |
|
3973 DBUS_TYPE_UINT32, &tempPlayStatus, |
|
3974 DBUS_TYPE_INVALID); |
|
3975 NS_ENSURE_TRUE_VOID(success); |
|
3976 |
|
3977 unused << mRunnable.forget(); // picked up by callback handler |
|
3978 } |
|
3979 |
|
3980 private: |
|
3981 const nsString mDeviceAddress; |
|
3982 int64_t mDuration; |
|
3983 int64_t mPosition; |
|
3984 ControlPlayStatus mPlayStatus; |
|
3985 nsRefPtr<BluetoothReplyRunnable> mRunnable; |
|
3986 }; |
|
3987 |
|
3988 void |
|
3989 BluetoothDBusService::SendPlayStatus(int64_t aDuration, |
|
3990 int64_t aPosition, |
|
3991 const nsAString& aPlayStatus, |
|
3992 BluetoothReplyRunnable* aRunnable) |
|
3993 { |
|
3994 MOZ_ASSERT(NS_IsMainThread()); |
|
3995 |
|
3996 if (!IsReady()) { |
|
3997 NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); |
|
3998 DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); |
|
3999 return; |
|
4000 } |
|
4001 |
|
4002 ControlPlayStatus playStatus = |
|
4003 PlayStatusStringToControlPlayStatus(aPlayStatus); |
|
4004 if (playStatus == ControlPlayStatus::PLAYSTATUS_UNKNOWN) { |
|
4005 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
4006 NS_LITERAL_STRING("Invalid play status")); |
|
4007 return; |
|
4008 } else if (aDuration < 0) { |
|
4009 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
4010 NS_LITERAL_STRING("Invalid duration")); |
|
4011 return; |
|
4012 } else if (aPosition < 0) { |
|
4013 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
4014 NS_LITERAL_STRING("Invalid position")); |
|
4015 return; |
|
4016 } |
|
4017 |
|
4018 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
4019 NS_ENSURE_TRUE_VOID(a2dp); |
|
4020 |
|
4021 if (!a2dp->IsConnected()) { |
|
4022 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
4023 NS_LITERAL_STRING(ERR_A2DP_IS_DISCONNECTED)); |
|
4024 return; |
|
4025 } else if (!a2dp->IsAvrcpConnected()) { |
|
4026 DispatchBluetoothReply(aRunnable, BluetoothValue(), |
|
4027 NS_LITERAL_STRING(ERR_AVRCP_IS_DISCONNECTED)); |
|
4028 return; |
|
4029 } |
|
4030 |
|
4031 if (playStatus != a2dp->GetPlayStatus()) { |
|
4032 UpdateNotification(ControlEventId::EVENT_PLAYBACK_STATUS_CHANGED, |
|
4033 playStatus); |
|
4034 } else if (aPosition != a2dp->GetPosition()) { |
|
4035 UpdateNotification(ControlEventId::EVENT_PLAYBACK_POS_CHANGED, aPosition); |
|
4036 } |
|
4037 |
|
4038 nsAutoString deviceAddress; |
|
4039 a2dp->GetAddress(deviceAddress); |
|
4040 |
|
4041 Task* task = new SendPlayStatusTask(deviceAddress, |
|
4042 aDuration, |
|
4043 aPosition, |
|
4044 playStatus, |
|
4045 aRunnable); |
|
4046 DispatchToDBusThread(task); |
|
4047 |
|
4048 a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus); |
|
4049 } |
|
4050 |
|
4051 static void |
|
4052 ControlCallback(DBusMessage* aMsg, void* aParam) |
|
4053 { |
|
4054 NS_ENSURE_TRUE_VOID(aMsg); |
|
4055 |
|
4056 BluetoothValue v; |
|
4057 nsAutoString replyError; |
|
4058 UnpackVoidMessage(aMsg, nullptr, v, replyError); |
|
4059 if (!v.get_bool()) { |
|
4060 BT_WARNING(NS_ConvertUTF16toUTF8(replyError).get()); |
|
4061 } |
|
4062 } |
|
4063 |
|
4064 class UpdatePlayStatusTask : public Task |
|
4065 { |
|
4066 public: |
|
4067 UpdatePlayStatusTask(const nsAString& aDeviceAddress, |
|
4068 int32_t aDuration, |
|
4069 int32_t aPosition, |
|
4070 ControlPlayStatus aPlayStatus) |
|
4071 : mDeviceAddress(aDeviceAddress) |
|
4072 , mDuration(aDuration) |
|
4073 , mPosition(aPosition) |
|
4074 , mPlayStatus(aPlayStatus) |
|
4075 { |
|
4076 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
4077 } |
|
4078 |
|
4079 void Run() MOZ_OVERRIDE |
|
4080 { |
|
4081 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
4082 MOZ_ASSERT(sDBusConnection); |
|
4083 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
4084 |
|
4085 const nsCString objectPath = NS_ConvertUTF16toUTF8( |
|
4086 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); |
|
4087 |
|
4088 uint32_t tempPlayStatus = mPlayStatus; |
|
4089 |
|
4090 bool success = sDBusConnection->SendWithReply( |
|
4091 ControlCallback, nullptr, -1, |
|
4092 BLUEZ_DBUS_BASE_IFC, |
|
4093 objectPath.get(), |
|
4094 DBUS_CTL_IFACE, "UpdatePlayStatus", |
|
4095 DBUS_TYPE_UINT32, &mDuration, |
|
4096 DBUS_TYPE_UINT32, &mPosition, |
|
4097 DBUS_TYPE_UINT32, &tempPlayStatus, |
|
4098 DBUS_TYPE_INVALID); |
|
4099 NS_ENSURE_TRUE_VOID(success); |
|
4100 } |
|
4101 |
|
4102 private: |
|
4103 const nsString mDeviceAddress; |
|
4104 int32_t mDuration; |
|
4105 int32_t mPosition; |
|
4106 ControlPlayStatus mPlayStatus; |
|
4107 }; |
|
4108 |
|
4109 void |
|
4110 BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration, |
|
4111 uint32_t aPosition, |
|
4112 ControlPlayStatus aPlayStatus) |
|
4113 { |
|
4114 MOZ_ASSERT(NS_IsMainThread()); |
|
4115 NS_ENSURE_TRUE_VOID(this->IsReady()); |
|
4116 |
|
4117 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
4118 NS_ENSURE_TRUE_VOID(a2dp); |
|
4119 MOZ_ASSERT(a2dp->IsConnected()); |
|
4120 MOZ_ASSERT(a2dp->IsAvrcpConnected()); |
|
4121 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
4122 |
|
4123 nsAutoString deviceAddress; |
|
4124 a2dp->GetAddress(deviceAddress); |
|
4125 |
|
4126 Task* task = new UpdatePlayStatusTask(deviceAddress, |
|
4127 aDuration, |
|
4128 aPosition, |
|
4129 aPlayStatus); |
|
4130 DispatchToDBusThread(task); |
|
4131 } |
|
4132 |
|
4133 class UpdateNotificationTask : public Task |
|
4134 { |
|
4135 public: |
|
4136 UpdateNotificationTask(const nsAString& aDeviceAddress, |
|
4137 BluetoothDBusService::ControlEventId aEventId, |
|
4138 uint64_t aData) |
|
4139 : mDeviceAddress(aDeviceAddress) |
|
4140 , mEventId(aEventId) |
|
4141 , mData(aData) |
|
4142 { |
|
4143 MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
|
4144 } |
|
4145 |
|
4146 void Run() MOZ_OVERRIDE |
|
4147 { |
|
4148 MOZ_ASSERT(!NS_IsMainThread()); // I/O thread |
|
4149 MOZ_ASSERT(sDBusConnection); |
|
4150 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
4151 |
|
4152 const nsCString objectPath = NS_ConvertUTF16toUTF8( |
|
4153 GetObjectPathFromAddress(sAdapterPath, mDeviceAddress)); |
|
4154 |
|
4155 uint16_t eventId = mEventId; |
|
4156 |
|
4157 bool success = sDBusConnection->SendWithReply( |
|
4158 ControlCallback, nullptr, -1, |
|
4159 BLUEZ_DBUS_BASE_IFC, |
|
4160 objectPath.get(), |
|
4161 DBUS_CTL_IFACE, "UpdateNotification", |
|
4162 DBUS_TYPE_UINT16, &eventId, |
|
4163 DBUS_TYPE_UINT64, &mData, |
|
4164 DBUS_TYPE_INVALID); |
|
4165 NS_ENSURE_TRUE_VOID(success); |
|
4166 } |
|
4167 |
|
4168 private: |
|
4169 const nsString mDeviceAddress; |
|
4170 int16_t mEventId; |
|
4171 int32_t mData; |
|
4172 }; |
|
4173 |
|
4174 void |
|
4175 BluetoothDBusService::UpdateNotification(ControlEventId aEventId, |
|
4176 uint64_t aData) |
|
4177 { |
|
4178 MOZ_ASSERT(NS_IsMainThread()); |
|
4179 NS_ENSURE_TRUE_VOID(this->IsReady()); |
|
4180 |
|
4181 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); |
|
4182 NS_ENSURE_TRUE_VOID(a2dp); |
|
4183 MOZ_ASSERT(a2dp->IsConnected()); |
|
4184 MOZ_ASSERT(a2dp->IsAvrcpConnected()); |
|
4185 MOZ_ASSERT(!sAdapterPath.IsEmpty()); |
|
4186 |
|
4187 nsAutoString deviceAddress; |
|
4188 a2dp->GetAddress(deviceAddress); |
|
4189 |
|
4190 Task* task = new UpdateNotificationTask(deviceAddress, aEventId, aData); |
|
4191 DispatchToDBusThread(task); |
|
4192 } |