dom/bluetooth/BluetoothProfileController.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "BluetoothProfileController.h"
michael@0 8 #include "BluetoothReplyRunnable.h"
michael@0 9
michael@0 10 #include "BluetoothA2dpManager.h"
michael@0 11 #include "BluetoothHfpManager.h"
michael@0 12 #include "BluetoothHidManager.h"
michael@0 13 #include "BluetoothUtils.h"
michael@0 14
michael@0 15 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
michael@0 16 #include "nsComponentManagerUtils.h"
michael@0 17
michael@0 18 USING_BLUETOOTH_NAMESPACE
michael@0 19
michael@0 20 #define BT_LOGR_PROFILE(mgr, msg, ...) \
michael@0 21 do { \
michael@0 22 nsCString name; \
michael@0 23 mgr->GetName(name); \
michael@0 24 BT_LOGR("[%s] " msg, name.get(), ##__VA_ARGS__); \
michael@0 25 } while(0)
michael@0 26
michael@0 27 #define CONNECTION_TIMEOUT_MS 15000
michael@0 28
michael@0 29 class CheckProfileStatusCallback : public nsITimerCallback
michael@0 30 {
michael@0 31 public:
michael@0 32 NS_DECL_ISUPPORTS
michael@0 33 NS_DECL_NSITIMERCALLBACK
michael@0 34
michael@0 35 CheckProfileStatusCallback(BluetoothProfileController* aController)
michael@0 36 : mController(aController)
michael@0 37 {
michael@0 38 MOZ_ASSERT(aController);
michael@0 39 }
michael@0 40
michael@0 41 virtual ~CheckProfileStatusCallback()
michael@0 42 {
michael@0 43 mController = nullptr;
michael@0 44 }
michael@0 45
michael@0 46 private:
michael@0 47 nsRefPtr<BluetoothProfileController> mController;
michael@0 48 };
michael@0 49
michael@0 50 BluetoothProfileController::BluetoothProfileController(
michael@0 51 bool aConnect,
michael@0 52 const nsAString& aDeviceAddress,
michael@0 53 BluetoothReplyRunnable* aRunnable,
michael@0 54 BluetoothProfileControllerCallback aCallback,
michael@0 55 uint16_t aServiceUuid,
michael@0 56 uint32_t aCod)
michael@0 57 : mConnect(aConnect)
michael@0 58 , mDeviceAddress(aDeviceAddress)
michael@0 59 , mRunnable(aRunnable)
michael@0 60 , mCallback(aCallback)
michael@0 61 , mCurrentProfileFinished(false)
michael@0 62 , mSuccess(false)
michael@0 63 , mProfilesIndex(-1)
michael@0 64 {
michael@0 65 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
michael@0 66 MOZ_ASSERT(aRunnable);
michael@0 67 MOZ_ASSERT(aCallback);
michael@0 68
michael@0 69 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 70 MOZ_ASSERT(mTimer);
michael@0 71
michael@0 72 mCheckProfileStatusCallback = new CheckProfileStatusCallback(this);
michael@0 73 mProfiles.Clear();
michael@0 74
michael@0 75 /**
michael@0 76 * If the service uuid is not specified, either connect multiple profiles
michael@0 77 * based on Cod, or disconnect all connected profiles.
michael@0 78 */
michael@0 79 if (!aServiceUuid) {
michael@0 80 mTarget.cod = aCod;
michael@0 81 SetupProfiles(false);
michael@0 82 } else {
michael@0 83 BluetoothServiceClass serviceClass =
michael@0 84 BluetoothUuidHelper::GetBluetoothServiceClass(aServiceUuid);
michael@0 85 mTarget.service = serviceClass;
michael@0 86 SetupProfiles(true);
michael@0 87 }
michael@0 88 }
michael@0 89
michael@0 90 BluetoothProfileController::~BluetoothProfileController()
michael@0 91 {
michael@0 92 mProfiles.Clear();
michael@0 93 mRunnable = nullptr;
michael@0 94 mCallback = nullptr;
michael@0 95
michael@0 96 if (mTimer) {
michael@0 97 mTimer->Cancel();
michael@0 98 }
michael@0 99 }
michael@0 100
michael@0 101 void
michael@0 102 BluetoothProfileController::AddProfileWithServiceClass(
michael@0 103 BluetoothServiceClass aClass)
michael@0 104 {
michael@0 105 BluetoothProfileManagerBase* profile;
michael@0 106 switch (aClass) {
michael@0 107 case BluetoothServiceClass::HANDSFREE:
michael@0 108 case BluetoothServiceClass::HEADSET:
michael@0 109 profile = BluetoothHfpManager::Get();
michael@0 110 break;
michael@0 111 case BluetoothServiceClass::A2DP:
michael@0 112 profile = BluetoothA2dpManager::Get();
michael@0 113 break;
michael@0 114 case BluetoothServiceClass::HID:
michael@0 115 profile = BluetoothHidManager::Get();
michael@0 116 break;
michael@0 117 default:
michael@0 118 DispatchBluetoothReply(mRunnable, BluetoothValue(),
michael@0 119 NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
michael@0 120 mCallback();
michael@0 121 return;
michael@0 122 }
michael@0 123
michael@0 124 AddProfile(profile);
michael@0 125 }
michael@0 126
michael@0 127 void
michael@0 128 BluetoothProfileController::AddProfile(BluetoothProfileManagerBase* aProfile,
michael@0 129 bool aCheckConnected)
michael@0 130 {
michael@0 131 if (!aProfile) {
michael@0 132 DispatchBluetoothReply(mRunnable, BluetoothValue(),
michael@0 133 NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
michael@0 134 mCallback();
michael@0 135 return;
michael@0 136 }
michael@0 137
michael@0 138 if (aCheckConnected && !aProfile->IsConnected()) {
michael@0 139 BT_WARNING("The profile is not connected.");
michael@0 140 return;
michael@0 141 }
michael@0 142
michael@0 143 mProfiles.AppendElement(aProfile);
michael@0 144 }
michael@0 145
michael@0 146 void
michael@0 147 BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
michael@0 148 {
michael@0 149 MOZ_ASSERT(NS_IsMainThread());
michael@0 150
michael@0 151 /**
michael@0 152 * When a service class is assigned, only its corresponding profile is put
michael@0 153 * into array.
michael@0 154 */
michael@0 155 if (aAssignServiceClass) {
michael@0 156 AddProfileWithServiceClass(mTarget.service);
michael@0 157 return;
michael@0 158 }
michael@0 159
michael@0 160 // For a disconnect request, all connected profiles are put into array.
michael@0 161 if (!mConnect) {
michael@0 162 AddProfile(BluetoothHidManager::Get(), true);
michael@0 163 AddProfile(BluetoothA2dpManager::Get(), true);
michael@0 164 AddProfile(BluetoothHfpManager::Get(), true);
michael@0 165 return;
michael@0 166 }
michael@0 167
michael@0 168 /**
michael@0 169 * For a connect request, put multiple profiles into array and connect to
michael@0 170 * all of them sequencely.
michael@0 171 */
michael@0 172 bool hasAudio = HAS_AUDIO(mTarget.cod);
michael@0 173 bool hasRendering = HAS_RENDERING(mTarget.cod);
michael@0 174 bool isPeripheral = IS_PERIPHERAL(mTarget.cod);
michael@0 175 bool isRemoteControl = IS_REMOTE_CONTROL(mTarget.cod);
michael@0 176 bool isKeyboard = IS_KEYBOARD(mTarget.cod);
michael@0 177 bool isPointingDevice = IS_POINTING_DEVICE(mTarget.cod);
michael@0 178
michael@0 179 NS_ENSURE_TRUE_VOID(hasAudio || hasRendering || isPeripheral);
michael@0 180
michael@0 181 // Audio bit should be set if remote device supports HFP/HSP.
michael@0 182 if (hasAudio) {
michael@0 183 AddProfile(BluetoothHfpManager::Get());
michael@0 184 }
michael@0 185
michael@0 186 // Rendering bit should be set if remote device supports A2DP.
michael@0 187 // A device which supports AVRCP should claim that it's a peripheral and it's
michael@0 188 // a remote control.
michael@0 189 if (hasRendering || (isPeripheral && isRemoteControl)) {
michael@0 190 AddProfile(BluetoothA2dpManager::Get());
michael@0 191 }
michael@0 192
michael@0 193 // A device which supports HID should claim that it's a peripheral and it's
michael@0 194 // either a keyboard, a pointing device, or both.
michael@0 195 if (isPeripheral && (isKeyboard || isPointingDevice)) {
michael@0 196 AddProfile(BluetoothHidManager::Get());
michael@0 197 }
michael@0 198 }
michael@0 199
michael@0 200 NS_IMPL_ISUPPORTS(CheckProfileStatusCallback, nsITimerCallback)
michael@0 201
michael@0 202 NS_IMETHODIMP
michael@0 203 CheckProfileStatusCallback::Notify(nsITimer* aTimer)
michael@0 204 {
michael@0 205 MOZ_ASSERT(mController);
michael@0 206 // Continue on the next profile since we haven't got the callback after
michael@0 207 // timeout.
michael@0 208 mController->GiveupAndContinue();
michael@0 209
michael@0 210 return NS_OK;
michael@0 211 }
michael@0 212
michael@0 213 void
michael@0 214 BluetoothProfileController::StartSession()
michael@0 215 {
michael@0 216 MOZ_ASSERT(NS_IsMainThread());
michael@0 217 MOZ_ASSERT(!mDeviceAddress.IsEmpty());
michael@0 218 MOZ_ASSERT(mProfilesIndex == -1);
michael@0 219 MOZ_ASSERT(mTimer);
michael@0 220
michael@0 221 if (mProfiles.Length() < 1) {
michael@0 222 BT_LOGR("No queued profile.");
michael@0 223 EndSession();
michael@0 224 return;
michael@0 225 }
michael@0 226
michael@0 227 if (mTimer) {
michael@0 228 mTimer->InitWithCallback(mCheckProfileStatusCallback, CONNECTION_TIMEOUT_MS,
michael@0 229 nsITimer::TYPE_ONE_SHOT);
michael@0 230 }
michael@0 231
michael@0 232 BT_LOGR("%s", mConnect ? "connecting" : "disconnecting");
michael@0 233
michael@0 234 Next();
michael@0 235 }
michael@0 236
michael@0 237 void
michael@0 238 BluetoothProfileController::EndSession()
michael@0 239 {
michael@0 240 MOZ_ASSERT(mRunnable && mCallback);
michael@0 241
michael@0 242 BT_LOGR("mSuccess %d", mSuccess);
michael@0 243
michael@0 244 // The action has completed, so the DOM request should be replied then invoke
michael@0 245 // the callback.
michael@0 246 if (mSuccess) {
michael@0 247 DispatchBluetoothReply(mRunnable, BluetoothValue(true), EmptyString());
michael@0 248 } else if (mConnect) {
michael@0 249 DispatchBluetoothReply(mRunnable, BluetoothValue(true),
michael@0 250 NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
michael@0 251 } else {
michael@0 252 DispatchBluetoothReply(mRunnable, BluetoothValue(true),
michael@0 253 NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
michael@0 254 }
michael@0 255
michael@0 256 mCallback();
michael@0 257 }
michael@0 258
michael@0 259 void
michael@0 260 BluetoothProfileController::Next()
michael@0 261 {
michael@0 262 MOZ_ASSERT(NS_IsMainThread());
michael@0 263 MOZ_ASSERT(!mDeviceAddress.IsEmpty());
michael@0 264 MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
michael@0 265 MOZ_ASSERT(mTimer);
michael@0 266
michael@0 267 mCurrentProfileFinished = false;
michael@0 268
michael@0 269 if (++mProfilesIndex >= (int)mProfiles.Length()) {
michael@0 270 EndSession();
michael@0 271 return;
michael@0 272 }
michael@0 273
michael@0 274 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
michael@0 275
michael@0 276 if (mConnect) {
michael@0 277 mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
michael@0 278 } else {
michael@0 279 mProfiles[mProfilesIndex]->Disconnect(this);
michael@0 280 }
michael@0 281 }
michael@0 282
michael@0 283 void
michael@0 284 BluetoothProfileController::NotifyCompletion(const nsAString& aErrorStr)
michael@0 285 {
michael@0 286 MOZ_ASSERT(NS_IsMainThread());
michael@0 287 MOZ_ASSERT(mTimer);
michael@0 288 MOZ_ASSERT(mProfiles.Length() > 0);
michael@0 289
michael@0 290 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
michael@0 291 NS_ConvertUTF16toUTF8(aErrorStr).get());
michael@0 292
michael@0 293 mCurrentProfileFinished = true;
michael@0 294
michael@0 295 if (mTimer) {
michael@0 296 mTimer->Cancel();
michael@0 297 }
michael@0 298
michael@0 299 mSuccess |= aErrorStr.IsEmpty();
michael@0 300
michael@0 301 Next();
michael@0 302 }
michael@0 303
michael@0 304 void
michael@0 305 BluetoothProfileController::GiveupAndContinue()
michael@0 306 {
michael@0 307 MOZ_ASSERT(!mCurrentProfileFinished);
michael@0 308 MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
michael@0 309
michael@0 310 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ERR_OPERATION_TIMEOUT);
michael@0 311 mProfiles[mProfilesIndex]->Reset();
michael@0 312 Next();
michael@0 313 }
michael@0 314

mercurial