Bumping manifests a=b2g-bump
[gecko.git] / dom / bluetooth / BluetoothProfileController.cpp
blobc9be3fe184339c3a32b9c6680245a5bbec7fb8af
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BluetoothProfileController.h"
9 #include "BluetoothA2dpManager.h"
10 #include "BluetoothHfpManager.h"
11 #include "BluetoothHidManager.h"
12 #include "BluetoothReplyRunnable.h"
13 #include "BluetoothService.h"
14 #include "BluetoothUtils.h"
16 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
17 #include "nsComponentManagerUtils.h"
19 USING_BLUETOOTH_NAMESPACE
21 #define BT_LOGR_PROFILE(mgr, msg, ...) \
22 do { \
23 nsCString name; \
24 mgr->GetName(name); \
25 BT_LOGR("[%s] " msg, name.get(), ##__VA_ARGS__); \
26 } while(0)
28 #define CONNECTION_TIMEOUT_MS 15000
30 class CheckProfileStatusCallback : public nsITimerCallback
32 public:
33 NS_DECL_ISUPPORTS
34 NS_DECL_NSITIMERCALLBACK
36 CheckProfileStatusCallback(BluetoothProfileController* aController)
37 : mController(aController)
39 MOZ_ASSERT(aController);
42 virtual ~CheckProfileStatusCallback()
44 mController = nullptr;
47 private:
48 nsRefPtr<BluetoothProfileController> mController;
51 BluetoothProfileController::BluetoothProfileController(
52 bool aConnect,
53 const nsAString& aDeviceAddress,
54 BluetoothReplyRunnable* aRunnable,
55 BluetoothProfileControllerCallback aCallback,
56 uint16_t aServiceUuid,
57 uint32_t aCod)
58 : mConnect(aConnect)
59 , mDeviceAddress(aDeviceAddress)
60 , mRunnable(aRunnable)
61 , mCallback(aCallback)
62 , mCurrentProfileFinished(false)
63 , mSuccess(false)
64 , mProfilesIndex(-1)
66 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
67 MOZ_ASSERT(aRunnable);
68 MOZ_ASSERT(aCallback);
70 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
71 MOZ_ASSERT(mTimer);
73 mProfiles.Clear();
75 /**
76 * If the service uuid is not specified, either connect multiple profiles
77 * based on Cod, or disconnect all connected profiles.
79 if (!aServiceUuid) {
80 mTarget.cod = aCod;
81 SetupProfiles(false);
82 } else {
83 BluetoothServiceClass serviceClass =
84 BluetoothUuidHelper::GetBluetoothServiceClass(aServiceUuid);
85 mTarget.service = serviceClass;
86 SetupProfiles(true);
90 BluetoothProfileController::~BluetoothProfileController()
92 mProfiles.Clear();
93 mRunnable = nullptr;
94 mCallback = nullptr;
96 if (mTimer) {
97 mTimer->Cancel();
101 void
102 BluetoothProfileController::AddProfileWithServiceClass(
103 BluetoothServiceClass aClass)
105 BluetoothProfileManagerBase* profile;
106 switch (aClass) {
107 case BluetoothServiceClass::HANDSFREE:
108 case BluetoothServiceClass::HEADSET:
109 profile = BluetoothHfpManager::Get();
110 break;
111 case BluetoothServiceClass::A2DP:
112 profile = BluetoothA2dpManager::Get();
113 break;
114 case BluetoothServiceClass::HID:
115 profile = BluetoothHidManager::Get();
116 break;
117 default:
118 DispatchBluetoothReply(mRunnable, BluetoothValue(),
119 NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
120 mCallback();
121 return;
124 AddProfile(profile);
127 void
128 BluetoothProfileController::AddProfile(BluetoothProfileManagerBase* aProfile,
129 bool aCheckConnected)
131 if (!aProfile) {
132 DispatchBluetoothReply(mRunnable, BluetoothValue(),
133 NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
134 mCallback();
135 return;
138 if (aCheckConnected && !aProfile->IsConnected()) {
139 BT_WARNING("The profile is not connected.");
140 return;
143 mProfiles.AppendElement(aProfile);
146 void
147 BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
149 MOZ_ASSERT(NS_IsMainThread());
152 * When a service class is assigned, only its corresponding profile is put
153 * into array.
155 if (aAssignServiceClass) {
156 AddProfileWithServiceClass(mTarget.service);
157 return;
160 // For a disconnect request, all connected profiles are put into array.
161 if (!mConnect) {
162 AddProfile(BluetoothHidManager::Get(), true);
163 AddProfile(BluetoothA2dpManager::Get(), true);
164 AddProfile(BluetoothHfpManager::Get(), true);
165 return;
169 * For a connect request, put multiple profiles into array and connect to
170 * all of them sequencely.
172 bool hasAudio = HAS_AUDIO(mTarget.cod);
173 bool hasRendering = HAS_RENDERING(mTarget.cod);
174 bool isPeripheral = IS_PERIPHERAL(mTarget.cod);
175 bool isRemoteControl = IS_REMOTE_CONTROL(mTarget.cod);
176 bool isKeyboard = IS_KEYBOARD(mTarget.cod);
177 bool isPointingDevice = IS_POINTING_DEVICE(mTarget.cod);
179 NS_ENSURE_TRUE_VOID(hasAudio || hasRendering || isPeripheral);
181 // Audio bit should be set if remote device supports HFP/HSP.
182 if (hasAudio) {
183 AddProfile(BluetoothHfpManager::Get());
186 // Rendering bit should be set if remote device supports A2DP.
187 // A device which supports AVRCP should claim that it's a peripheral and it's
188 // a remote control.
189 if (hasRendering || (isPeripheral && isRemoteControl)) {
190 AddProfile(BluetoothA2dpManager::Get());
193 // A device which supports HID should claim that it's a peripheral and it's
194 // either a keyboard, a pointing device, or both.
195 if (isPeripheral && (isKeyboard || isPointingDevice)) {
196 AddProfile(BluetoothHidManager::Get());
200 NS_IMPL_ISUPPORTS(CheckProfileStatusCallback, nsITimerCallback)
202 NS_IMETHODIMP
203 CheckProfileStatusCallback::Notify(nsITimer* aTimer)
205 MOZ_ASSERT(mController);
206 // Continue on the next profile since we haven't got the callback after
207 // timeout.
208 mController->GiveupAndContinue();
210 return NS_OK;
213 void
214 BluetoothProfileController::StartSession()
216 MOZ_ASSERT(NS_IsMainThread());
217 MOZ_ASSERT(!mDeviceAddress.IsEmpty());
218 MOZ_ASSERT(mProfilesIndex == -1);
219 MOZ_ASSERT(mTimer);
221 if (!IsBtServiceAvailable()) {
222 EndSession();
223 return;
226 if (mProfiles.Length() < 1) {
227 BT_LOGR("No queued profile.");
228 EndSession();
229 return;
232 if (mTimer) {
233 mTimer->InitWithCallback(new CheckProfileStatusCallback(this),
234 CONNECTION_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
237 BT_LOGR("%s", mConnect ? "connecting" : "disconnecting");
239 Next();
242 void
243 BluetoothProfileController::EndSession()
245 MOZ_ASSERT(mRunnable && mCallback);
247 BT_LOGR("mSuccess %d", mSuccess);
249 // Don't have to check profile status and retrigger session after connection
250 // timeout, since session is end.
251 if (mTimer) {
252 mTimer->Cancel();
255 // The action has completed, so the DOM request should be replied then invoke
256 // the callback.
257 if (mSuccess) {
258 DispatchBluetoothReply(mRunnable, BluetoothValue(true), EmptyString());
259 } else if (mConnect) {
260 DispatchBluetoothReply(mRunnable, BluetoothValue(true),
261 NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
262 } else {
263 DispatchBluetoothReply(mRunnable, BluetoothValue(true),
264 NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
267 mCallback();
270 void
271 BluetoothProfileController::Next()
273 MOZ_ASSERT(NS_IsMainThread());
274 MOZ_ASSERT(!mDeviceAddress.IsEmpty());
275 MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
276 MOZ_ASSERT(mTimer);
278 mCurrentProfileFinished = false;
280 if (!IsBtServiceAvailable()) {
281 EndSession();
282 return;
285 if (++mProfilesIndex >= (int)mProfiles.Length()) {
286 EndSession();
287 return;
290 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
292 if (mConnect) {
293 mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
294 } else {
295 mProfiles[mProfilesIndex]->Disconnect(this);
299 bool
300 BluetoothProfileController::IsBtServiceAvailable() const
302 BluetoothService* bs = BluetoothService::Get();
303 return (bs && bs->IsEnabled() && !bs->IsToggling());
306 void
307 BluetoothProfileController::NotifyCompletion(const nsAString& aErrorStr)
309 MOZ_ASSERT(NS_IsMainThread());
310 MOZ_ASSERT(mTimer);
311 MOZ_ASSERT(mProfiles.Length() > 0);
313 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
314 NS_ConvertUTF16toUTF8(aErrorStr).get());
316 mCurrentProfileFinished = true;
318 if (mTimer) {
319 mTimer->Cancel();
322 mSuccess |= aErrorStr.IsEmpty();
324 Next();
327 void
328 BluetoothProfileController::GiveupAndContinue()
330 MOZ_ASSERT(!mCurrentProfileFinished);
331 MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
333 BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ERR_OPERATION_TIMEOUT);
334 mProfiles[mProfilesIndex]->Reset();
336 if (IsBtServiceAvailable()) {
337 Next();
338 } else {
339 EndSession();