dom/bluetooth/bluez/BluetoothHfpManager.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 "base/basictypes.h"
     9 #include "BluetoothHfpManager.h"
    11 #include "BluetoothProfileController.h"
    12 #include "BluetoothReplyRunnable.h"
    13 #include "BluetoothService.h"
    14 #include "BluetoothSocket.h"
    15 #include "BluetoothUtils.h"
    16 #include "BluetoothUuid.h"
    18 #include "jsapi.h"
    19 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
    20 #include "mozilla/Services.h"
    21 #include "mozilla/StaticPtr.h"
    22 #include "nsContentUtils.h"
    23 #include "nsIObserverService.h"
    24 #include "nsISettingsService.h"
    25 #include "nsServiceManagerUtils.h"
    27 #ifdef MOZ_B2G_RIL
    28 #include "nsIDOMIccInfo.h"
    29 #include "nsIDOMMobileConnection.h"
    30 #include "nsIIccProvider.h"
    31 #include "nsIMobileConnectionProvider.h"
    32 #include "nsITelephonyProvider.h"
    33 #include "nsRadioInterfaceLayer.h"
    34 #endif
    36 /**
    37  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
    38  * Capabilities" in Bluetooth hands-free profile 1.6
    39  */
    40 #define BRSF_BIT_THREE_WAY_CALLING         1
    41 #define BSRF_BIT_EC_NR_FUNCTION            (1 << 1)
    42 #define BRSF_BIT_VOICE_RECOGNITION         (1 << 2)
    43 #define BRSF_BIT_IN_BAND_RING_TONE         (1 << 3)
    44 #define BRSF_BIT_ATTACH_NUM_TO_VOICE_TAG   (1 << 4)
    45 #define BRSF_BIT_ABILITY_TO_REJECT_CALL    (1 << 5)
    46 #define BRSF_BIT_ENHANCED_CALL_STATUS      (1 << 6)
    47 #define BRSF_BIT_ENHANCED_CALL_CONTROL     (1 << 7)
    48 #define BRSF_BIT_EXTENDED_ERR_RESULT_CODES (1 << 8)
    49 #define BRSF_BIT_CODEC_NEGOTIATION         (1 << 9)
    51 #ifdef MOZ_B2G_RIL
    52 /**
    53  * These constants are used in result code such as +CLIP and +CCWA. The value
    54  * of these constants is the same as TOA_INTERNATIONAL/TOA_UNKNOWN defined in
    55  * ril_consts.js
    56  */
    57 #define TOA_UNKNOWN 0x81
    58 #define TOA_INTERNATIONAL 0x91
    59 #endif
    61 #define CR_LF "\xd\xa";
    63 #define MOZSETTINGS_CHANGED_ID               "mozsettings-changed"
    64 #define AUDIO_VOLUME_BT_SCO_ID               "audio.volume.bt_sco"
    66 #define RESPONSE_CIEV      "+CIEV: "
    67 #define RESPONSE_CIND      "+CIND: "
    68 #define RESPONSE_CLCC      "+CLCC: "
    69 #define RESPONSE_BRSF      "+BRSF: "
    70 #define RESPONSE_VGS       "+VGS: "
    71 #define RESPONSE_CME_ERROR "+CME ERROR: "
    73 using namespace mozilla;
    74 using namespace mozilla::ipc;
    75 USING_BLUETOOTH_NAMESPACE
    77 namespace {
    78   StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
    79   bool sInShutdown = false;
    80   static const char kHfpCrlf[] = "\xd\xa";
    82 #ifdef MOZ_B2G_RIL
    83   // Sending ringtone related
    84   static bool sStopSendingRingFlag = true;
    85   static int sRingInterval = 3000; //unit: ms
    87   // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a
    88   // magic number. The mechanism should be revised once we can get call history.
    89   static int sWaitingForDialingInterval = 2000; //unit: ms
    91   // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the
    92   // time window set in Dialer and the extra '0.7' second is a magic number.
    93   // The mechanism should be revised once we know the exact time at which
    94   // Dialer stops playing.
    95   static int sBusyToneInterval = 3700; //unit: ms
    96 #endif // MOZ_B2G_RIL
    97 } // anonymous namespace
    99 #ifdef MOZ_B2G_RIL
   100 /* CallState for sCINDItems[CINDType::CALL].value
   101  * - NO_CALL: there are no calls in progress
   102  * - IN_PROGRESS: at least one call is in progress
   103  */
   104 enum CallState {
   105   NO_CALL,
   106   IN_PROGRESS
   107 };
   109 /* CallSetupState for sCINDItems[CINDType::CALLSETUP].value
   110  * - NO_CALLSETUP: not currently in call set up
   111  * - INCOMING: an incoming call process ongoing
   112  * - OUTGOING: an outgoing call set up is ongoing
   113  * - OUTGOING_ALERTING: remote party being alerted in an outgoing call
   114  */
   115 enum CallSetupState {
   116   NO_CALLSETUP,
   117   INCOMING,
   118   OUTGOING,
   119   OUTGOING_ALERTING
   120 };
   122 /* CallHeldState for sCINDItems[CINDType::CALLHELD].value
   123  * - NO_CALLHELD: no calls held
   124  * - ONHOLD_ACTIVE: both an active and a held call
   125  * - ONHOLD_NOACTIVE: call on hold, no active call
   126  */
   127 enum CallHeldState {
   128   NO_CALLHELD,
   129   ONHOLD_ACTIVE,
   130   ONHOLD_NOACTIVE
   131 };
   132 #endif // MOZ_B2G_RIL
   134 typedef struct {
   135   const char* name;
   136   const char* range;
   137   int value;
   138   bool activated;
   139 } CINDItem;
   141 enum CINDType {
   142   BATTCHG = 1,
   143 #ifdef MOZ_B2G_RIL
   144   CALL,
   145   CALLHELD,
   146   CALLSETUP,
   147   SERVICE,
   148   SIGNAL,
   149   ROAM
   150 #endif
   151 };
   153 static CINDItem sCINDItems[] = {
   154   {},
   155   {"battchg", "0-5", 5, true},
   156 #ifdef MOZ_B2G_RIL
   157   {"call", "0,1", CallState::NO_CALL, true},
   158   {"callheld", "0-2", CallHeldState::NO_CALLHELD, true},
   159   {"callsetup", "0-3", CallSetupState::NO_CALLSETUP, true},
   160   {"service", "0,1", 0, true},
   161   {"signal", "0-5", 0, true},
   162   {"roam", "0,1", 0, true}
   163 #endif
   164 };
   166 class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
   167 {
   168 public:
   169   NS_DECL_ISUPPORTS
   171   NS_IMETHOD
   172   Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   173   {
   174     MOZ_ASSERT(NS_IsMainThread());
   176     JSContext *cx = nsContentUtils::GetCurrentJSContext();
   177     NS_ENSURE_TRUE(cx, NS_OK);
   179     if (!aResult.isNumber()) {
   180       BT_WARNING("'" AUDIO_VOLUME_BT_SCO_ID "' is not a number!");
   181       return NS_OK;
   182     }
   184     BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   185     hfp->mCurrentVgs = aResult.toNumber();
   187     return NS_OK;
   188   }
   190   NS_IMETHOD
   191   HandleError(const nsAString& aName)
   192   {
   193     BT_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO_ID "'");
   194     return NS_OK;
   195   }
   196 };
   198 NS_IMPL_ISUPPORTS(BluetoothHfpManager::GetVolumeTask,
   199                   nsISettingsServiceCallback);
   201 NS_IMETHODIMP
   202 BluetoothHfpManager::Observe(nsISupports* aSubject,
   203                              const char* aTopic,
   204                              const char16_t* aData)
   205 {
   206   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
   207     HandleVolumeChanged(nsDependentString(aData));
   208   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   209     HandleShutdown();
   210   } else {
   211     MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
   212     return NS_ERROR_UNEXPECTED;
   213   }
   215   return NS_OK;
   216 }
   218 void
   219 BluetoothHfpManager::Notify(const hal::BatteryInformation& aBatteryInfo)
   220 {
   221   // Range of battery level: [0, 1], double
   222   // Range of CIND::BATTCHG: [0, 5], int
   223   int level = ceil(aBatteryInfo.level() * 5.0);
   224   if (level != sCINDItems[CINDType::BATTCHG].value) {
   225     sCINDItems[CINDType::BATTCHG].value = level;
   226     SendCommand(RESPONSE_CIEV, CINDType::BATTCHG);
   227   }
   228 }
   230 #ifdef MOZ_B2G_RIL
   231 class BluetoothHfpManager::RespondToBLDNTask : public Task
   232 {
   233 private:
   234   void Run() MOZ_OVERRIDE
   235   {
   236     MOZ_ASSERT(sBluetoothHfpManager);
   238     if (!sBluetoothHfpManager->mDialingRequestProcessed) {
   239       sBluetoothHfpManager->mDialingRequestProcessed = true;
   240       sBluetoothHfpManager->SendLine("ERROR");
   241     }
   242   }
   243 };
   245 class BluetoothHfpManager::SendRingIndicatorTask : public Task
   246 {
   247 public:
   248   SendRingIndicatorTask(const nsAString& aNumber, int aType)
   249     : mNumber(aNumber)
   250     , mType(aType)
   251   {
   252     MOZ_ASSERT(NS_IsMainThread());
   253   }
   255   void Run() MOZ_OVERRIDE
   256   {
   257     MOZ_ASSERT(NS_IsMainThread());
   259     // Stop sending RING indicator
   260     if (sStopSendingRingFlag) {
   261       return;
   262     }
   264     if (!sBluetoothHfpManager) {
   265       BT_WARNING("BluetoothHfpManager no longer exists, cannot send ring!");
   266       return;
   267     }
   269     nsAutoCString ringMsg("RING");
   270     sBluetoothHfpManager->SendLine(ringMsg.get());
   272     if (!mNumber.IsEmpty()) {
   273       nsAutoCString clipMsg("+CLIP: \"");
   274       clipMsg.Append(NS_ConvertUTF16toUTF8(mNumber).get());
   275       clipMsg.AppendLiteral("\",");
   276       clipMsg.AppendInt(mType);
   277       sBluetoothHfpManager->SendLine(clipMsg.get());
   278     }
   280     MessageLoop::current()->
   281       PostDelayedTask(FROM_HERE,
   282                       new SendRingIndicatorTask(mNumber, mType),
   283                       sRingInterval);
   284   }
   286 private:
   287   nsString mNumber;
   288   int mType;
   289 };
   290 #endif // MOZ_B2G_RIL
   292 class BluetoothHfpManager::CloseScoTask : public Task
   293 {
   294 private:
   295   void Run() MOZ_OVERRIDE
   296   {
   297     MOZ_ASSERT(sBluetoothHfpManager);
   299     sBluetoothHfpManager->DisconnectSco();
   300   }
   301 };
   303 #ifdef MOZ_B2G_RIL
   304 static bool
   305 IsValidDtmf(const char aChar) {
   306   // Valid DTMF: [*#0-9ABCD]
   307   if (aChar == '*' || aChar == '#') {
   308     return true;
   309   } else if (aChar >= '0' && aChar <= '9') {
   310     return true;
   311   } else if (aChar >= 'A' && aChar <= 'D') {
   312     return true;
   313   }
   314   return false;
   315 }
   317 static bool
   318 IsMandatoryIndicator(const CINDType aType) {
   319   return (aType == CINDType::CALL) ||
   320          (aType == CINDType::CALLHELD) ||
   321          (aType == CINDType::CALLSETUP);
   322 }
   324 /**
   325  *  Call
   326  */
   327 Call::Call()
   328 {
   329   Reset();
   330 }
   332 void
   333 Call::Reset()
   334 {
   335   mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
   336   mDirection = false;
   337   mIsConference = false;
   338   mNumber.Truncate();
   339   mType = TOA_UNKNOWN;
   340 }
   342 bool
   343 Call::IsActive()
   344 {
   345   return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
   346 }
   347 #endif // MOZ_B2G_RIL
   349 /**
   350  *  BluetoothHfpManager
   351  */
   352 BluetoothHfpManager::BluetoothHfpManager()
   353 {
   354 #ifdef MOZ_B2G_RIL
   355   mPhoneType = PhoneType::NONE;
   356 #endif // MOZ_B2G_RIL
   358   Reset();
   359 }
   361 #ifdef MOZ_B2G_RIL
   362 void
   363 BluetoothHfpManager::ResetCallArray()
   364 {
   365   mCurrentCallArray.Clear();
   366   // Append a call object at the beginning of mCurrentCallArray since call
   367   // index from RIL starts at 1.
   368   Call call;
   369   mCurrentCallArray.AppendElement(call);
   371   if (mPhoneType == PhoneType::CDMA) {
   372     mCdmaSecondCall.Reset();
   373   }
   374 }
   375 #endif // MOZ_B2G_RIL
   377 void
   378 BluetoothHfpManager::Reset()
   379 {
   380 #ifdef MOZ_B2G_RIL
   381   sStopSendingRingFlag = true;
   382   sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
   383   sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
   384   sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
   385 #endif
   386   for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
   387     sCINDItems[i].activated = true;
   388   }
   390 #ifdef MOZ_B2G_RIL
   391   mCCWA = false;
   392   mCLIP = false;
   393   mDialingRequestProcessed = true;
   395   // We disable BSIR by default as it requires OEM implement BT SCO + SPEAKER
   396   // output audio path in audio driver. OEM can enable BSIR by setting
   397   // mBSIR=true here.
   398   //
   399   // Please see Bug 878728 for more information.
   400   mBSIR = false;
   402   ResetCallArray();
   403 #endif
   404   mCMEE = false;
   405   mCMER = false;
   406   mConnectScoRequest = false;
   407   mSlcConnected = false;
   408   mIsHsp = false;
   409   mReceiveVgsFlag = false;
   410   mController = nullptr;
   411 }
   413 bool
   414 BluetoothHfpManager::Init()
   415 {
   416   MOZ_ASSERT(NS_IsMainThread());
   418   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   419   NS_ENSURE_TRUE(obs, false);
   421   if (NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false)) ||
   422       NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
   423     BT_WARNING("Failed to add observers!");
   424     return false;
   425   }
   427   hal::RegisterBatteryObserver(this);
   429 #ifdef MOZ_B2G_RIL
   430   mListener = new BluetoothRilListener();
   431   if (!mListener->Listen(true)) {
   432     BT_WARNING("Failed to start listening RIL");
   433     return false;
   434   }
   435 #endif
   437   nsCOMPtr<nsISettingsService> settings =
   438     do_GetService("@mozilla.org/settingsService;1");
   439   NS_ENSURE_TRUE(settings, false);
   441   nsCOMPtr<nsISettingsServiceLock> settingsLock;
   442   nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
   443   NS_ENSURE_SUCCESS(rv, false);
   445   nsRefPtr<GetVolumeTask> callback = new GetVolumeTask();
   446   rv = settingsLock->Get(AUDIO_VOLUME_BT_SCO_ID, callback);
   447   NS_ENSURE_SUCCESS(rv, false);
   449   Listen();
   451   mScoSocket = new BluetoothSocket(this,
   452                                    BluetoothSocketType::SCO,
   453                                    true,
   454                                    false);
   455   mScoSocketStatus = mScoSocket->GetConnectionStatus();
   456   ListenSco();
   457   return true;
   458 }
   460 BluetoothHfpManager::~BluetoothHfpManager()
   461 {
   462 #ifdef MOZ_B2G_RIL
   463   if (!mListener->Listen(false)) {
   464     BT_WARNING("Failed to stop listening RIL");
   465   }
   466   mListener = nullptr;
   467 #endif
   469   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   470   NS_ENSURE_TRUE_VOID(obs);
   472   if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
   473       NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID))) {
   474     BT_WARNING("Failed to remove observers!");
   475   }
   477   hal::UnregisterBatteryObserver(this);
   478 }
   480 //static
   481 BluetoothHfpManager*
   482 BluetoothHfpManager::Get()
   483 {
   484   MOZ_ASSERT(NS_IsMainThread());
   486   // If sBluetoothHfpManager already exists, exit early
   487   if (sBluetoothHfpManager) {
   488     return sBluetoothHfpManager;
   489   }
   491   // If we're in shutdown, don't create a new instance
   492   NS_ENSURE_FALSE(sInShutdown, nullptr);
   494   // Create a new instance, register, and return
   495   BluetoothHfpManager* manager = new BluetoothHfpManager();
   496   NS_ENSURE_TRUE(manager->Init(), nullptr);
   498   sBluetoothHfpManager = manager;
   499   return sBluetoothHfpManager;
   500 }
   502 void
   503 BluetoothHfpManager::NotifyConnectionStatusChanged(const nsAString& aType)
   504 {
   505   MOZ_ASSERT(NS_IsMainThread());
   507   // Notify Gecko observers
   508   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   509   NS_ENSURE_TRUE_VOID(obs);
   511   if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(),
   512                                      mDeviceAddress.get()))) {
   513     BT_WARNING("Failed to notify observsers!");
   514   }
   516   // Dispatch an event of status change
   517   bool status;
   518   nsAutoString eventName;
   519   if (aType.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
   520     status = IsConnected();
   521     eventName.AssignLiteral(HFP_STATUS_CHANGED_ID);
   522   } else if (aType.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
   523     status = IsScoConnected();
   524     eventName.AssignLiteral(SCO_STATUS_CHANGED_ID);
   525   } else {
   526     MOZ_ASSERT(false);
   527     return;
   528   }
   530   DispatchStatusChangedEvent(eventName, mDeviceAddress, status);
   531 }
   533 #ifdef MOZ_B2G_RIL
   534 void
   535 BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
   536 {
   537   nsString type, name;
   538   BluetoothValue v;
   539   InfallibleTArray<BluetoothNamedValue> parameters;
   540   type.AssignLiteral("bluetooth-dialer-command");
   542   name.AssignLiteral("command");
   543   v = nsString(aCommand);
   544   parameters.AppendElement(BluetoothNamedValue(name, v));
   546   if (!BroadcastSystemMessage(type, parameters)) {
   547     BT_WARNING("Failed to broadcast system message to dialer");
   548   }
   549 }
   550 #endif // MOZ_B2G_RIL
   552 void
   553 BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
   554 {
   555   MOZ_ASSERT(NS_IsMainThread());
   557   // The string that we're interested in will be a JSON string that looks like:
   558   //  {"key":"volumeup", "value":10}
   559   //  {"key":"volumedown", "value":2}
   561   JSContext* cx = nsContentUtils::GetSafeJSContext();
   562   NS_ENSURE_TRUE_VOID(cx);
   564   JS::Rooted<JS::Value> val(cx);
   565   NS_ENSURE_TRUE_VOID(JS_ParseJSON(cx, aData.BeginReading(), aData.Length(), &val));
   566   NS_ENSURE_TRUE_VOID(val.isObject());
   568   JS::Rooted<JSObject*> obj(cx, &val.toObject());
   569   JS::Rooted<JS::Value> key(cx);
   570   if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
   571     return;
   572   }
   574   bool match;
   575   if (!JS_StringEqualsAscii(cx, key.toString(), AUDIO_VOLUME_BT_SCO_ID, &match) ||
   576       !match) {
   577     return;
   578   }
   580   JS::Rooted<JS::Value> value(cx);
   581   if (!JS_GetProperty(cx, obj, "value", &value)||
   582       !value.isNumber()) {
   583     return;
   584   }
   586   mCurrentVgs = value.toNumber();
   588   // Adjust volume by headset and we don't have to send volume back to headset
   589   if (mReceiveVgsFlag) {
   590     mReceiveVgsFlag = false;
   591     return;
   592   }
   594   // Only send volume back when there's a connected headset
   595   if (IsConnected()) {
   596     SendCommand(RESPONSE_VGS, mCurrentVgs);
   597   }
   598 }
   600 #ifdef MOZ_B2G_RIL
   601 void
   602 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
   603 {
   604   nsCOMPtr<nsIMobileConnectionProvider> connection =
   605     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   606   NS_ENSURE_TRUE_VOID(connection);
   608   nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
   609   connection->GetVoiceConnectionInfo(aClientId, getter_AddRefs(voiceInfo));
   610   NS_ENSURE_TRUE_VOID(voiceInfo);
   612   nsString type;
   613   voiceInfo->GetType(type);
   614   mPhoneType = GetPhoneType(type);
   616   bool roaming;
   617   voiceInfo->GetRoaming(&roaming);
   618   UpdateCIND(CINDType::ROAM, roaming);
   620   nsString regState;
   621   voiceInfo->GetState(regState);
   622   bool service = regState.EqualsLiteral("registered");
   623   if (service != sCINDItems[CINDType::SERVICE].value) {
   624     // Notify BluetoothRilListener of service change
   625     mListener->ServiceChanged(aClientId, service);
   626   }
   627   UpdateCIND(CINDType::SERVICE, service);
   629   JSContext* cx = nsContentUtils::GetSafeJSContext();
   630   NS_ENSURE_TRUE_VOID(cx);
   631   JS::Rooted<JS::Value> value(cx);
   632   voiceInfo->GetRelSignalStrength(&value);
   633   NS_ENSURE_TRUE_VOID(value.isNumber());
   634   uint8_t signal = ceil(value.toNumber() / 20.0);
   635   UpdateCIND(CINDType::SIGNAL, signal);
   637   /**
   638    * Possible return values for mode are:
   639    * - null (unknown): set mNetworkSelectionMode to 0 (auto)
   640    * - automatic: set mNetworkSelectionMode to 0 (auto)
   641    * - manual: set mNetworkSelectionMode to 1 (manual)
   642    */
   643   nsString mode;
   644   connection->GetNetworkSelectionMode(aClientId, mode);
   645   if (mode.EqualsLiteral("manual")) {
   646     mNetworkSelectionMode = 1;
   647   } else {
   648     mNetworkSelectionMode = 0;
   649   }
   651   nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
   652   voiceInfo->GetNetwork(getter_AddRefs(network));
   653   NS_ENSURE_TRUE_VOID(network);
   654   network->GetLongName(mOperatorName);
   656   // According to GSM 07.07, "<format> indicates if the format is alphanumeric
   657   // or numeric; long alphanumeric format can be upto 16 characters long and
   658   // short format up to 8 characters (refer GSM MoU SE.13 [9])..."
   659   // However, we found that the operator name may sometimes be longer than 16
   660   // characters. After discussion, we decided to fix this here but not in RIL
   661   // or modem.
   662   //
   663   // Please see Bug 871366 for more information.
   664   if (mOperatorName.Length() > 16) {
   665     BT_WARNING("The operator name was longer than 16 characters. We cut it.");
   666     mOperatorName.Left(mOperatorName, 16);
   667   }
   668 }
   670 void
   671 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
   672 {
   673   nsCOMPtr<nsIIccProvider> icc =
   674     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   675   NS_ENSURE_TRUE_VOID(icc);
   677   nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
   678   icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
   679   NS_ENSURE_TRUE_VOID(iccInfo);
   681   nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   682   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   683   gsmIccInfo->GetMsisdn(mMsisdn);
   684 }
   685 #endif // MOZ_B2G_RIL
   687 void
   688 BluetoothHfpManager::HandleShutdown()
   689 {
   690   MOZ_ASSERT(NS_IsMainThread());
   691   sInShutdown = true;
   692   Disconnect(nullptr);
   693   DisconnectSco();
   694   sBluetoothHfpManager = nullptr;
   695 }
   697 // Virtual function of class SocketConsumer
   698 void
   699 BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
   700                                        nsAutoPtr<UnixSocketRawData>& aMessage)
   701 {
   702   MOZ_ASSERT(NS_IsMainThread());
   703   MOZ_ASSERT(aSocket);
   705   nsAutoCString msg((const char*)aMessage->mData.get(), aMessage->mSize);
   706   msg.StripWhitespace();
   708   nsTArray<nsCString> atCommandValues;
   710   // For more information, please refer to 4.34.1 "Bluetooth Defined AT
   711   // Capabilities" in Bluetooth hands-free profile 1.6
   712   if (msg.Find("AT+BRSF=") != -1) {
   713 #ifdef MOZ_B2G_RIL
   714     uint32_t brsf = BRSF_BIT_ABILITY_TO_REJECT_CALL |
   715                     BRSF_BIT_ENHANCED_CALL_STATUS;
   717     // No support for three way calling in CDMA since
   718     // CDMA disallows to hang existing call for CHLD=1
   719     if (mPhoneType != PhoneType::CDMA) {
   720       brsf |= BRSF_BIT_THREE_WAY_CALLING;
   721     }
   723     if (mBSIR) {
   724       brsf |= BRSF_BIT_IN_BAND_RING_TONE;
   725     }
   726 #else
   727     uint32_t brsf = 0;
   728 #endif // MOZ_B2G_RIL
   730     SendCommand(RESPONSE_BRSF, brsf);
   731   } else if (msg.Find("AT+CIND=?") != -1) {
   732     // Asking for CIND range
   733     SendCommand(RESPONSE_CIND, 0);
   734   } else if (msg.Find("AT+CIND?") != -1) {
   735     // Asking for CIND value
   736     SendCommand(RESPONSE_CIND, 1);
   737   } else if (msg.Find("AT+CMER=") != -1) {
   738     /**
   739      * SLC establishment is done when AT+CMER has been received.
   740      * Do nothing but respond with "OK".
   741      */
   742     ParseAtCommand(msg, 8, atCommandValues);
   744     if (atCommandValues.Length() < 4) {
   745       BT_WARNING("Could't get the value of command [AT+CMER=]");
   746       goto respond_with_ok;
   747     }
   749     if (!atCommandValues[0].EqualsLiteral("3") ||
   750         !atCommandValues[1].EqualsLiteral("0") ||
   751         !atCommandValues[2].EqualsLiteral("0")) {
   752       BT_WARNING("Wrong value of CMER");
   753       goto respond_with_ok;
   754     }
   756     mCMER = atCommandValues[3].EqualsLiteral("1");
   758     /**
   759      * SLC is connected once the "indicator status update" is enabled by
   760      * AT+CMER command. See 4.2.1 in Bluetooth hands-free profile 1.6
   761      * for more details.
   762      */
   763     if (mCMER) {
   764       mSlcConnected = true;
   765     }
   767     // If we get internal request for SCO connection,
   768     // setup SCO after Service Level Connection established.
   769     if (mConnectScoRequest) {
   770       mConnectScoRequest = false;
   771       ConnectSco();
   772     }
   773   } else if (msg.Find("AT+CMEE=") != -1) {
   774     ParseAtCommand(msg, 8, atCommandValues);
   776     if (atCommandValues.IsEmpty()) {
   777       BT_WARNING("Could't get the value of command [AT+CMEE=]");
   778       goto respond_with_ok;
   779     }
   781     // AT+CMEE = 0: +CME ERROR shall not be used
   782     // AT+CMEE = 1: use numeric <err>
   783     // AT+CMEE = 2: use verbose <err>
   784     mCMEE = !atCommandValues[0].EqualsLiteral("0");
   785 #ifdef MOZ_B2G_RIL
   786   } else if (msg.Find("AT+COPS=") != -1) {
   787     ParseAtCommand(msg, 8, atCommandValues);
   789     if (atCommandValues.Length() != 2) {
   790       BT_WARNING("Could't get the value of command [AT+COPS=]");
   791       goto respond_with_ok;
   792     }
   794     // Handsfree only support AT+COPS=3,0
   795     if (!atCommandValues[0].EqualsLiteral("3") ||
   796         !atCommandValues[1].EqualsLiteral("0")) {
   797       if (mCMEE) {
   798         SendCommand(RESPONSE_CME_ERROR, BluetoothCmeError::OPERATION_NOT_SUPPORTED);
   799       } else {
   800         SendLine("ERROR");
   801       }
   802       return;
   803     }
   804   } else if (msg.Find("AT+COPS?") != -1) {
   805     nsAutoCString message("+COPS: ");
   806     message.AppendInt(mNetworkSelectionMode);
   807     message.AppendLiteral(",0,\"");
   808     message.Append(NS_ConvertUTF16toUTF8(mOperatorName));
   809     message.AppendLiteral("\"");
   810     SendLine(message.get());
   811   } else if (msg.Find("AT+VTS=") != -1) {
   812     ParseAtCommand(msg, 7, atCommandValues);
   814     if (atCommandValues.Length() != 1) {
   815       BT_WARNING("Couldn't get the value of command [AT+VTS=]");
   816       goto respond_with_ok;
   817     }
   819     if (IsValidDtmf(atCommandValues[0].get()[0])) {
   820       nsAutoCString message("VTS=");
   821       message += atCommandValues[0].get()[0];
   822       NotifyDialer(NS_ConvertUTF8toUTF16(message));
   823     }
   824 #endif // MOZ_B2G_RIL
   825   } else if (msg.Find("AT+VGM=") != -1) {
   826     ParseAtCommand(msg, 7, atCommandValues);
   828     if (atCommandValues.IsEmpty()) {
   829       BT_WARNING("Couldn't get the value of command [AT+VGM]");
   830       goto respond_with_ok;
   831     }
   833     nsresult rv;
   834     int vgm = atCommandValues[0].ToInteger(&rv);
   835     if (NS_FAILED(rv)) {
   836       BT_WARNING("Failed to extract microphone volume from bluetooth headset!");
   837       goto respond_with_ok;
   838     }
   840     if (vgm < 0 || vgm > 15) {
   841       BT_WARNING("Received invalid VGM value");
   842       goto respond_with_ok;
   843     }
   845     mCurrentVgm = vgm;
   846 #ifdef MOZ_B2G_RIL
   847   } else if (msg.Find("AT+CHLD=?") != -1) {
   848     SendLine("+CHLD: (0,1,2,3)");
   849   } else if (msg.Find("AT+CHLD=") != -1) {
   850     ParseAtCommand(msg, 8, atCommandValues);
   852     if (atCommandValues.IsEmpty()) {
   853       BT_WARNING("Could't get the value of command [AT+CHLD=]");
   854       goto respond_with_ok;
   855     }
   857     /**
   858      * The following three cases are supported:
   859      * AT+CHLD=0 - Releases all held calls or sets User Determined User Busy
   860      *             (UDUB) for a waiting call
   861      * AT+CHLD=1 - Releases active calls and accepts the other (held or
   862      *             waiting) call
   863      * AT+CHLD=2 - Places active calls on hold and accepts the other (held
   864      *             or waiting) call
   865      * AT+CHLD=3 - Adds a held call to the conversation.
   866      *
   867      * The following cases are NOT supported yet:
   868      * AT+CHLD=1<idx>, AT+CHLD=2<idx>, AT+CHLD=4
   869      * Please see 4.33.2 in Bluetooth hands-free profile 1.6 for more
   870      * information.
   871      */
   872     char chld = atCommandValues[0][0];
   873     bool valid = true;
   874     if (atCommandValues[0].Length() > 1) {
   875       BT_WARNING("No index should be included in command [AT+CHLD]");
   876       valid = false;
   877     } else if (chld == '4') {
   878       BT_WARNING("The value of command [AT+CHLD] is not supported");
   879       valid = false;
   880     } else if (chld == '0') {
   881       // We need to rename these dialer commands for better readability
   882       // and expandability.
   883       // See bug 884190 for more information.
   884       NotifyDialer(NS_LITERAL_STRING("CHLD=0"));
   885     } else if (chld == '1') {
   886       NotifyDialer(NS_LITERAL_STRING("CHLD=1"));
   887     } else if (chld == '2') {
   888       NotifyDialer(NS_LITERAL_STRING("CHLD=2"));
   889     } else if (chld == '3') {
   890       NotifyDialer(NS_LITERAL_STRING("CHLD=3"));
   891     } else {
   892       BT_WARNING("Wrong value of command [AT+CHLD]");
   893       valid = false;
   894     }
   896     if (!valid) {
   897       SendLine("ERROR");
   898       return;
   899     }
   900 #endif // MOZ_B2G_RIL
   901   } else if (msg.Find("AT+VGS=") != -1) {
   902     // Adjust volume by headset
   903     mReceiveVgsFlag = true;
   904     ParseAtCommand(msg, 7, atCommandValues);
   906     if (atCommandValues.IsEmpty()) {
   907       BT_WARNING("Could't get the value of command [AT+VGS=]");
   908       goto respond_with_ok;
   909     }
   911     nsresult rv;
   912     int newVgs = atCommandValues[0].ToInteger(&rv);
   913     if (NS_FAILED(rv)) {
   914       BT_WARNING("Failed to extract volume value from bluetooth headset!");
   915       goto respond_with_ok;
   916     }
   918     if (newVgs == mCurrentVgs) {
   919       goto respond_with_ok;
   920     }
   922     if (newVgs < 0 || newVgs > 15) {
   923       BT_WARNING("Received invalid VGS value");
   924       goto respond_with_ok;
   925     }
   927     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   928     if (!os) {
   929       BT_WARNING("Failed to get observer service!");
   930       goto respond_with_ok;
   931     }
   933     nsString data;
   934     data.AppendInt(newVgs);
   935     os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get());
   936 #ifdef MOZ_B2G_RIL
   937   } else if ((msg.Find("AT+BLDN") != -1) || (msg.Find("ATD>") != -1)) {
   938     // Dialer app of FFOS v1 does not have plan to support Memory Dailing.
   939     // However, in order to pass Bluetooth HFP certification, we still have to
   940     // make a call when we receive AT command 'ATD>n'.
   941     mDialingRequestProcessed = false;
   943     if (msg.Find("AT+BLDN") != -1) {
   944       NotifyDialer(NS_LITERAL_STRING("BLDN"));
   945     } else {
   946       NotifyDialer(NS_ConvertUTF8toUTF16(msg));
   947     }
   949     MessageLoop::current()->
   950       PostDelayedTask(FROM_HERE, new RespondToBLDNTask(),
   951                       sWaitingForDialingInterval);
   953     // Don't send response 'OK' here because we'll respond later in either
   954     // RespondToBLDNTask or HandleCallStateChanged()
   955     return;
   956   } else if (msg.Find("ATA") != -1) {
   957     NotifyDialer(NS_LITERAL_STRING("ATA"));
   958   } else if (msg.Find("AT+CHUP") != -1) {
   959     NotifyDialer(NS_LITERAL_STRING("CHUP"));
   960   } else if (msg.Find("AT+CLCC") != -1) {
   961     SendCommand(RESPONSE_CLCC);
   962   } else if (msg.Find("ATD") != -1) {
   963     nsAutoCString message(msg), newMsg;
   964     int end = message.FindChar(';');
   965     if (end < 0) {
   966       BT_WARNING("Could't get the value of command [ATD]");
   967       goto respond_with_ok;
   968     }
   970     newMsg += nsDependentCSubstring(message, 0, end);
   971     NotifyDialer(NS_ConvertUTF8toUTF16(newMsg));
   972   } else if (msg.Find("AT+CLIP=") != -1) {
   973     ParseAtCommand(msg, 8, atCommandValues);
   975     if (atCommandValues.IsEmpty()) {
   976       BT_WARNING("Could't get the value of command [AT+CLIP=]");
   977       goto respond_with_ok;
   978     }
   980     mCLIP = atCommandValues[0].EqualsLiteral("1");
   981   } else if (msg.Find("AT+CCWA=") != -1) {
   982     ParseAtCommand(msg, 8, atCommandValues);
   984     if (atCommandValues.IsEmpty()) {
   985       BT_WARNING("Could't get the value of command [AT+CCWA=]");
   986       goto respond_with_ok;
   987     }
   989     mCCWA = atCommandValues[0].EqualsLiteral("1");
   990   } else if (msg.Find("AT+CKPD") != -1) {
   991     if (!sStopSendingRingFlag) {
   992       // Bluetooth HSP spec 4.2.2
   993       // There is an incoming call, notify Dialer to pick up the phone call
   994       // and SCO will be established after we get the CallStateChanged event
   995       // indicating the call is answered successfully.
   996       NotifyDialer(NS_LITERAL_STRING("ATA"));
   997     } else {
   998       if (!IsScoConnected()) {
   999         // Bluetooth HSP spec 4.3
  1000         // If there's no SCO, set up a SCO link.
  1001         ConnectSco();
  1002       } else if (!mFirstCKPD) {
  1003         // Bluetooth HSP spec 4.5
  1004         // There are two ways to release SCO: sending CHUP to dialer or closing
  1005         // SCO socket directly. We notify dialer only if there is at least one
  1006         // active call.
  1007         if (mCurrentCallArray.Length() > 1) {
  1008           NotifyDialer(NS_LITERAL_STRING("CHUP"));
  1009         } else {
  1010           DisconnectSco();
  1012       } else {
  1013         // Three conditions have to be matched to come in here:
  1014         // (1) Not sending RING indicator
  1015         // (2) A SCO link exists
  1016         // (3) This is the very first AT+CKPD=200 of this session
  1017         // It is the case of Figure 4.3, Bluetooth HSP spec. Do nothing.
  1018         BT_WARNING("AT+CKPD=200: Do nothing");
  1022     mFirstCKPD = false;
  1023   } else if (msg.Find("AT+CNUM") != -1) {
  1024     if (!mMsisdn.IsEmpty()) {
  1025       nsAutoCString message("+CNUM: ,\"");
  1026       message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
  1027       message.AppendLiteral("\",");
  1028       message.AppendInt(TOA_UNKNOWN);
  1029       message.AppendLiteral(",,4");
  1030       SendLine(message.get());
  1032   } else if (msg.Find("AT+BIA=") != -1) {
  1033     ParseAtCommand(msg, 7, atCommandValues);
  1035     for (uint8_t i = 0; i < atCommandValues.Length(); i++) {
  1036       CINDType indicatorType = (CINDType) (i + 1);
  1037       if (indicatorType >= (int)ArrayLength(sCINDItems)) {
  1038         // Ignore excess parameters at the end
  1039         break;
  1042       if (!IsMandatoryIndicator(indicatorType)) {
  1043         /**
  1044          * Accept only following indicator states:
  1045          * - "1": activate
  1046          * - "0": deactivate
  1047          * - "" : maintain current state
  1048          * Otherwise we regard the command incorrectly formatted.
  1049          */
  1050         if (atCommandValues[i].EqualsLiteral("1")) {
  1051           sCINDItems[indicatorType].activated = 1;
  1052         } else if (atCommandValues[i].EqualsLiteral("0")) {
  1053           sCINDItems[indicatorType].activated = 0;
  1054         } else if (!atCommandValues[i].EqualsLiteral("")) {
  1055           SendLine("ERROR");
  1056           return;
  1058       } else {
  1059         // Ignore requests to activate/deactivate mandatory indicators
  1062 #endif // MOZ_B2G_RIL
  1063   } else {
  1064     nsCString warningMsg;
  1065     warningMsg.Append(NS_LITERAL_CSTRING("Unsupported AT command: "));
  1066     warningMsg.Append(msg);
  1067     warningMsg.Append(NS_LITERAL_CSTRING(", reply with ERROR"));
  1068     BT_WARNING(warningMsg.get());
  1070     SendLine("ERROR");
  1071     return;
  1074 respond_with_ok:
  1075   // We always respond to remote device with "OK" in general cases.
  1076   SendLine("OK");
  1079 void
  1080 BluetoothHfpManager::Connect(const nsAString& aDeviceAddress,
  1081                              BluetoothProfileController* aController)
  1083   MOZ_ASSERT(NS_IsMainThread());
  1084   MOZ_ASSERT(aController && !mController);
  1086   BluetoothService* bs = BluetoothService::Get();
  1087   if (!bs || sInShutdown) {
  1088     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
  1089     return;
  1092   if (mSocket) {
  1093     if (mDeviceAddress == aDeviceAddress) {
  1094       aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
  1095     } else {
  1096       aController->NotifyCompletion(NS_LITERAL_STRING(ERR_REACHED_CONNECTION_LIMIT));
  1098     return;
  1101   nsString uuid;
  1102   BluetoothUuidHelper::GetString(BluetoothServiceClass::HANDSFREE, uuid);
  1104   if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
  1105     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
  1106     return;
  1109   // Stop listening because currently we only support one connection at a time.
  1110   if (mHandsfreeSocket) {
  1111     mHandsfreeSocket->Disconnect();
  1112     mHandsfreeSocket = nullptr;
  1115   if (mHeadsetSocket) {
  1116     mHeadsetSocket->Disconnect();
  1117     mHeadsetSocket = nullptr;
  1120   mController = aController;
  1121   mSocket =
  1122     new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
  1125 bool
  1126 BluetoothHfpManager::Listen()
  1128   MOZ_ASSERT(NS_IsMainThread());
  1130   if (sInShutdown) {
  1131     BT_WARNING("Listen called while in shutdown!");
  1132     return false;
  1135   if (mSocket) {
  1136     BT_WARNING("mSocket exists. Failed to listen.");
  1137     return false;
  1140   if (!mHandsfreeSocket) {
  1141     mHandsfreeSocket =
  1142       new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
  1144     if (!mHandsfreeSocket->Listen(
  1145           BluetoothReservedChannels::CHANNEL_HANDSFREE_AG)) {
  1146       BT_WARNING("[HFP] Can't listen on RFCOMM socket!");
  1147       mHandsfreeSocket = nullptr;
  1148       return false;
  1152   if (!mHeadsetSocket) {
  1153     mHeadsetSocket =
  1154       new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
  1156     if (!mHeadsetSocket->Listen(
  1157           BluetoothReservedChannels::CHANNEL_HEADSET_AG)) {
  1158       BT_WARNING("[HSP] Can't listen on RFCOMM socket!");
  1159       mHandsfreeSocket->Disconnect();
  1160       mHandsfreeSocket = nullptr;
  1161       mHeadsetSocket = nullptr;
  1162       return false;
  1166   return true;
  1169 void
  1170 BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
  1172   MOZ_ASSERT(NS_IsMainThread());
  1174   if (!mSocket) {
  1175     if (aController) {
  1176       aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
  1178     return;
  1181   MOZ_ASSERT(!mController);
  1183   mController = aController;
  1184   mSocket->Disconnect();
  1187 #ifdef MOZ_B2G_RIL
  1188 void
  1189 BluetoothHfpManager::SendCCWA(const nsAString& aNumber, int aType)
  1191   if (mCCWA) {
  1192     nsAutoCString ccwaMsg("+CCWA: \"");
  1193     ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
  1194     ccwaMsg.AppendLiteral("\",");
  1195     ccwaMsg.AppendInt(aType);
  1196     SendLine(ccwaMsg.get());
  1200 bool
  1201 BluetoothHfpManager::SendCLCC(const Call& aCall, int aIndex)
  1203   if (aCall.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
  1204     return true;
  1207   nsAutoCString message(RESPONSE_CLCC);
  1208   message.AppendInt(aIndex);
  1209   message.AppendLiteral(",");
  1210   message.AppendInt(aCall.mDirection);
  1211   message.AppendLiteral(",");
  1213   int status = 0;
  1214   switch (aCall.mState) {
  1215     case nsITelephonyProvider::CALL_STATE_CONNECTED:
  1216       if (mPhoneType == PhoneType::CDMA && aIndex == 1) {
  1217         status = (mCdmaSecondCall.IsActive()) ? 1 : 0;
  1219       message.AppendInt(status);
  1220       break;
  1221     case nsITelephonyProvider::CALL_STATE_HELD:
  1222       message.AppendInt(1);
  1223       break;
  1224     case nsITelephonyProvider::CALL_STATE_DIALING:
  1225       message.AppendInt(2);
  1226       break;
  1227     case nsITelephonyProvider::CALL_STATE_ALERTING:
  1228       message.AppendInt(3);
  1229       break;
  1230     case nsITelephonyProvider::CALL_STATE_INCOMING:
  1231       if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
  1232         message.AppendInt(4);
  1233       } else {
  1234         message.AppendInt(5);
  1236       break;
  1237     default:
  1238       BT_WARNING("Not handling call status for CLCC");
  1239       break;
  1242   message.AppendLiteral(",0,0,\"");
  1243   message.Append(NS_ConvertUTF16toUTF8(aCall.mNumber));
  1244   message.AppendLiteral("\",");
  1245   message.AppendInt(aCall.mType);
  1247   return SendLine(message.get());
  1249 #endif // MOZ_B2G_RIL
  1251 bool
  1252 BluetoothHfpManager::SendLine(const char* aMessage)
  1254   MOZ_ASSERT(mSocket);
  1256   nsAutoCString msg;
  1258   msg.AppendLiteral(kHfpCrlf);
  1259   msg.Append(aMessage);
  1260   msg.AppendLiteral(kHfpCrlf);
  1262   return mSocket->SendSocketData(msg);
  1265 bool
  1266 BluetoothHfpManager::SendCommand(const char* aCommand, uint32_t aValue)
  1268   if (!IsConnected()) {
  1269     BT_WARNING("Trying to SendCommand() without a SLC");
  1270     return false;
  1273   nsAutoCString message;
  1274   message += aCommand;
  1276   if (!strcmp(aCommand, RESPONSE_CIEV)) {
  1277     if (!mCMER || !sCINDItems[aValue].activated) {
  1278       // Indicator status update is disabled
  1279       return true;
  1282     if ((aValue < 1) || (aValue > ArrayLength(sCINDItems) - 1)) {
  1283       BT_WARNING("unexpected CINDType for CIEV command");
  1284       return false;
  1287     message.AppendInt(aValue);
  1288     message.AppendLiteral(",");
  1289     message.AppendInt(sCINDItems[aValue].value);
  1290   } else if (!strcmp(aCommand, RESPONSE_CIND)) {
  1291     if (!aValue) {
  1292       // Query for range
  1293       for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
  1294         message.AppendLiteral("(\"");
  1295         message.Append(sCINDItems[i].name);
  1296         message.AppendLiteral("\",(");
  1297         message.Append(sCINDItems[i].range);
  1298         message.AppendLiteral(")");
  1299         if (i == (ArrayLength(sCINDItems) - 1)) {
  1300           message.AppendLiteral(")");
  1301           break;
  1303         message.AppendLiteral("),");
  1305     } else {
  1306       // Query for value
  1307       for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
  1308         message.AppendInt(sCINDItems[i].value);
  1309         if (i == (ArrayLength(sCINDItems) - 1)) {
  1310           break;
  1312         message.AppendLiteral(",");
  1315 #ifdef MOZ_B2G_RIL
  1316   } else if (!strcmp(aCommand, RESPONSE_CLCC)) {
  1317     bool rv = true;
  1318     uint32_t callNumbers = mCurrentCallArray.Length();
  1319     uint32_t i;
  1320     for (i = 1; i < callNumbers; i++) {
  1321       rv &= SendCLCC(mCurrentCallArray[i], i);
  1324     if (!mCdmaSecondCall.mNumber.IsEmpty()) {
  1325       MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
  1326       MOZ_ASSERT(i == 2);
  1328       rv &= SendCLCC(mCdmaSecondCall, 2);
  1331     return rv;
  1332 #endif // MOZ_B2G_RIL
  1333   } else {
  1334     message.AppendInt(aValue);
  1337   return SendLine(message.get());
  1340 #ifdef MOZ_B2G_RIL
  1341 void
  1342 BluetoothHfpManager::UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend)
  1344   if (sCINDItems[aType].value != aValue) {
  1345     sCINDItems[aType].value = aValue;
  1346     if (aSend) {
  1347       SendCommand(RESPONSE_CIEV, aType);
  1352 uint32_t
  1353 BluetoothHfpManager::FindFirstCall(uint16_t aState)
  1355   uint32_t callLength = mCurrentCallArray.Length();
  1357   for (uint32_t i = 1; i < callLength; ++i) {
  1358     if (mCurrentCallArray[i].mState == aState) {
  1359       return i;
  1363   return 0;
  1366 uint32_t
  1367 BluetoothHfpManager::GetNumberOfCalls(uint16_t aState)
  1369   uint32_t num = 0;
  1370   uint32_t callLength = mCurrentCallArray.Length();
  1372   for (uint32_t i = 1; i < callLength; ++i) {
  1373     if (mCurrentCallArray[i].mState == aState) {
  1374       ++num;
  1378   return num;
  1381 uint32_t
  1382 BluetoothHfpManager::GetNumberOfConCalls()
  1384   uint32_t num = 0;
  1385   uint32_t callLength = mCurrentCallArray.Length();
  1387   for (uint32_t i = 1; i < callLength; ++i) {
  1388     if (mCurrentCallArray[i].mIsConference) {
  1389       ++num;
  1393   return num;
  1396 uint32_t
  1397 BluetoothHfpManager::GetNumberOfConCalls(uint16_t aState)
  1399   uint32_t num = 0;
  1400   uint32_t callLength = mCurrentCallArray.Length();
  1402   for (uint32_t i = 1; i < callLength; ++i) {
  1403     if (mCurrentCallArray[i].mIsConference
  1404         && mCurrentCallArray[i].mState == aState) {
  1405       ++num;
  1409   return num;
  1412 void
  1413 BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
  1414                                             uint16_t aCallState,
  1415                                             const nsAString& aError,
  1416                                             const nsAString& aNumber,
  1417                                             const bool aIsOutgoing,
  1418                                             const bool aIsConference,
  1419                                             bool aSend)
  1421   if (!IsConnected()) {
  1422     // Normal case. No need to print out warnings.
  1423     return;
  1426   // aCallIndex can be UINT32_MAX for the pending outgoing call state update.
  1427   // aCallIndex will be updated again after real call state changes. See Bug
  1428   // 990467.
  1429   if (aCallIndex == UINT32_MAX) {
  1430     return;
  1433   while (aCallIndex >= mCurrentCallArray.Length()) {
  1434     Call call;
  1435     mCurrentCallArray.AppendElement(call);
  1438   uint16_t prevCallState = mCurrentCallArray[aCallIndex].mState;
  1439   mCurrentCallArray[aCallIndex].mState = aCallState;
  1440   mCurrentCallArray[aCallIndex].mDirection = !aIsOutgoing;
  1442   bool prevCallIsConference = mCurrentCallArray[aCallIndex].mIsConference;
  1443   mCurrentCallArray[aCallIndex].mIsConference = aIsConference;
  1445   // Same logic as implementation in ril_worker.js
  1446   if (aNumber.Length() && aNumber[0] == '+') {
  1447     mCurrentCallArray[aCallIndex].mType = TOA_INTERNATIONAL;
  1449   mCurrentCallArray[aCallIndex].mNumber = aNumber;
  1451   nsRefPtr<nsRunnable> sendRingTask;
  1452   nsString address;
  1454   switch (aCallState) {
  1455     case nsITelephonyProvider::CALL_STATE_HELD:
  1456       switch (prevCallState) {
  1457         case nsITelephonyProvider::CALL_STATE_CONNECTED: {
  1458           uint32_t numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
  1459           uint32_t numHeld = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_HELD);
  1460           uint32_t numConCalls = GetNumberOfConCalls();
  1462           /**
  1463            * An active call becomes a held call.
  1465            * If this call is not a conference call,
  1466            * - callheld state = ONHOLD_NOACTIVE if no active call remains;
  1467            * - callheld state = ONHOLD_ACTIVE otherwise.
  1468            * If this call belongs to a conference call and all other members of
  1469            * the conference call have become held calls,
  1470            * - callheld state = ONHOLD_NOACTIVE if no active call remains;
  1471            * - callheld state = ONHOLD_ACTIVE otherwise.
  1473            * Note number of active calls may be 0 in-between state transition
  1474            * (c1 has become held but c2 has not become active yet), so we regard
  1475            * no active call remains if there is no other active/held call
  1476            * besides this changed call/group of conference call.
  1477            */
  1478           if (!aIsConference) {
  1479             if (numActive + numHeld == 1) {
  1480               // A single active call is put on hold.
  1481               sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
  1482             } else {
  1483               // An active call is placed on hold or active/held calls swapped.
  1484               sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
  1486             SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
  1487           } else if (GetNumberOfConCalls(nsITelephonyProvider::CALL_STATE_HELD)
  1488                      == numConCalls) {
  1489             if (numActive + numHeld == numConCalls) {
  1490               // An active conference call is put on hold.
  1491               sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_NOACTIVE;
  1492             } else {
  1493               // Active calls are placed on hold or active/held calls swapped.
  1494               sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
  1496             SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
  1498           break;
  1500         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
  1501           // The call state changed from DISCONNECTED to HELD. It could happen
  1502           // when user held a call before Bluetooth got connected.
  1503           if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
  1504             // callheld = ONHOLD_ACTIVE if an active call already exists.
  1505             sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
  1506             SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
  1508           break;
  1510       break;
  1511     case nsITelephonyProvider::CALL_STATE_INCOMING:
  1512       if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
  1513         SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
  1514         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
  1515       } else {
  1516         // Start sending RING indicator to HF
  1517         sStopSendingRingFlag = false;
  1518         UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
  1520         if (mBSIR) {
  1521           // Setup audio connection for in-band ring tone
  1522           ConnectSco();
  1525         nsAutoString number(aNumber);
  1526         if (!mCLIP) {
  1527           number.AssignLiteral("");
  1530         MessageLoop::current()->PostDelayedTask(
  1531           FROM_HERE,
  1532           new SendRingIndicatorTask(number,
  1533                                     mCurrentCallArray[aCallIndex].mType),
  1534           sRingInterval);
  1536       break;
  1537     case nsITelephonyProvider::CALL_STATE_DIALING:
  1538       if (!mDialingRequestProcessed) {
  1539         SendLine("OK");
  1540         mDialingRequestProcessed = true;
  1543       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING, aSend);
  1544       ConnectSco();
  1545       break;
  1546     case nsITelephonyProvider::CALL_STATE_ALERTING:
  1547       UpdateCIND(CINDType::CALLSETUP, CallSetupState::OUTGOING_ALERTING, aSend);
  1549       // If there's an ongoing call when the headset is just connected, we have
  1550       // to open a sco socket here.
  1551       ConnectSco();
  1552       break;
  1553     case nsITelephonyProvider::CALL_STATE_CONNECTED:
  1554       /**
  1555        * A call becomes active because:
  1556        * - user answers an incoming call,
  1557        * - user dials a outgoing call and it is answered, or
  1558        * - SLC is connected when a call is active.
  1559        */
  1560       switch (prevCallState) {
  1561         case nsITelephonyProvider::CALL_STATE_INCOMING:
  1562         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
  1563           // Incoming call, no break
  1564           sStopSendingRingFlag = true;
  1565           ConnectSco();
  1566           // NO BREAK HERE. continue to next statement
  1567         case nsITelephonyProvider::CALL_STATE_DIALING:
  1568         case nsITelephonyProvider::CALL_STATE_ALERTING:
  1569           // Outgoing call
  1570           UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
  1571           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
  1573           if (FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
  1574             // callheld state = ONHOLD_ACTIVE if a held call already exists.
  1575             UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
  1577           break;
  1578         case nsITelephonyProvider::CALL_STATE_CONNECTED:
  1579           // User wants to add a held call to the conversation.
  1580           // The original connected call becomes a conference call here.
  1581           if (aIsConference) {
  1582             UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
  1584           break;
  1585         case nsITelephonyProvider::CALL_STATE_HELD:
  1586           if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
  1587             if (aIsConference && !prevCallIsConference) {
  1588               // The held call was merged and becomes a conference call.
  1589               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
  1590             } else if (sCINDItems[CINDType::CALLHELD].value ==
  1591                        CallHeldState::ONHOLD_NOACTIVE) {
  1592               // The held call(s) become connected call(s).
  1593               UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
  1596           break;
  1598         default:
  1599           BT_WARNING("Not handling state changed");
  1601       break;
  1602     case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
  1603       switch (prevCallState) {
  1604         case nsITelephonyProvider::CALL_STATE_INCOMING:
  1605           // Incoming call, no break
  1606           sStopSendingRingFlag = true;
  1607         case nsITelephonyProvider::CALL_STATE_DIALING:
  1608         case nsITelephonyProvider::CALL_STATE_ALERTING:
  1609           // Outgoing call
  1610           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
  1611           break;
  1612         case nsITelephonyProvider::CALL_STATE_CONNECTED:
  1613           // No call is ongoing
  1614           if (sCINDItems[CINDType::CALLHELD].value ==
  1615               CallHeldState::NO_CALLHELD) {
  1616             UpdateCIND(CINDType::CALL, CallState::NO_CALL, aSend);
  1618           break;
  1619         default:
  1620           BT_WARNING("Not handling state changed");
  1623       // Handle held calls separately
  1624       if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_HELD)) {
  1625         UpdateCIND(CINDType::CALLHELD, CallHeldState::NO_CALLHELD, aSend);
  1626       } else if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
  1627         UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_NOACTIVE, aSend);
  1628       } else {
  1629         UpdateCIND(CINDType::CALLHELD, CallHeldState::ONHOLD_ACTIVE, aSend);
  1632       // -1 is necessary because call 0 is an invalid (padding) call object.
  1633       if (mCurrentCallArray.Length() - 1 ==
  1634           GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_DISCONNECTED)) {
  1635         // In order to let user hear busy tone via connected Bluetooth headset,
  1636         // we postpone the timing of dropping SCO.
  1637         if (!(aError.Equals(NS_LITERAL_STRING("BusyError")))) {
  1638           DisconnectSco();
  1639         } else {
  1640           // Close Sco later since Dialer is still playing busy tone via HF.
  1641           MessageLoop::current()->PostDelayedTask(FROM_HERE,
  1642                                                   new CloseScoTask(),
  1643                                                   sBusyToneInterval);
  1646         ResetCallArray();
  1648       break;
  1649     default:
  1650       BT_WARNING("Not handling state changed");
  1651       break;
  1655 PhoneType
  1656 BluetoothHfpManager::GetPhoneType(const nsAString& aType)
  1658   // FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
  1659   if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
  1660       aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
  1661       aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
  1662       aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
  1663     return PhoneType::GSM;
  1664   } else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
  1665              aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
  1666              aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
  1667     return PhoneType::CDMA;
  1670   return PhoneType::NONE;
  1673 void
  1674 BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
  1676   MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
  1678   // Always regard second call as incoming call since v1.2 RIL
  1679   // doesn't support outgoing second call in CDMA.
  1680   mCdmaSecondCall.mDirection = true;
  1682   mCdmaSecondCall.mNumber = aNumber;
  1683   mCdmaSecondCall.mType = (aNumber[0] == '+') ? TOA_INTERNATIONAL :
  1684                                                 TOA_UNKNOWN;
  1686   SendCCWA(aNumber, mCdmaSecondCall.mType);
  1687   UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
  1690 void
  1691 BluetoothHfpManager::AnswerWaitingCall()
  1693   MOZ_ASSERT(NS_IsMainThread());
  1694   MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
  1696   // Pick up second call. First call is held now.
  1697   mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED;
  1698   UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
  1700   sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
  1701   SendCommand(RESPONSE_CIEV, CINDType::CALLHELD);
  1704 void
  1705 BluetoothHfpManager::IgnoreWaitingCall()
  1707   MOZ_ASSERT(NS_IsMainThread());
  1708   MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
  1710   mCdmaSecondCall.Reset();
  1711   UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
  1714 void
  1715 BluetoothHfpManager::ToggleCalls()
  1717   MOZ_ASSERT(NS_IsMainThread());
  1718   MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
  1720   // Toggle acitve and held calls
  1721   mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
  1722                              nsITelephonyProvider::CALL_STATE_HELD :
  1723                              nsITelephonyProvider::CALL_STATE_CONNECTED;
  1725 #endif // MOZ_B2G_RIL
  1727 void
  1728 BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
  1730   MOZ_ASSERT(aSocket);
  1731 #ifdef MOZ_B2G_RIL
  1732   MOZ_ASSERT(mListener);
  1733 #endif
  1735   // Success to create a SCO socket
  1736   if (aSocket == mScoSocket) {
  1737     OnScoConnectSuccess();
  1738     return;
  1741   /**
  1742    * If the created connection is an inbound connection, close another server
  1743    * socket because currently only one SLC is allowed. After that, we need to
  1744    * make sure that both server socket would be nulled out. As for outbound
  1745    * connections, we do nothing since sockets have been already handled in
  1746    * function Connect().
  1747    */
  1748   if (aSocket == mHandsfreeSocket) {
  1749     MOZ_ASSERT(!mSocket);
  1750     mIsHsp = false;
  1751     mHandsfreeSocket.swap(mSocket);
  1753     mHeadsetSocket->Disconnect();
  1754     mHeadsetSocket = nullptr;
  1755   } else if (aSocket == mHeadsetSocket) {
  1756     MOZ_ASSERT(!mSocket);
  1757     mIsHsp = true;
  1758     mHeadsetSocket.swap(mSocket);
  1760     mHandsfreeSocket->Disconnect();
  1761     mHandsfreeSocket = nullptr;
  1764 #ifdef MOZ_B2G_RIL
  1765   // Enumerate current calls
  1766   mListener->EnumerateCalls();
  1768   mFirstCKPD = true;
  1769 #endif
  1771   // Cache device path for NotifySettings() since we can't get socket address
  1772   // when a headset disconnect with us
  1773   mSocket->GetAddress(mDeviceAddress);
  1774   NotifyConnectionStatusChanged(
  1775     NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
  1777   ListenSco();
  1779   OnConnect(EmptyString());
  1782 void
  1783 BluetoothHfpManager::OnSocketConnectError(BluetoothSocket* aSocket)
  1785   // Failed to create a SCO socket
  1786   if (aSocket == mScoSocket) {
  1787     OnScoConnectError();
  1788     return;
  1791   mHandsfreeSocket = nullptr;
  1792   mHeadsetSocket = nullptr;
  1794   OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
  1797 void
  1798 BluetoothHfpManager::OnSocketDisconnect(BluetoothSocket* aSocket)
  1800   MOZ_ASSERT(aSocket);
  1802   if (aSocket == mScoSocket) {
  1803     // SCO socket is closed
  1804     OnScoDisconnect();
  1805     return;
  1808   if (aSocket != mSocket) {
  1809     // Do nothing when a listening server socket is closed.
  1810     return;
  1813   DisconnectSco();
  1815   NotifyConnectionStatusChanged(
  1816     NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
  1817   OnDisconnect(EmptyString());
  1819   Reset();
  1822 void
  1823 BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
  1825   // UpdateSdpRecord() is not called so this callback function should not
  1826   // be invoked.
  1827   MOZ_ASSUME_UNREACHABLE("UpdateSdpRecords() should be called somewhere");
  1830 void
  1831 BluetoothHfpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
  1832                                          const nsAString& aServiceUuid,
  1833                                          int aChannel)
  1835   MOZ_ASSERT(NS_IsMainThread());
  1836   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
  1838   BluetoothService* bs = BluetoothService::Get();
  1839   NS_ENSURE_TRUE_VOID(bs);
  1841   if (aChannel < 0) {
  1842     // If we can't find Handsfree server channel number on the remote device,
  1843     // try to create HSP connection instead.
  1844     nsString hspUuid;
  1845     BluetoothUuidHelper::GetString(BluetoothServiceClass::HEADSET, hspUuid);
  1847     if (aServiceUuid.Equals(hspUuid)) {
  1848       OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND));
  1849     } else if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress,
  1850                                                hspUuid, this))) {
  1851       OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
  1852     } else {
  1853       mIsHsp = true;
  1856     return;
  1859   MOZ_ASSERT(mSocket);
  1861   if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
  1862     OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
  1866 void
  1867 BluetoothHfpManager::OnScoConnectSuccess()
  1869   // For active connection request, we need to reply the DOMRequest
  1870   if (mScoRunnable) {
  1871     DispatchBluetoothReply(mScoRunnable,
  1872                            BluetoothValue(true), EmptyString());
  1873     mScoRunnable = nullptr;
  1876   NotifyConnectionStatusChanged(
  1877     NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
  1879   mScoSocketStatus = mScoSocket->GetConnectionStatus();
  1882 void
  1883 BluetoothHfpManager::OnScoConnectError()
  1885   if (mScoRunnable) {
  1886     NS_NAMED_LITERAL_STRING(replyError, "Failed to create SCO socket!");
  1887     DispatchBluetoothReply(mScoRunnable, BluetoothValue(), replyError);
  1889     mScoRunnable = nullptr;
  1892   ListenSco();
  1895 void
  1896 BluetoothHfpManager::OnScoDisconnect()
  1898   if (mScoSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
  1899     ListenSco();
  1900     NotifyConnectionStatusChanged(
  1901       NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
  1905 bool
  1906 BluetoothHfpManager::IsConnected()
  1908   if (mSocket) {
  1909     return mSocket->GetConnectionStatus() ==
  1910            SocketConnectionStatus::SOCKET_CONNECTED;
  1913   return false;
  1916 void
  1917 BluetoothHfpManager::GetAddress(nsAString& aDeviceAddress)
  1919   return mSocket->GetAddress(aDeviceAddress);
  1922 bool
  1923 BluetoothHfpManager::ConnectSco(BluetoothReplyRunnable* aRunnable)
  1925   MOZ_ASSERT(NS_IsMainThread());
  1927   NS_ENSURE_TRUE(!sInShutdown, false);
  1928   NS_ENSURE_TRUE(IsConnected(), false);
  1930   SocketConnectionStatus status = mScoSocket->GetConnectionStatus();
  1931   if (status == SocketConnectionStatus::SOCKET_CONNECTED ||
  1932       status == SocketConnectionStatus::SOCKET_CONNECTING ||
  1933       (mScoRunnable && (mScoRunnable != aRunnable))) {
  1934     BT_WARNING("SCO connection exists or is being established");
  1935     return false;
  1938   // If we are not using HSP, we have to make sure Service Level Connection
  1939   // established before we start to set up SCO (synchronous connection).
  1940   if (!mSlcConnected && !mIsHsp) {
  1941     mConnectScoRequest = true;
  1942     BT_WARNING("ConnectSco called before Service Level Connection established");
  1943     return false;
  1946   // Stop listening
  1947   mScoSocket->Disconnect();
  1949   mScoSocket->Connect(NS_ConvertUTF16toUTF8(mDeviceAddress), -1);
  1950   mScoSocketStatus = mScoSocket->GetConnectionStatus();
  1952   mScoRunnable = aRunnable;
  1953   return true;
  1956 bool
  1957 BluetoothHfpManager::DisconnectSco()
  1959   if (!IsScoConnected()) {
  1960     BT_WARNING("SCO has been already disconnected.");
  1961     return false;
  1964   mScoSocket->Disconnect();
  1965   return true;
  1968 bool
  1969 BluetoothHfpManager::ListenSco()
  1971   MOZ_ASSERT(NS_IsMainThread());
  1973   if (sInShutdown) {
  1974     BT_WARNING("ListenSco called while in shutdown!");
  1975     return false;
  1978   if (mScoSocket->GetConnectionStatus() ==
  1979       SocketConnectionStatus::SOCKET_LISTENING) {
  1980     BT_WARNING("SCO socket has been already listening");
  1981     return false;
  1984   mScoSocket->Disconnect();
  1986   if (!mScoSocket->Listen(-1)) {
  1987     BT_WARNING("Can't listen on SCO socket!");
  1988     return false;
  1991   mScoSocketStatus = mScoSocket->GetConnectionStatus();
  1992   return true;
  1995 bool
  1996 BluetoothHfpManager::IsScoConnected()
  1998   if (mScoSocket) {
  1999     return mScoSocket->GetConnectionStatus() ==
  2000            SocketConnectionStatus::SOCKET_CONNECTED;
  2002   return false;
  2005 void
  2006 BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
  2008   MOZ_ASSERT(NS_IsMainThread());
  2010   // When we failed to create a socket, restart listening.
  2011   if (!aErrorStr.IsEmpty()) {
  2012     mSocket = nullptr;
  2013     Listen();
  2016   /**
  2017    * On the one hand, notify the controller that we've done for outbound
  2018    * connections. On the other hand, we do nothing for inbound connections.
  2019    */
  2020   NS_ENSURE_TRUE_VOID(mController);
  2022   nsRefPtr<BluetoothProfileController> controller = mController.forget();
  2023   controller->NotifyCompletion(aErrorStr);
  2026 void
  2027 BluetoothHfpManager::OnDisconnect(const nsAString& aErrorStr)
  2029   MOZ_ASSERT(NS_IsMainThread());
  2031   // Start listening
  2032   mSocket = nullptr;
  2033   Listen();
  2035   /**
  2036    * On the one hand, notify the controller that we've done for outbound
  2037    * connections. On the other hand, we do nothing for inbound connections.
  2038    */
  2039   NS_ENSURE_TRUE_VOID(mController);
  2041   nsRefPtr<BluetoothProfileController> controller = mController.forget();
  2042   controller->NotifyCompletion(aErrorStr);
  2045 NS_IMPL_ISUPPORTS(BluetoothHfpManager, nsIObserver)

mercurial