Merge birch to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 30 Jul 2013 09:01:43 -0400
changeset 152685 dbd7d55d64ffb24fef1b83ac44477cb91957398c
parent 152666 8911e64ad234b3519e2aa52b4144db67631b0ea9 (current diff)
parent 152684 7476377d143fb6508fd8d3108f0a35c4d9da4e00 (diff)
child 152786 129ce98f4cb24f84d8d18d2563aaade4facb2a76
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge birch to m-c.
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -4,64 +4,125 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This JS shim contains the callbacks to fire DOMRequest events for
 // navigator.pay API within the payment processor's scope.
 
 "use strict";
 
-dump("======================= payment.js ======================= \n");
+let _DEBUG = false;
+function _debug(s) { dump("== Payment flow == " + s + "\n"); }
+_debug("Frame script injected");
 
 let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 #ifdef MOZ_B2G_RIL
-XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
+XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
                                    "@mozilla.org/ril/content-helper;1",
-                                   "nsIMobileConnectionProvider");
+                                   "nsIIccProvider");
+
+XPCOMUtils.defineLazyServiceGetter(this, "smsService",
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
+const kSilentSmsReceivedTopic = "silent-sms-received";
+
+const MOBILEMESSAGECALLBACK_CID =
+  Components.ID("{b484d8c9-6be4-4f94-ab60-c9c7ebcc853d}");
+
+// In order to send messages through nsISmsService, we need to implement
+// nsIMobileMessageCallback, as the WebSMS API implementation is not usable
+// from JS.
+function SilentSmsRequest() {
+}
+SilentSmsRequest.prototype = {
+  __exposedProps__: {
+    onsuccess: 'rw',
+    onerror: 'rw'
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
+
+  classID: MOBILEMESSAGECALLBACK_CID,
+
+  set onsuccess(aSuccessCallback) {
+    this._onsuccess = aSuccessCallback;
+  },
+
+  set onerror(aErrorCallback) {
+    this._onerror = aErrorCallback;
+  },
+
+  notifyMessageSent: function notifyMessageSent(aMessage) {
+    if (_DEBUG) {
+      _debug("Silent message successfully sent");
+    }
+    this._onsuccess(aMessage);
+  },
+
+  notifySendMessageFailed: function notifySendMessageFailed(aError) {
+    if (_DEBUG) {
+      _debug("Error sending silent message " + aError);
+    }
+    this._onerror(aError);
+  }
+};
 #endif
 
-
 const kClosePaymentFlowEvent = "close-payment-flow-dialog";
 
-let _requestId;
+let gRequestId;
+
+let gBrowser = Services.wm.getMostRecentWindow("navigator:browser");
 
 let PaymentProvider = {
-
+#ifdef MOZ_B2G_RIL
   __exposedProps__: {
     paymentSuccess: 'r',
     paymentFailed: 'r',
-    iccIds: 'r'
+    iccIds: 'r',
+    mcc: 'r',
+    mnc: 'r',
+    sendSilentSms: 'r',
+    observeSilentSms: 'r',
+    removeSilentSmsObserver: 'r'
   },
+#else
+  __exposedProps__: {
+    paymentSuccess: 'r',
+    paymentFailed: 'r'
+  },
+#endif
 
   _closePaymentFlowDialog: function _closePaymentFlowDialog(aCallback) {
     // After receiving the payment provider confirmation about the
     // successful or failed payment flow, we notify the UI to close the
     // payment flow dialog and return to the caller application.
     let id = kClosePaymentFlowEvent + "-" + uuidgen.generateUUID().toString();
 
-    let browser = Services.wm.getMostRecentWindow("navigator:browser");
-    let content = browser.getContentWindow();
+    let content = gBrowser.getContentWindow();
     if (!content) {
       return;
     }
 
     let detail = {
       type: kClosePaymentFlowEvent,
       id: id,
-      requestId: _requestId
+      requestId: gRequestId
     };
 
     // In order to avoid race conditions, we wait for the UI to notify that
     // it has successfully closed the payment flow and has recovered the
     // caller app, before notifying the parent process to fire the success
     // or error event over the DOMRequest.
     content.addEventListener("mozContentEvent",
                              function closePaymentFlowReturn(evt) {
@@ -72,54 +133,181 @@ let PaymentProvider = {
       content.removeEventListener("mozContentEvent",
                                   closePaymentFlowReturn);
 
       let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                    .createInstance(Ci.nsIPaymentUIGlue);
       glue.cleanup();
     });
 
-    browser.shell.sendChromeEvent(detail);
+    gBrowser.shell.sendChromeEvent(detail);
+
+#ifdef MOZ_B2G_RIL
+    this._cleanUp();
+#endif
   },
 
   paymentSuccess: function paymentSuccess(aResult) {
+    if (_DEBUG) {
+      _debug("paymentSuccess " + aResult);
+    }
+
     PaymentProvider._closePaymentFlowDialog(function notifySuccess() {
-      if (!_requestId) {
+      if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
-                                                 requestId: _requestId });
+                                                 requestId: gRequestId });
     });
   },
 
   paymentFailed: function paymentFailed(aErrorMsg) {
+    if (_DEBUG) {
+      _debug("paymentFailed " + aErrorMsg);
+    }
+
     PaymentProvider._closePaymentFlowDialog(function notifyError() {
-      if (!_requestId) {
+      if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
-                                                requestId: _requestId });
+                                                requestId: gRequestId });
     });
   },
 
+#ifdef MOZ_B2G_RIL
+  // Until bug 814629 is done, we only have support for a single SIM, so we
+  // can only provide information for a single ICC. However, we return an array
+  // so the payment provider facing API won't need to change once we support
+  // multiple SIMs.
+
   get iccIds() {
-#ifdef MOZ_B2G_RIL
-    // Until bug 814629 is done, we only have support for a single SIM, so we
-    // can only provide a single ICC ID. However, we return an array so the
-    // payment provider facing API won't need to change once we support
-    // multiple SIMs.
-    return [mobileConnection.iccInfo.iccid];
-#else
-    return null;
-#endif
+    return [iccProvider.iccInfo.iccid];
+  },
+
+  get mcc() {
+    return [iccProvider.iccInfo.mcc];
+  },
+
+  get mnc() {
+    return [iccProvider.iccInfo.mnc];
+  },
+
+  _silentNumbers: null,
+  _silentSmsObservers: null,
+
+  sendSilentSms: function sendSilentSms(aNumber, aMessage) {
+    if (_DEBUG) {
+      _debug("Sending silent message " + aNumber + " - " + aMessage);
+    }
+
+    let request = new SilentSmsRequest();
+    smsService.send(aNumber, aMessage, true, request);
+    return request;
+  },
+
+  observeSilentSms: function observeSilentSms(aNumber, aCallback) {
+    if (_DEBUG) {
+      _debug("observeSilentSms " + aNumber);
+    }
+
+    if (!this._silentSmsObservers) {
+      this._silentSmsObservers = {};
+      this._silentNumbers = [];
+      Services.obs.addObserver(this._onSilentSms.bind(this),
+                               kSilentSmsReceivedTopic,
+                               false);
+    }
+
+    if (!this._silentSmsObservers[aNumber]) {
+      this._silentSmsObservers[aNumber] = [];
+      this._silentNumbers.push(aNumber);
+      smsService.addSilentNumber(aNumber);
+    }
+
+    if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
+      this._silentSmsObservers[aNumber].push(aCallback);
+    }
   },
 
+  removeSilentSmsObserver: function removeSilentSmsObserver(aNumber, aCallback) {
+    if (_DEBUG) {
+      _debug("removeSilentSmsObserver " + aNumber);
+    }
+
+    if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
+      if (_DEBUG) {
+        _debug("No observers for " + aNumber);
+      }
+      return;
+    }
+
+    let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
+    if (index != -1) {
+      this._silentSmsObservers[aNumber].splice(index, 1);
+      if (this._silentSmsObservers[aNumber].length == 0) {
+        this._silentSmsObservers[aNumber] = null;
+        this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
+        smsService.removeSilentNumber(aNumber);
+      }
+    } else if (_DEBUG) {
+      _debug("No callback found for " + aNumber);
+    }
+  },
+
+  _onSilentSms: function _onSilentSms(aSubject, aTopic, aData) {
+    if (_DEBUG) {
+      _debug("Got silent message! " + aSubject.sender + " - " + aSubject.body);
+    }
+
+    let number = aSubject.sender;
+    if (!number || this._silentNumbers.indexOf(number) == -1) {
+      if (_DEBUG) {
+        _debug("No observers for " + number);
+      }
+      return;
+    }
+
+    this._silentSmsObservers[number].forEach(function(callback) {
+      callback(aSubject);
+    });
+  },
+
+  _cleanUp: function _cleanUp() {
+    if (_DEBUG) {
+      _debug("Cleaning up!");
+    }
+
+    if (!this._silentNumbers) {
+      return;
+    }
+
+    while (this._silentNumbers.length) {
+      let number = this._silentNumbers.pop();
+      smsService.removeSilentNumber(number);
+    }
+    this._silentNumbers = null;
+    this._silentSmsObservers = null;
+    Services.obs.removeObserver(this._onSilentSms, kSilentSmsReceivedTopic);
+  }
+#endif
 };
 
 // We save the identifier of the DOM request, so we can dispatch the results
 // of the payment flow to the appropriate content process.
 addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
-  _requestId = aMessage.json.requestId;
+  gRequestId = aMessage.json.requestId;
 });
 
 addEventListener("DOMWindowCreated", function(e) {
   content.wrappedJSObject.mozPaymentProvider = PaymentProvider;
 });
+
+#ifdef MOZ_B2G_RIL
+// If the trusted dialog is not closed via paymentSuccess or paymentFailed
+// a mozContentEvent with type 'cancel' is sent from the UI. We need to listen
+// for this event to clean up the silent sms observers if any exists.
+gBrowser.getContentWindow().addEventListener("mozContentEvent", function(e) {
+  if (e.detail.type === "cancel") {
+    PaymentProvider._cleanUp();
+  }
+});
+#endif
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "74877b52dcb397d6de83b682ac5d374cc6d508be", 
+    "revision": "10f16f6a15f8a1c4b910548b4d0968baaac21c91", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/bluetooth/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/BluetoothA2dpManager.cpp
@@ -200,17 +200,16 @@ BluetoothA2dpManager::HandleSinkProperty
 
   const nsString& name = arr[0].name();
   const BluetoothValue& value = arr[0].value();
   if (name.EqualsLiteral("Connected")) {
     // Indicates if a stream is setup to a A2DP sink on the remote device.
     MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
     mA2dpConnected = value.get_bool();
     NotifyStatusChanged();
-    NotifyAudioManager();
   } else if (name.EqualsLiteral("Playing")) {
     // Indicates if a stream is active to a A2DP sink on the remote device.
     MOZ_ASSERT(value.type() == BluetoothValue::Tbool);
     mPlaying = value.get_bool();
   } else if (name.EqualsLiteral("State")) {
     MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
     HandleSinkStateChanged(StatusStringToSinkState(value.get_nsString()));
   } else {
@@ -254,48 +253,40 @@ BluetoothA2dpManager::HandleSinkStateCha
   mSinkState = aState;
 }
 
 void
 BluetoothA2dpManager::NotifyStatusChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  // Broadcast system message to Gaia
   NS_NAMED_LITERAL_STRING(type, BLUETOOTH_A2DP_STATUS_CHANGED_ID);
   InfallibleTArray<BluetoothNamedValue> parameters;
 
   BluetoothValue v = mA2dpConnected;
   parameters.AppendElement(
     BluetoothNamedValue(NS_LITERAL_STRING("connected"), v));
 
   v = mDeviceAddress;
   parameters.AppendElement(
     BluetoothNamedValue(NS_LITERAL_STRING("address"), v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to settings");
-    return;
   }
-}
 
-void
-BluetoothA2dpManager::NotifyAudioManager()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
+  // Notify Gecko observers
   nsCOMPtr<nsIObserverService> obs =
     do_GetService("@mozilla.org/observer-service;1");
   NS_ENSURE_TRUE_VOID(obs);
 
-  nsAutoString data;
-  data.AppendInt(mA2dpConnected);
-
   if (NS_FAILED(obs->NotifyObservers(this,
                                      BLUETOOTH_A2DP_STATUS_CHANGED_ID,
-                                     data.BeginReading()))) {
+                                     mDeviceAddress.get()))) {
     NS_WARNING("Failed to notify bluetooth-a2dp-status-changed observsers!");
   }
 }
 
 void
 BluetoothA2dpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
                                           const nsAString& aServiceUuid,
                                           int aChannel)
--- a/dom/bluetooth/BluetoothA2dpManager.h
+++ b/dom/bluetooth/BluetoothA2dpManager.h
@@ -68,23 +68,21 @@ public:
 private:
   BluetoothA2dpManager();
   bool Init();
 
   void HandleSinkStateChanged(SinkState aState);
   void HandleShutdown();
 
   void NotifyStatusChanged();
-  void NotifyAudioManager();
-
-  nsString mDeviceAddress;
 
   // A2DP data member
   bool mA2dpConnected;
   bool mPlaying;
+  nsString mDeviceAddress;
   SinkState mSinkState;
 
   // AVRCP data member
   bool mAvrcpConnected;
   nsString mAlbum;
   nsString mArtist;
   nsString mTitle;
   uint32_t mDuration;
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -50,16 +50,17 @@ extern bool gBluetoothDebugFlag;
 #define KEY_REMOTE_AGENT "/B2G/bluetooth/remote_device_agent"
 #define KEY_MANAGER      "/B2G/bluetooth/manager"
 #define KEY_ADAPTER      "/B2G/bluetooth/adapter"
 
 /**
  * When connection status of Bluetooth profiles change, we'll notify observers
  * of following topics.
  */
+#define BLUETOOTH_HFP_STATUS_CHANGED_ID      "bluetooth-hfp-status-changed"
 #define BLUETOOTH_SCO_STATUS_CHANGED_ID      "bluetooth-sco-status-changed"
 #define BLUETOOTH_A2DP_STATUS_CHANGED_ID     "bluetooth-a2dp-status-changed"
 
 // Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
 #define BLUETOOTH_ADDRESS_LENGTH 17
 #define BLUETOOTH_ADDRESS_NONE   "00:00:00:00:00:00"
 
 BEGIN_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -449,41 +449,50 @@ BluetoothHfpManager::Get()
   BluetoothHfpManager* manager = new BluetoothHfpManager();
   NS_ENSURE_TRUE(manager->Init(), nullptr);
 
   sBluetoothHfpManager = manager;
   return sBluetoothHfpManager;
 }
 
 void
-BluetoothHfpManager::NotifyStatusChanged(const nsAString& aType)
+BluetoothHfpManager::NotifyStatusChanged(const char* aType)
 {
-  nsString type, name;
+  // Broadcast system message to Gaia
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
-  type = aType;
-
+  nsAutoString type, name;
+  type = NS_ConvertUTF8toUTF16(aType);
   name.AssignLiteral("connected");
-  if (type.EqualsLiteral("bluetooth-hfp-status-changed")) {
+
+  if (type.EqualsLiteral(BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
     v = IsConnected();
-  } else if (type.EqualsLiteral("bluetooth-sco-status-changed")) {
+  } else if (type.EqualsLiteral(BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
     v = IsScoConnected();
   } else {
     NS_WARNING("Wrong type for NotifyStatusChanged");
     return;
   }
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("address");
   v = mDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to settings");
-    return;
+  }
+
+  // Notify Gecko observers
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService("@mozilla.org/observer-service;1");
+  NS_ENSURE_TRUE_VOID(obs);
+
+  if (NS_FAILED(obs->NotifyObservers(this, aType, mDeviceAddress.get()))) {
+    NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
   }
 }
 
 void
 BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
 {
   nsString type, name;
   BluetoothValue v;
@@ -491,36 +500,16 @@ BluetoothHfpManager::NotifyDialer(const 
   type.AssignLiteral("bluetooth-dialer-command");
 
   name.AssignLiteral("command");
   v = nsString(aCommand);
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to dialer");
-    return;
-  }
-}
-
-void
-BluetoothHfpManager::NotifyAudioManager(bool aStatus)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsCOMPtr<nsIObserverService> obs =
-    do_GetService("@mozilla.org/observer-service;1");
-  NS_ENSURE_TRUE_VOID(obs);
-
-  nsAutoString data;
-  data.AppendInt(aStatus);
-
-  if (NS_FAILED(obs->NotifyObservers(this,
-                                     BLUETOOTH_SCO_STATUS_CHANGED_ID,
-                                     data.BeginReading()))) {
-    NS_WARNING("Failed to notify bluetooth-sco-status-changed observsers!");
   }
 }
 
 void
 BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -1488,17 +1477,17 @@ BluetoothHfpManager::OnConnectSuccess(Bl
     mRunnable = nullptr;
   }
 
   mFirstCKPD = true;
 
   // Cache device path for NotifySettings() since we can't get socket address
   // when a headset disconnect with us
   mSocket->GetAddress(mDeviceAddress);
-  NotifyStatusChanged(NS_LITERAL_STRING("bluetooth-hfp-status-changed"));
+  NotifyStatusChanged(BLUETOOTH_HFP_STATUS_CHANGED_ID);
 
   ListenSco();
 
   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   NS_ENSURE_TRUE_VOID(a2dp);
   a2dp->Connect(mDeviceAddress);
 }
 
@@ -1543,17 +1532,17 @@ BluetoothHfpManager::OnDisconnect(Blueto
     // Do nothing when a listening server socket is closed.
     return;
   }
 
   mSocket = nullptr;
   DisconnectSco();
 
   Listen();
-  NotifyStatusChanged(NS_LITERAL_STRING("bluetooth-hfp-status-changed"));
+  NotifyStatusChanged(BLUETOOTH_HFP_STATUS_CHANGED_ID);
   Reset();
 }
 
 void
 BluetoothHfpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
@@ -1623,18 +1612,17 @@ BluetoothHfpManager::OnScoConnectSuccess
 {
   // For active connection request, we need to reply the DOMRequest
   if (mScoRunnable) {
     DispatchBluetoothReply(mScoRunnable,
                            BluetoothValue(true), EmptyString());
     mScoRunnable = nullptr;
   }
 
-  NotifyAudioManager(true);
-  NotifyStatusChanged(NS_LITERAL_STRING("bluetooth-sco-status-changed"));
+  NotifyStatusChanged(BLUETOOTH_SCO_STATUS_CHANGED_ID);
 
   mScoSocketStatus = mScoSocket->GetConnectionStatus();
 }
 
 void
 BluetoothHfpManager::OnScoConnectError()
 {
   if (mScoRunnable) {
@@ -1647,18 +1635,17 @@ BluetoothHfpManager::OnScoConnectError()
   ListenSco();
 }
 
 void
 BluetoothHfpManager::OnScoDisconnect()
 {
   if (mScoSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
     ListenSco();
-    NotifyAudioManager(false);
-    NotifyStatusChanged(NS_LITERAL_STRING("bluetooth-sco-status-changed"));
+    NotifyStatusChanged(BLUETOOTH_SCO_STATUS_CHANGED_ID);
   }
 }
 
 bool
 BluetoothHfpManager::IsConnected()
 {
   if (mSocket) {
     return mSocket->GetConnectionStatus() ==
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -113,18 +113,17 @@ private:
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
   void Reset();
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
 
   void NotifyDialer(const nsAString& aCommand);
-  void NotifyStatusChanged(const nsAString& aType);
-  void NotifyAudioManager(bool aStatus);
+  void NotifyStatusChanged(const char* aType);
 
   bool SendCommand(const char* aCommand, uint32_t aValue = 0);
   bool SendLine(const char* aMessage);
   void UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend = true);
   void OnScoConnectSuccess();
   void OnScoConnectError();
   void OnScoDisconnect();
 
--- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
@@ -8,17 +8,17 @@ dictionary SmsThreadListItem
 {
   unsigned long long id;
   DOMString senderOrReceiver;
   unsigned long long timestamp;
   DOMString body;
   unsigned long long unreadCount;
 };
 
-[scriptable, builtinclass, uuid(a22d9aae-ee0a-11e2-949e-e770d0d3883f)]
+[scriptable, uuid(ea5fb581-bee7-40a6-b2dc-c98b99a2dc49)]
 interface nsIMobileMessageCallback : nsISupports
 {
   /**
    * All SMS related errors.
    * Make sure to keep this list in sync with the list in:
    * embedding/android/GeckoSmsManager.java
    */
   const unsigned short SUCCESS_NO_ERROR          = 0;
--- a/dom/mobilemessage/interfaces/nsISmsService.idl
+++ b/dom/mobilemessage/interfaces/nsISmsService.idl
@@ -8,19 +8,24 @@ interface nsIDOMMozSmsMessage;
 interface nsIDOMMozSmsSegmentInfo;
 interface nsIMobileMessageCallback;
 
 %{C++
 #define SMS_SERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } }
 #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
 %}
 
-[scriptable, builtinclass, uuid(c4b2ed2a-8714-11e2-bd2b-13f1a0759342)]
+[scriptable, builtinclass, uuid(f0d5d11b-0326-4cb1-bb76-a3f912212287)]
 interface nsISmsService : nsISupports
 {
   boolean hasSupport();
 
   nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
 
   void send(in DOMString number,
             in DOMString message,
+            in boolean silent,
             in nsIMobileMessageCallback request);
+
+  boolean isSilentNumber(in DOMString number);
+  void addSilentNumber(in DOMString number);
+  void removeSilentNumber(in DOMString number);
 };
--- a/dom/mobilemessage/src/Constants.cpp
+++ b/dom/mobilemessage/src/Constants.cpp
@@ -9,12 +9,13 @@ namespace mobilemessage {
 
 const char* kSmsReceivedObserverTopic        = "sms-received";
 const char* kSmsRetrievingObserverTopic      = "sms-retrieving";
 const char* kSmsSendingObserverTopic         = "sms-sending";
 const char* kSmsSentObserverTopic            = "sms-sent";
 const char* kSmsFailedObserverTopic          = "sms-failed";
 const char* kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
 const char* kSmsDeliveryErrorObserverTopic   = "sms-delivery-error";
+const char* kSilentSmsReceivedObserverTopic  = "silent-sms-received";
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/src/Constants.h
+++ b/dom/mobilemessage/src/Constants.h
@@ -13,21 +13,17 @@ namespace mobilemessage {
 // Defined in the .cpp.
 extern const char* kSmsReceivedObserverTopic;
 extern const char* kSmsRetrievingObserverTopic;
 extern const char* kSmsSendingObserverTopic;
 extern const char* kSmsSentObserverTopic;
 extern const char* kSmsFailedObserverTopic;
 extern const char* kSmsDeliverySuccessObserverTopic;
 extern const char* kSmsDeliveryErrorObserverTopic;
-
-extern const char* kMmsSendingObserverTopic;
-extern const char* kMmsSentObserverTopic;
-extern const char* kMmsFailedObserverTopic;
-extern const char* kMmsReceivedObserverTopic;
+extern const char* kSilentSmsReceivedObserverTopic;
 
 #define DELIVERY_RECEIVED       NS_LITERAL_STRING("received")
 #define DELIVERY_SENDING        NS_LITERAL_STRING("sending")
 #define DELIVERY_SENT           NS_LITERAL_STRING("sent")
 #define DELIVERY_ERROR          NS_LITERAL_STRING("error")
 #define DELIVERY_NOT_DOWNLOADED NS_LITERAL_STRING("not-downloaded")
 
 #define DELIVERY_STATUS_NOT_APPLICABLE NS_LITERAL_STRING("not-applicable")
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -119,17 +119,18 @@ MobileMessageManager::Send(JSContext* aC
 
   nsDependentJSString number;
   number.init(aCx, aNumber);
 
   nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
   nsCOMPtr<nsIMobileMessageCallback> msgCallback =
     new MobileMessageCallback(request);
 
-  nsresult rv = smsService->Send(number, aMessage, msgCallback);
+  // By default, we don't send silent messages via MobileMessageManager.
+  nsresult rv = smsService->Send(number, aMessage, false, msgCallback);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JSObject*> global(aCx, aGlobal);
   rv = nsContentUtils::WrapNative(aCx, global,
                                   static_cast<nsIDOMDOMRequest*>(request.get()),
                                   aRequest);
   if (NS_FAILED(rv)) {
     NS_ERROR("Failed to create the js value!");
--- a/dom/mobilemessage/src/android/SmsService.cpp
+++ b/dom/mobilemessage/src/android/SmsService.cpp
@@ -35,22 +35,46 @@ SmsService::GetSegmentInfoForText(const 
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
   info.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SmsService::Send(const nsAString& aNumber, const nsAString& aMessage,
+SmsService::Send(const nsAString& aNumber,
+                 const nsAString& aMessage,
+                 const bool       aSilent,
                  nsIMobileMessageCallback* aRequest)
 {
   if (!AndroidBridge::Bridge()) {
     return NS_OK;
   }
 
   AndroidBridge::Bridge()->SendMessage(aNumber, aMessage, aRequest);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsService::IsSilentNumber(const nsAString& aNumber,
+                           bool*            aIsSilent)
+{
+  NS_NOTYETIMPLEMENTED("Implement me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SmsService::AddSilentNumber(const nsAString& aNumber)
+{
+  NS_NOTYETIMPLEMENTED("Implement me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SmsService::RemoveSilentNumber(const nsAString& aNumber)
+{
+  NS_NOTYETIMPLEMENTED("Implement me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/src/fallback/SmsService.cpp
+++ b/dom/mobilemessage/src/fallback/SmsService.cpp
@@ -27,17 +27,40 @@ SmsService::GetSegmentInfoForText(const 
 {
   NS_ERROR("We should not be here!");
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 SmsService::Send(const nsAString& aNumber,
                  const nsAString& aMessage,
+                 const bool       aSilent,
                  nsIMobileMessageCallback* aRequest)
 {
   NS_ERROR("We should not be here!");
-  return NS_OK;
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SmsService::IsSilentNumber(const nsAString& aNumber,
+                           bool*            aIsSilent)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SmsService::AddSilentNumber(const nsAString& aNumber)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SmsService::RemoveSilentNumber(const nsAString& aNumber)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
 }
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/src/gonk/SmsService.cpp
+++ b/dom/mobilemessage/src/gonk/SmsService.cpp
@@ -37,18 +37,49 @@ SmsService::GetSegmentInfoForText(const 
   NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
 
   return mRadioInterface->GetSegmentInfoForText(aText, aResult);
 }
 
 NS_IMETHODIMP
 SmsService::Send(const nsAString& aNumber,
                  const nsAString& aMessage,
+                 const bool       aSilent,
                  nsIMobileMessageCallback* aRequest)
 {
   NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE);
 
-  return mRadioInterface->SendSMS(aNumber, aMessage, aRequest);
+  return mRadioInterface->SendSMS(aNumber, aMessage, aSilent, aRequest);
+}
+
+NS_IMETHODIMP
+SmsService::IsSilentNumber(const nsAString& aNumber,
+                           bool*            aIsSilent)
+{
+  *aIsSilent = mSilentNumbers.Contains(aNumber);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsService::AddSilentNumber(const nsAString& aNumber)
+{
+  if (mSilentNumbers.Contains(aNumber)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_ENSURE_TRUE(mSilentNumbers.AppendElement(aNumber), NS_ERROR_FAILURE);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsService::RemoveSilentNumber(const nsAString& aNumber)
+{
+  if (!mSilentNumbers.Contains(aNumber)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  NS_ENSURE_TRUE(mSilentNumbers.RemoveElement(aNumber), NS_ERROR_FAILURE);
+  return NS_OK;
 }
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/src/gonk/SmsService.h
+++ b/dom/mobilemessage/src/gonk/SmsService.h
@@ -3,30 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_mobilemessage_SmsService_h
 #define mozilla_dom_mobilemessage_SmsService_h
 
 #include "nsISmsService.h"
 #include "nsCOMPtr.h"
 #include "nsIRadioInterfaceLayer.h"
+#include "nsTArray.h"
+#include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 class SmsService : public nsISmsService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISMSSERVICE
   SmsService();
 
 protected:
   // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS
   nsCOMPtr<nsIRadioInterface> mRadioInterface;
+  nsTArray<nsString> mSilentNumbers;
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_SmsService_h
--- a/dom/mobilemessage/src/ipc/PSms.ipdl
+++ b/dom/mobilemessage/src/ipc/PSms.ipdl
@@ -21,16 +21,17 @@ struct SendMmsMessageRequest
   nsString smil;
   MmsAttachmentData[] attachments;
 };
 
 struct SendSmsMessageRequest
 {
   nsString number;
   nsString message;
+  bool     silent;
 };
 
 union SendMessageRequest
 {
   SendMmsMessageRequest;
   SendSmsMessageRequest;
 };
 
@@ -95,16 +96,18 @@ child:
   NotifySentMessage(MobileMessageData aMessageData);
 
   NotifyFailedMessage(MobileMessageData aMessageData);
 
   NotifyDeliverySuccessMessage(MobileMessageData aMessageData);
 
   NotifyDeliveryErrorMessage(MobileMessageData aMessageData);
 
+  NotifyReceivedSilentMessage(MobileMessageData aMessageData);
+
 parent:
   /**
    * Sent when the child no longer needs to use sms.
    */
   __delete__();
 
   /**
    * Sent when the child makes an asynchronous request to the parent.
@@ -116,13 +119,16 @@ parent:
    */
   PMobileMessageCursor(IPCMobileMessageCursor request);
 
   sync HasSupport()
       returns (bool aHasSupport);
 
   sync GetSegmentInfoForText(nsString aText)
       returns (SmsSegmentInfoData aResult);
+
+  AddSilentNumber(nsString aNumber);
+  RemoveSilentNumber(nsString aNumber);
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/src/ipc/SmsChild.cpp
+++ b/dom/mobilemessage/src/ipc/SmsChild.cpp
@@ -104,16 +104,23 @@ SmsChild::RecvNotifyDeliverySuccessMessa
 
 bool
 SmsChild::RecvNotifyDeliveryErrorMessage(const MobileMessageData& aData)
 {
   NotifyObserversWithMobileMessage(kSmsDeliveryErrorObserverTopic, aData);
   return true;
 }
 
+bool
+SmsChild::RecvNotifyReceivedSilentMessage(const MobileMessageData& aData)
+{
+  NotifyObserversWithMobileMessage(kSilentSmsReceivedObserverTopic, aData);
+  return true;
+}
+
 PSmsRequestChild*
 SmsChild::AllocPSmsRequestChild(const IPCSmsRequest& aRequest)
 {
   MOZ_CRASH("Caller is supposed to manually construct a request!");
 }
 
 bool
 SmsChild::DeallocPSmsRequestChild(PSmsRequestChild* aActor)
--- a/dom/mobilemessage/src/ipc/SmsChild.h
+++ b/dom/mobilemessage/src/ipc/SmsChild.h
@@ -50,16 +50,19 @@ protected:
   RecvNotifyFailedMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyDeliverySuccessMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyDeliveryErrorMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
 
+  virtual bool
+  RecvNotifyReceivedSilentMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
+
   virtual PSmsRequestChild*
   AllocPSmsRequestChild(const IPCSmsRequest& aRequest) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPSmsRequestChild(PSmsRequestChild* aActor) MOZ_OVERRIDE;
 
   virtual PMobileMessageCursorChild*
   AllocPMobileMessageCursorChild(const IPCMobileMessageCursor& aCursor) MOZ_OVERRIDE;
--- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
@@ -110,23 +110,53 @@ SmsIPCService::GetSegmentInfoForText(con
   nsCOMPtr<nsIDOMMozSmsSegmentInfo> info = new SmsSegmentInfo(data);
   info.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SmsIPCService::Send(const nsAString& aNumber,
                     const nsAString& aMessage,
+                    const bool aSilent,
                     nsIMobileMessageCallback* aRequest)
 {
   return SendRequest(SendMessageRequest(SendSmsMessageRequest(nsString(aNumber),
-                                                              nsString(aMessage))),
+                                                              nsString(aMessage),
+                                                              aSilent)),
                      aRequest);
 }
 
+NS_IMETHODIMP
+SmsIPCService::IsSilentNumber(const nsAString& aNumber,
+                              bool*            aIsSilent)
+{
+  NS_ERROR("We should not be here!");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SmsIPCService::AddSilentNumber(const nsAString& aNumber)
+{
+  PSmsChild* smsChild = GetSmsChild();
+  NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE);
+
+  smsChild->SendAddSilentNumber(nsString(aNumber));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsIPCService::RemoveSilentNumber(const nsAString& aNumber)
+{
+  PSmsChild* smsChild = GetSmsChild();
+  NS_ENSURE_TRUE(smsChild, NS_ERROR_FAILURE);
+
+  smsChild->SendRemoveSilentNumber(nsString(aNumber));
+  return NS_OK;
+}
+
 /*
  * Implementation of nsIMobileMessageDatabaseService.
  */
 NS_IMETHODIMP
 SmsIPCService::GetMessageMoz(int32_t aMessageId,
                              nsIMobileMessageCallback* aRequest)
 {
   return SendRequest(GetMessageRequest(aMessageId), aRequest);
--- a/dom/mobilemessage/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -146,16 +146,17 @@ SmsParent::SmsParent()
 
   obs->AddObserver(this, kSmsReceivedObserverTopic, false);
   obs->AddObserver(this, kSmsRetrievingObserverTopic, false);
   obs->AddObserver(this, kSmsSendingObserverTopic, false);
   obs->AddObserver(this, kSmsSentObserverTopic, false);
   obs->AddObserver(this, kSmsFailedObserverTopic, false);
   obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false);
   obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
+  obs->AddObserver(this, kSilentSmsReceivedObserverTopic, false);
 }
 
 void
 SmsParent::ActorDestroy(ActorDestroyReason why)
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return;
@@ -163,16 +164,17 @@ SmsParent::ActorDestroy(ActorDestroyReas
 
   obs->RemoveObserver(this, kSmsReceivedObserverTopic);
   obs->RemoveObserver(this, kSmsRetrievingObserverTopic);
   obs->RemoveObserver(this, kSmsSendingObserverTopic);
   obs->RemoveObserver(this, kSmsSentObserverTopic);
   obs->RemoveObserver(this, kSmsFailedObserverTopic);
   obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic);
   obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
+  obs->RemoveObserver(this, kSilentSmsReceivedObserverTopic);
 }
 
 NS_IMETHODIMP
 SmsParent::Observe(nsISupports* aSubject, const char* aTopic,
                    const PRUnichar* aData)
 {
   if (!strcmp(aTopic, kSmsReceivedObserverTopic)) {
     MobileMessageData msgData;
@@ -246,16 +248,34 @@ SmsParent::Observe(nsISupports* aSubject
       NS_ERROR("Got a 'sms-delivery-error' topic without a valid message!");
       return NS_OK;
     }
 
     unused << SendNotifyDeliveryErrorMessage(msgData);
     return NS_OK;
   }
 
+  if (!strcmp(aTopic, kSilentSmsReceivedObserverTopic)) {
+    nsCOMPtr<nsIDOMMozSmsMessage> smsMsg = do_QueryInterface(aSubject);
+    if (!smsMsg) {
+      return NS_OK;
+    }
+
+    nsString sender;
+    if (NS_FAILED(smsMsg->GetSender(sender)) ||
+        !mSilentNumbers.Contains(sender)) {
+      return NS_OK;
+    }
+
+    MobileMessageData msgData =
+      static_cast<SmsMessage*>(smsMsg.get())->GetData();
+    unused << SendNotifyReceivedSilentMessage(msgData);
+    return NS_OK;
+  }
+
   return NS_OK;
 }
 
 bool
 SmsParent::GetMobileMessageDataFromMessage(nsISupports *aMsg,
                                            MobileMessageData &aData)
 {
   nsCOMPtr<nsIDOMMozMmsMessage> mmsMsg = do_QueryInterface(aMsg);
@@ -316,16 +336,52 @@ SmsParent::RecvGetSegmentInfoForText(con
 
   aResult->segments() = segments;
   aResult->charsPerSegment() = charsPerSegment;
   aResult->charsAvailableInLastSegment() = charsAvailableInLastSegment;
   return true;
 }
 
 bool
+SmsParent::RecvAddSilentNumber(const nsString& aNumber)
+{
+  if (mSilentNumbers.Contains(aNumber)) {
+    return true;
+  }
+
+  nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(smsService, true);
+
+  nsresult rv = smsService->AddSilentNumber(aNumber);
+  if (NS_SUCCEEDED(rv)) {
+    mSilentNumbers.AppendElement(aNumber);
+  }
+
+  return true;
+}
+
+bool
+SmsParent::RecvRemoveSilentNumber(const nsString& aNumber)
+{
+  if (!mSilentNumbers.Contains(aNumber)) {
+    return true;
+  }
+
+  nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(smsService, true);
+
+  nsresult rv = smsService->RemoveSilentNumber(aNumber);
+  if (NS_SUCCEEDED(rv)) {
+    mSilentNumbers.RemoveElement(aNumber);
+  }
+
+  return true;
+}
+
+bool
 SmsParent::RecvPSmsRequestConstructor(PSmsRequestParent* aActor,
                                       const IPCSmsRequest& aRequest)
 {
   SmsRequestParent* actor = static_cast<SmsRequestParent*>(aActor);
 
   switch (aRequest.type()) {
     case IPCSmsRequest::TSendMessageRequest:
       return actor->DoRequest(aRequest.get_SendMessageRequest());
@@ -417,17 +473,17 @@ bool
 SmsRequestParent::DoRequest(const SendMessageRequest& aRequest)
 {
   switch(aRequest.type()) {
   case SendMessageRequest::TSendSmsMessageRequest: {
       nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
       NS_ENSURE_TRUE(smsService, true);
 
       const SendSmsMessageRequest &data = aRequest.get_SendSmsMessageRequest();
-      smsService->Send(data.number(), data.message(), this);
+      smsService->Send(data.number(), data.message(), data.silent(), this);
     }
     break;
   case SendMessageRequest::TSendMmsMessageRequest: {
       nsCOMPtr<nsIMmsService> mmsService = do_GetService(MMS_SERVICE_CONTRACTID);
       NS_ENSURE_TRUE(mmsService, true);
 
       AutoJSContext cx;
       JS::Rooted<JS::Value> params(cx);
--- a/dom/mobilemessage/src/ipc/SmsParent.h
+++ b/dom/mobilemessage/src/ipc/SmsParent.h
@@ -32,16 +32,22 @@ public:
 
 protected:
   virtual bool
   RecvHasSupport(bool* aHasSupport) MOZ_OVERRIDE;
 
   virtual bool
   RecvGetSegmentInfoForText(const nsString& aText, SmsSegmentInfoData* aResult) MOZ_OVERRIDE;
 
+  virtual bool
+  RecvAddSilentNumber(const nsString& aNumber) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvRemoveSilentNumber(const nsString& aNumber) MOZ_OVERRIDE;
+
   SmsParent();
   virtual ~SmsParent()
   {
     MOZ_COUNT_DTOR(SmsParent);
   }
 
   virtual void
   ActorDestroy(ActorDestroyReason why);
@@ -63,16 +69,19 @@ protected:
   virtual PMobileMessageCursorParent*
   AllocPMobileMessageCursorParent(const IPCMobileMessageCursor& aCursor) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPMobileMessageCursorParent(PMobileMessageCursorParent* aActor) MOZ_OVERRIDE;
 
   bool
   GetMobileMessageDataFromMessage(nsISupports* aMsg, MobileMessageData& aData);
+
+private:
+  nsTArray<nsString> mSilentNumbers;
 };
 
 class SmsRequestParent : public PSmsRequestParent
                        , public nsIMobileMessageCallback
 {
   friend class SmsParent;
 
   bool mActorDestroyed;
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -22,16 +22,17 @@
 #include "nsPrintfCString.h"
 
 #include "mozilla/Hal.h"
 #include "mozilla/Services.h"
 #include "base/message_loop.h"
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
+#include "BluetoothHfpManager.h"
 
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla::hal;
 using namespace mozilla;
@@ -163,68 +164,88 @@ InternalSetAudioRoutes(SwitchState aStat
     status_t (*)(audio_devices_t, audio_policy_dev_state_t, const char*)
     >(AudioSystem::setDeviceConnectionState)) {
     InternalSetAudioRoutesICS(aState);
   } else {
     NS_NOTREACHED("Doesn't support audio routing on GB version");
   }
 }
 
+void
+AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
+                                           const char* aTopic,
+                                           const nsCString aAddress)
+{
+  bool status;
+  if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
+    BluetoothHfpManager* hfp =
+      static_cast<BluetoothHfpManager*>(aSubject);
+    status = hfp->IsScoConnected();
+  } else {
+    BluetoothProfileManagerBase* profile =
+      static_cast<BluetoothProfileManagerBase*>(aSubject);
+    status = profile->IsConnected();
+  }
+
+  audio_policy_dev_state_t audioState = status ?
+    AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
+    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+
+  if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
+    if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+      String8 cmd;
+      cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
+      AudioSystem::setParameters(0, cmd);
+      SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
+    } else {
+      int32_t force;
+      GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
+      if (force == nsIAudioManager::FORCE_BT_SCO)
+        SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
+    }
+  } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+                                          audioState, aAddress.get());
+    if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+      String8 cmd("bluetooth_enabled=true");
+      AudioSystem::setParameters(0, cmd);
+      cmd.setTo("A2dpSuspended=false");
+      AudioSystem::setParameters(0, cmd);
+    } else {
+      String8 cmd("bluetooth_enabled=false");
+      AudioSystem::setParameters(0, cmd);
+      cmd.setTo("A2dpSuspended=true");
+      AudioSystem::setParameters(0, cmd);
+    }
+  } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+                                          audioState, aAddress.get());
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+                                          audioState, aAddress.get());
+  }
+}
+
 nsresult
 AudioManager::Observe(nsISupports* aSubject,
                       const char* aTopic,
                       const PRUnichar* aData)
 {
   if ((strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID) == 0) ||
+      (strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID) == 0) ||
       (strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID) == 0)) {
-    nsresult rv;
-    int status = NS_ConvertUTF16toUTF8(aData).ToInteger(&rv);
-    if (NS_FAILED(rv) || status > 1 || status < 0) {
-      NS_WARNING(nsPrintfCString("Wrong data value of %s", aTopic).get());
+    nsCString address = NS_ConvertUTF16toUTF8(nsDependentString(aData));
+    if (address.IsEmpty()) {
+      NS_WARNING(nsPrintfCString("Invalid address of %s", aTopic).get());
       return NS_ERROR_FAILURE;
     }
 
-    nsAutoString tmp_address;
-    BluetoothProfileManagerBase* profile =
-      static_cast<BluetoothProfileManagerBase*>(aSubject);
-    profile->GetAddress(tmp_address);
-    nsAutoCString address = NS_ConvertUTF16toUTF8(tmp_address);
-
-    audio_policy_dev_state_t audioState = status ?
-      AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
-      AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+    HandleBluetoothStatusChanged(aSubject, aTopic, address);
+    return NS_OK;
+  }
 
-    if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
-                                            audioState, address.get());
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
-                                            audioState, address.get());
-      if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
-        String8 cmd;
-        cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
-        AudioSystem::setParameters(0, cmd);
-        SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
-      } else {
-        // only force to none if the current force setting is bt_sco
-        int32_t force;
-        GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
-        if (force == nsIAudioManager::FORCE_BT_SCO)
-          SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
-      }
-    } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
-                                            audioState, address.get());
-      if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
-        String8 cmd("bluetooth_enabled=true");
-        AudioSystem::setParameters(0, cmd);
-        cmd.setTo("A2dpSuspended=false");
-        AudioSystem::setParameters(0, cmd);
-      }
-    }
-  }
   // To process the volume control on each audio channel according to
   // change of settings
   else if (!strcmp(aTopic, "mozsettings-changed")) {
     AutoSafeJSContext cx;
     nsDependentString dataStr(aData);
     JS::Rooted<JS::Value> val(cx);
     if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
         !val.isObject()) {
@@ -310,16 +331,19 @@ AudioManager::AudioManager() : mPhoneSta
     NS_WARNING("Failed to add bluetooth sco status changed observer!");
   }
   if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) {
     NS_WARNING("Failed to add bluetooth a2dp status changed observer!");
   }
   if (NS_FAILED(obs->AddObserver(this, "mozsettings-changed", false))) {
     NS_WARNING("Failed to add mozsettings-changed oberver!");
   }
+  if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID, false))) {
+    NS_WARNING("Failed to add bluetooth hfp status changed observer!");
+  }
 
   for (int loop = 0; loop < AUDIO_STREAM_CNT; loop++) {
     AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
                                   sMaxStreamVolumeTbl[loop]);
     mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop];
   }
   // Force publicnotification to output at maximal volume
   SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE,
@@ -337,16 +361,19 @@ AudioManager::~AudioManager() {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
   if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) {
     NS_WARNING("Failed to remove bluetooth sco status changed observer!");
   }
   if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) {
     NS_WARNING("Failed to remove bluetooth a2dp status changed observer!");
   }
+  if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_HFP_STATUS_CHANGED_ID))) {
+    NS_WARNING("Failed to remove bluetooth hfp status changed observer!");
+  }
 }
 
 NS_IMETHODIMP
 AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
 {
   if (AudioSystem::isMicrophoneMuted(aMicrophoneMuted)) {
     return NS_ERROR_FAILURE;
   }
@@ -370,17 +397,22 @@ AudioManager::GetPhoneState(int32_t* aSt
 }
 
 NS_IMETHODIMP
 AudioManager::SetPhoneState(int32_t aState)
 {
   if (mPhoneState == aState) {
     return NS_OK;
   }
-
+  // follow the switch audio path logic for android, Bug 897364
+  int usage;
+  GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &usage);
+  if (aState == PHONE_STATE_NORMAL && usage == nsIAudioManager::FORCE_BT_SCO) {
+    SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
+  }
 #if ANDROID_VERSION < 17
   if (AudioSystem::setPhoneState(aState)) {
 #else
   if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) {
 #endif
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -59,15 +59,19 @@ protected:
   int mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT];
 
   android::status_t SetStreamVolumeIndex(int32_t aStream, int32_t aIndex);
   android::status_t GetStreamVolumeIndex(int32_t aStream, int32_t *aIndex);
 
 private:
   nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
   nsCOMPtr<AudioChannelAgent>             mPhoneAudioAgent;
+
+  void HandleBluetoothStatusChanged(nsISupports* aSubject,
+                                    const char* aTopic,
+                                    const nsCString aAddress);
 };
 
 } /* namespace gonk */
 } /* namespace dom */
 } /* namespace mozilla */
 
 #endif // mozilla_dom_system_b2g_audiomanager_h__
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -46,16 +46,17 @@ const RADIOINTERFACE_CID =
 const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsITelephonyProvider = Ci.nsITelephonyProvider;
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
+const kSilentSmsReceivedObserverTopic    = "silent-sms-received";
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 const kSysMsgListenerReadyObserverTopic  = "system-message-listener-ready";
 const kSysClockChangeObserverTopic       = "system-clock-change";
@@ -140,16 +141,20 @@ const RIL_IPC_CELLBROADCAST_MSG_NAMES = 
 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
                                    "@mozilla.org/power/powermanagerservice;1",
                                    "nsIPowerManagerService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
                                    "nsIMobileMessageService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
                                    "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1",
                                    "nsIRilMobileMessageDatabaseService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 
@@ -1883,16 +1888,41 @@ RadioInterface.prototype = {
     }
 
     message.type = "sms";
     message.sender = message.sender || null;
     message.receiver = this.getMsisdn();
     message.body = message.fullBody = message.fullBody || null;
     message.timestamp = Date.now();
 
+    if (gSmsService.isSilentNumber(message.sender)) {
+      message.id = -1;
+      message.threadId = 0;
+      message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
+      message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
+      message.read = false;
+
+      let domMessage =
+        gMobileMessageService.createSmsMessage(message.id,
+                                               message.threadId,
+                                               message.delivery,
+                                               message.deliveryStatus,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.messageClass,
+                                               message.timestamp,
+                                               message.read);
+
+      Services.obs.notifyObservers(domMessage,
+                                   kSilentSmsReceivedObserverTopic,
+                                   null);
+      return true;
+    }
+
     // TODO: Bug #768441
     // For now we don't store indicators persistently. When the mwi.discard
     // flag is false, we'll need to persist the indicator to EFmwis.
     // See TS 23.040 9.2.3.24.2
 
     let mwi = message.mwi;
     if (mwi) {
       mwi.returnNumber = message.sender;
@@ -1972,16 +2002,35 @@ RadioInterface.prototype = {
   handleSmsSent: function handleSmsSent(message) {
     if (DEBUG) this.debug("handleSmsSent: " + JSON.stringify(message));
 
     let options = this._sentSmsEnvelopes[message.envelopeId];
     if (!options) {
       return;
     }
 
+    if (options.silent) {
+      // There is no way to modify nsIDOMMozSmsMessage attributes as they are
+      // read only so we just create a new sms instance to send along with
+      // the notification.
+      let sms = options.sms;
+      options.request.notifyMessageSent(
+        gMobileMessageService.createSmsMessage(sms.id,
+                                               sms.threadId,
+                                               DOM_MOBILE_MESSAGE_DELIVERY_SENT,
+                                               sms.deliveryStatus,
+                                               sms.sender,
+                                               sms.receiver,
+                                               sms.body,
+                                               sms.messageClass,
+                                               sms.timestamp,
+                                               sms.read));
+      return;
+    }
+
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      DOM_MOBILE_MESSAGE_DELIVERY_SENT,
                                                      options.sms.deliveryStatus,
                                                      function notifyResult(rv, domMessage) {
       // TODO bug 832140 handle !Components.isSuccessCode(rv)
       this.broadcastSmsSystemMessage("sms-sent", domMessage);
 
@@ -2001,16 +2050,20 @@ RadioInterface.prototype = {
     if (DEBUG) this.debug("handleSmsDelivery: " + JSON.stringify(message));
 
     let options = this._sentSmsEnvelopes[message.envelopeId];
     if (!options) {
       return;
     }
     delete this._sentSmsEnvelopes[message.envelopeId];
 
+    if (options.silent) {
+      return;
+    }
+
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      options.sms.delivery,
                                                      message.deliveryStatus,
                                                      function notifyResult(rv, domMessage) {
       // TODO bug 832140 handle !Components.isSuccessCode(rv)
       let topic = (message.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
                   ? kSmsDeliverySuccessObserverTopic
@@ -2030,16 +2083,21 @@ RadioInterface.prototype = {
 
     let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR;
     switch (message.errorMsg) {
       case RIL.ERROR_RADIO_NOT_AVAILABLE:
         error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
         break;
     }
 
+    if (options.silent) {
+      options.request.notifySendMessageFailed(error);
+      return;
+    }
+
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
                                                      RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
                                                      function notifyResult(rv, domMessage) {
       // TODO bug 832140 handle !Components.isSuccessCode(rv)
       options.request.notifySendMessageFailed(error);
       Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
@@ -3140,17 +3198,17 @@ RadioInterface.prototype = {
     }
 
     let result = gMobileMessageService.createSmsSegmentInfo(options.segmentMaxSeq,
                                                             options.segmentChars,
                                                             options.segmentChars - charsInLastSegment);
     return result;
   },
 
-  sendSMS: function sendSMS(number, message, request) {
+  sendSMS: function sendSMS(number, message, silent, request) {
     let strict7BitEncoding;
     try {
       strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding");
     } catch (e) {
       strict7BitEncoding = false;
     }
 
     let options = this._fragmentText(message, null, strict7BitEncoding);
@@ -3164,71 +3222,96 @@ RadioInterface.prototype = {
       requestStatusReport = true;
     }
     options.requestStatusReport = requestStatusReport;
     if (options.segmentMaxSeq > 1) {
       options.segmentRef16Bit = this.segmentRef16Bit;
       options.segmentRef = this.nextSegmentRef;
     }
 
+    let notifyResult = (function notifyResult(rv, domMessage) {
+      // TODO bug 832140 handle !Components.isSuccessCode(rv)
+      if (!silent) {
+        Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
+      }
+
+      // If the radio is disabled or the SIM card is not ready, just directly
+      // return with the corresponding error code.
+      let errorCode;
+      if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
+        if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
+                              options.number);
+        errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
+      } else if (!this._radioEnabled) {
+        if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
+        errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
+      } else if (this.rilContext.cardState != "ready") {
+        if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS.");
+        errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
+      }
+      if (errorCode) {
+        if (silent) {
+          request.notifySendMessageFailed(errorCode);
+          return;
+        }
+
+        gMobileMessageDatabaseService
+          .setMessageDelivery(domMessage.id,
+                              null,
+                              DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
+                              RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
+                              function notifyResult(rv, domMessage) {
+          // TODO bug 832140 handle !Components.isSuccessCode(rv)
+          request.notifySendMessageFailed(errorCode);
+          Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
+        });
+        return;
+      }
+
+      // Keep current SMS message info for sent/delivered notifications
+      options.envelopeId = this.createSmsEnvelope({
+        request: request,
+        sms: domMessage,
+        requestStatusReport: options.requestStatusReport,
+        silent: silent
+      });
+
+      // This is the entry point starting to send SMS.
+      this.worker.postMessage(options);
+    }).bind(this);
+
     let sendingMessage = {
       type: "sms",
       sender: this.getMsisdn(),
       receiver: number,
       body: message,
       deliveryStatusRequested: options.requestStatusReport,
       timestamp: Date.now()
     };
 
+    if (silent) {
+      let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING;
+      let delivery = DOM_MOBILE_MESSAGE_DELIVERY_SENDING;
+      let domMessage =
+        gMobileMessageService.createSmsMessage(-1, // id
+                                               0,  // threadId
+                                               delivery,
+                                               deliveryStatus,
+                                               sendingMessage.sender,
+                                               sendingMessage.receiver,
+                                               sendingMessage.body,
+                                               "normal", // message class
+                                               sendingMessage.timestamp,
+                                               false);
+      notifyResult(Cr.NS_OK, domMessage);
+      return;
+    }
+
     let id = gMobileMessageDatabaseService.saveSendingMessage(
-      sendingMessage,
-      function notifyResult(rv, domMessage) {
-
-        // TODO bug 832140 handle !Components.isSuccessCode(rv)
-        Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
-
-        // If the radio is disabled or the SIM card is not ready, just directly
-        // return with the corresponding error code.
-        let errorCode;
-        if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
-          if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
-                                options.number);
-          errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
-        } else if (!this._radioEnabled) {
-          if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
-          errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
-        } else if (this.rilContext.cardState != "ready") {
-          if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS.");
-          errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
-        }
-        if (errorCode) {
-          gMobileMessageDatabaseService
-            .setMessageDelivery(domMessage.id,
-                                null,
-                                DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
-                                RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
-                                function notifyResult(rv, domMessage) {
-            // TODO bug 832140 handle !Components.isSuccessCode(rv)
-            request.notifySendMessageFailed(errorCode);
-            Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
-          });
-          return;
-        }
-
-        // Keep current SMS message info for sent/delivered notifications
-        options.envelopeId = this.createSmsEnvelope({
-          request: request,
-          sms: domMessage,
-          requestStatusReport: options.requestStatusReport
-        });
-
-        // This is the entry point starting to send SMS.
-        this.worker.postMessage(options);
-
-      }.bind(this));
+      sendingMessage, notifyResult);
   },
 
   registerDataCallCallback: function registerDataCallCallback(callback) {
     if (this._datacall_callbacks) {
       if (this._datacall_callbacks.indexOf(callback) != -1) {
         throw new Error("Already registered this callback!");
       }
     } else {
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -74,17 +74,17 @@ interface nsIRilContext : nsISupports
 
   readonly attribute nsIDOMMozIccInfo iccInfo;
 
   readonly attribute nsIDOMMozMobileConnectionInfo voice;
 
   readonly attribute nsIDOMMozMobileConnectionInfo data;
 };
 
-[scriptable, uuid(6dde3eaf-243d-4afa-abdb-95c94c2b1c7a)]
+[scriptable, uuid(715c972b-97c5-48fd-a8b1-d50e6852153a)]
 interface nsIRadioInterface : nsISupports
 {
   /**
    * Activates or deactivates radio power.
    */
   void setRadioEnabled(in bool value);
 
   readonly attribute nsIRilContext rilContext;
@@ -114,16 +114,17 @@ interface nsIRadioInterface : nsISupport
 
   /**
    * SMS-related functionality.
    */
   nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
 
   void sendSMS(in DOMString number,
                in DOMString message,
+               in boolean silent,
                in nsIMobileMessageCallback request);
 };
 
 [scriptable, uuid(44b03951-1444-4c03-bd37-0bcb3a01b56f)]
 interface nsIRadioInterfaceLayer : nsISupports
 {
   readonly attribute unsigned long numRadioInterfaces;
 
--- a/dom/system/gonk/nsVolume.cpp
+++ b/dom/system/gonk/nsVolume.cpp
@@ -149,19 +149,19 @@ NS_IMETHODIMP nsVolume::GetIsFake(bool *
   *aIsFake = mIsFake;
   return NS_OK;
 }
 
 void
 nsVolume::LogState() const
 {
   if (mState == nsIVolume::STATE_MOUNTED) {
-    LOG("nsVolume: %s state %s @ '%s' gen %d locked %d",
+    LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d",
         NameStr().get(), StateStr(), MountPointStr().get(),
-        MountGeneration(), (int)IsMountLocked());
+        MountGeneration(), (int)IsMountLocked(), (int)IsFake());
     return;
   }
 
   LOG("nsVolume: %s state %s", NameStr().get(), StateStr());
 }
 
 void nsVolume::Set(nsIVolume* aVolume)
 {
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -372,16 +372,17 @@ nsVolumeService::UpdateVolume(nsIVolume*
 }
 
 NS_IMETHODIMP
 nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT, -1);
     vol->SetIsFake(true);
+    vol->LogState();
     UpdateVolume(vol.get());
     return NS_OK;
   }
 
   ContentChild::GetSingleton()->SendCreateFakeVolume(nsString(name), nsString(path));
   return NS_OK;
 }
 
@@ -393,16 +394,17 @@ nsVolumeService::SetFakeVolumeState(cons
     {
       MonitorAutoLock autoLock(mArrayMonitor);
       vol = FindVolumeByName(name);
     }
     if (!vol || !vol->IsFake()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
     vol->SetState(state);
+    vol->LogState();
     UpdateVolume(vol.get());
     return NS_OK;
   }
 
   ContentChild::GetSingleton()->SendSetFakeVolumeState(nsString(name), state);
   return NS_OK;
 }