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