dom/bluetooth/BluetoothProfileController.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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"
     8 #include "BluetoothReplyRunnable.h"
    10 #include "BluetoothA2dpManager.h"
    11 #include "BluetoothHfpManager.h"
    12 #include "BluetoothHidManager.h"
    13 #include "BluetoothUtils.h"
    15 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
    16 #include "nsComponentManagerUtils.h"
    18 USING_BLUETOOTH_NAMESPACE
    20 #define BT_LOGR_PROFILE(mgr, msg, ...)               \
    21   do {                                               \
    22     nsCString name;                                  \
    23     mgr->GetName(name);                              \
    24     BT_LOGR("[%s] " msg, name.get(), ##__VA_ARGS__); \
    25   } while(0)
    27 #define CONNECTION_TIMEOUT_MS 15000
    29 class CheckProfileStatusCallback : public nsITimerCallback
    30 {
    31 public:
    32   NS_DECL_ISUPPORTS
    33   NS_DECL_NSITIMERCALLBACK
    35   CheckProfileStatusCallback(BluetoothProfileController* aController)
    36     : mController(aController)
    37   {
    38     MOZ_ASSERT(aController);
    39   }
    41   virtual ~CheckProfileStatusCallback()
    42   {
    43     mController = nullptr;
    44   }
    46 private:
    47   nsRefPtr<BluetoothProfileController> mController;
    48 };
    50 BluetoothProfileController::BluetoothProfileController(
    51                                    bool aConnect,
    52                                    const nsAString& aDeviceAddress,
    53                                    BluetoothReplyRunnable* aRunnable,
    54                                    BluetoothProfileControllerCallback aCallback,
    55                                    uint16_t aServiceUuid,
    56                                    uint32_t aCod)
    57   : mConnect(aConnect)
    58   , mDeviceAddress(aDeviceAddress)
    59   , mRunnable(aRunnable)
    60   , mCallback(aCallback)
    61   , mCurrentProfileFinished(false)
    62   , mSuccess(false)
    63   , mProfilesIndex(-1)
    64 {
    65   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
    66   MOZ_ASSERT(aRunnable);
    67   MOZ_ASSERT(aCallback);
    69   mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    70   MOZ_ASSERT(mTimer);
    72   mCheckProfileStatusCallback = new CheckProfileStatusCallback(this);
    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.
    78    */
    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);
    87   }
    88 }
    90 BluetoothProfileController::~BluetoothProfileController()
    91 {
    92   mProfiles.Clear();
    93   mRunnable = nullptr;
    94   mCallback = nullptr;
    96   if (mTimer) {
    97     mTimer->Cancel();
    98   }
    99 }
   101 void
   102 BluetoothProfileController::AddProfileWithServiceClass(
   103                                                    BluetoothServiceClass aClass)
   104 {
   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;
   122   }
   124   AddProfile(profile);
   125 }
   127 void
   128 BluetoothProfileController::AddProfile(BluetoothProfileManagerBase* aProfile,
   129                                        bool aCheckConnected)
   130 {
   131   if (!aProfile) {
   132     DispatchBluetoothReply(mRunnable, BluetoothValue(),
   133                            NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
   134     mCallback();
   135     return;
   136   }
   138   if (aCheckConnected && !aProfile->IsConnected()) {
   139     BT_WARNING("The profile is not connected.");
   140     return;
   141   }
   143   mProfiles.AppendElement(aProfile);
   144 }
   146 void
   147 BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
   148 {
   149   MOZ_ASSERT(NS_IsMainThread());
   151   /**
   152    * When a service class is assigned, only its corresponding profile is put
   153    * into array.
   154    */
   155   if (aAssignServiceClass) {
   156     AddProfileWithServiceClass(mTarget.service);
   157     return;
   158   }
   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;
   166   }
   168   /**
   169    * For a connect request, put multiple profiles into array and connect to
   170    * all of them sequencely.
   171    */
   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());
   184   }
   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());
   191   }
   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());
   197   }
   198 }
   200 NS_IMPL_ISUPPORTS(CheckProfileStatusCallback, nsITimerCallback)
   202 NS_IMETHODIMP
   203 CheckProfileStatusCallback::Notify(nsITimer* aTimer)
   204 {
   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;
   211 }
   213 void
   214 BluetoothProfileController::StartSession()
   215 {
   216   MOZ_ASSERT(NS_IsMainThread());
   217   MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   218   MOZ_ASSERT(mProfilesIndex == -1);
   219   MOZ_ASSERT(mTimer);
   221   if (mProfiles.Length() < 1) {
   222     BT_LOGR("No queued profile.");
   223     EndSession();
   224     return;
   225   }
   227   if (mTimer) {
   228     mTimer->InitWithCallback(mCheckProfileStatusCallback, CONNECTION_TIMEOUT_MS,
   229                              nsITimer::TYPE_ONE_SHOT);
   230   }
   232   BT_LOGR("%s", mConnect ? "connecting" : "disconnecting");
   234   Next();
   235 }
   237 void
   238 BluetoothProfileController::EndSession()
   239 {
   240   MOZ_ASSERT(mRunnable && mCallback);
   242   BT_LOGR("mSuccess %d", mSuccess);
   244   // The action has completed, so the DOM request should be replied then invoke
   245   // the callback.
   246   if (mSuccess) {
   247     DispatchBluetoothReply(mRunnable, BluetoothValue(true), EmptyString());
   248   } else if (mConnect) {
   249     DispatchBluetoothReply(mRunnable, BluetoothValue(true),
   250                            NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
   251   } else {
   252     DispatchBluetoothReply(mRunnable, BluetoothValue(true),
   253                            NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
   254   }
   256   mCallback();
   257 }
   259 void
   260 BluetoothProfileController::Next()
   261 {
   262   MOZ_ASSERT(NS_IsMainThread());
   263   MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   264   MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
   265   MOZ_ASSERT(mTimer);
   267   mCurrentProfileFinished = false;
   269   if (++mProfilesIndex >= (int)mProfiles.Length()) {
   270     EndSession();
   271     return;
   272   }
   274   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
   276   if (mConnect) {
   277     mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
   278   } else {
   279     mProfiles[mProfilesIndex]->Disconnect(this);
   280   }
   281 }
   283 void
   284 BluetoothProfileController::NotifyCompletion(const nsAString& aErrorStr)
   285 {
   286   MOZ_ASSERT(NS_IsMainThread());
   287   MOZ_ASSERT(mTimer);
   288   MOZ_ASSERT(mProfiles.Length() > 0);
   290   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
   291                   NS_ConvertUTF16toUTF8(aErrorStr).get());
   293   mCurrentProfileFinished = true;
   295   if (mTimer) {
   296     mTimer->Cancel();
   297   }
   299   mSuccess |= aErrorStr.IsEmpty();
   301   Next();
   302 }
   304 void
   305 BluetoothProfileController::GiveupAndContinue()
   306 {
   307   MOZ_ASSERT(!mCurrentProfileFinished);
   308   MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
   310   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ERR_OPERATION_TIMEOUT);
   311   mProfiles[mProfilesIndex]->Reset();
   312   Next();
   313 }

mercurial