Merge b2g-inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Dec 2013 15:56:46 -0500
changeset 173921 79d1a5a93b64fe467beb7a4897cc680d2f6b50d4
parent 173889 e070e04b0795949266c592c172fc689e8c5b16ad (current diff)
parent 173920 b746f594a8843718bce644c884312d09f6412d57 (diff)
child 173922 8f33dd2600ce10c7b45e5e0e0445040a71e1caf0
child 173953 b3806ae5399d5b1a23ad5f14ecb28406c2ad59a2
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.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 b2g-inbound to m-c.
CLOBBER
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 915735 requires clobber
+Bug 933585 - clobber required on windows
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "d10e0357c63bb565db8bdd3c23f062bfb9e21315", 
+    "revision": "63d432c3395f95c0ba19578487b796c1707042bd", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -39,17 +39,16 @@ pref("prompts.tab_modal.enabled", true);
 pref("layers.offmainthreadcomposition.enabled", true);
 pref("layers.async-pan-zoom.enabled", true);
 pref("layers.componentalpha.enabled", false);
 
 // Prefs to control the async pan/zoom behaviour
 pref("apz.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
 pref("apz.pan_repaint_interval", 50);   // prefer 20 fps
 pref("apz.fling_repaint_interval", 50); // prefer 20 fps
-pref("apz.fling_friction", "0.002");
 pref("apz.fling_stopped_threshold", "0.2");
 
 // 0 = free, 1 = standard, 2 = sticky
 pref("apz.axis_lock_mode", 2);
 pref("apz.cross_slide.enabled", true);
 
 // Enable Microsoft TSF support by default for imes.
 pref("intl.enable_tsf_support", true);
--- a/dom/bluetooth/ObexBase.cpp
+++ b/dom/bluetooth/ObexBase.cpp
@@ -73,52 +73,62 @@ AppendHeaderConnectionId(uint8_t* aRetBu
 void
 SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength)
 {
   aRetBuf[0] = aOpcode;
   aRetBuf[1] = (aPacketLength & 0xFF00) >> 8;
   aRetBuf[2] = aPacketLength & 0x00FF;
 }
 
-void
+bool
 ParseHeaders(const uint8_t* aHeaderStart,
              int aTotalLength,
              ObexHeaderSet* aRetHandlerSet)
 {
   const uint8_t* ptr = aHeaderStart;
 
   while (ptr - aHeaderStart < aTotalLength) {
     ObexHeaderId headerId = (ObexHeaderId)*ptr++;
 
-    int contentLength = 0;
+    uint16_t contentLength = 0;
     uint8_t highByte, lowByte;
 
     // Defined in 2.1 OBEX Headers, IrOBEX 1.2
     switch (headerId >> 6)
     {
       case 0x00:
         // Null-terminated Unicode text, length prefixed with 2-byte
         // unsigned integer.
       case 0x01:
         // byte sequence, length prefixed with 2 byte unsigned integer.
         highByte = *ptr++;
         lowByte = *ptr++;
-        contentLength = (((int)highByte << 8) | lowByte) - 3;
+        contentLength = (((uint16_t)highByte << 8) | lowByte) - 3;
         break;
 
       case 0x02:
         // 1 byte quantity
         contentLength = 1;
         break;
 
       case 0x03:
         // 4 byte quantity
         contentLength = 4;
         break;
     }
 
+    // Length check to prevent from memory pollusion.
+    if (ptr + contentLength > aHeaderStart + aTotalLength) {
+      // Severe error occurred. We can't even believe the received data, so
+      // clear all headers.
+      MOZ_ASSERT(false);
+      aRetHandlerSet->ClearHeaders();
+      return false;
+    }
+
     aRetHandlerSet->AddHeader(new ObexHeader(headerId, contentLength, ptr));
-
     ptr += contentLength;
   }
+
+  return true;
 }
 
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/ObexBase.h
+++ b/dom/bluetooth/ObexBase.h
@@ -97,17 +97,18 @@ enum ObexResponseCode {
   ServiceUnavailable = 0xD3,
   GatewayTimeout = 0xD4,
   HttpVersionNotSupported = 0xD5,
 
   DatabaseFull = 0xE0,
   DatabaseLocked = 0xE1,
 };
 
-class ObexHeader {
+class ObexHeader
+{
 public:
   ObexHeader(ObexHeaderId aId, int aDataLength, const uint8_t* aData)
     : mId(aId)
     , mDataLength(aDataLength)
     , mData(nullptr)
   {
     mData = new uint8_t[mDataLength];
     memcpy(mData, aData, aDataLength);
@@ -117,21 +118,19 @@ public:
   {
   }
 
   ObexHeaderId mId;
   int mDataLength;
   nsAutoArrayPtr<uint8_t> mData;
 };
 
-class ObexHeaderSet {
+class ObexHeaderSet
+{
 public:
-  uint8_t mOpcode;
-  nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
-
   ObexHeaderSet(uint8_t aOpcode) : mOpcode(aOpcode)
   {
   }
 
   ~ObexHeaderSet()
   {
   }
 
@@ -236,23 +235,36 @@ public:
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == aId) {
         return true;
       }
     }
 
     return false;
   }
+
+  void ClearHeaders()
+  {
+    mHeaders.Clear();
+  }
+
+private:
+  uint8_t mOpcode;
+  nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
 };
 
 int AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength);
 int AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength);
 int AppendHeaderEndOfBody(uint8_t* aRetBuf);
 int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
 int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
 void SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength);
-void ParseHeaders(const uint8_t* aHeaderStart,
+
+/**
+ * @return true when the message was parsed without any error, false otherwise.
+ */
+bool ParseHeaders(const uint8_t* aHeaderStart,
                   int aTotalLength,
                   ObexHeaderSet* aRetHanderSet);
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -798,35 +798,41 @@ BluetoothOppManager::ServerDataHandler(U
     }
   }
 
   ObexHeaderSet pktHeaders(opCode);
   if (opCode == ObexRequestCode::Connect) {
     // Section 3.3.1 "Connect", IrOBEX 1.2
     // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
     // [Headers:var]
-    ParseHeaders(&aMessage->mData[7],
-                 receivedLength - 7,
-                 &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToConnect();
     AfterOppConnected();
   } else if (opCode == ObexRequestCode::Abort) {
     // Section 3.3.5 "Abort", IrOBEX 1.2
     // [opcode:1][length:2][Headers:var]
-    ParseHeaders(&aMessage->mData[3],
-                receivedLength - 3,
-                &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToDisconnectOrAbort();
     DeleteReceivedFile();
   } else if (opCode == ObexRequestCode::Disconnect) {
     // Section 3.3.2 "Disconnect", IrOBEX 1.2
     // [opcode:1][length:2][Headers:var]
-    ParseHeaders(&aMessage->mData[3],
-                receivedLength - 3,
-                &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToDisconnectOrAbort();
     AfterOppDisconnected();
     FileTransferComplete();
   } else if (opCode == ObexRequestCode::Put ||
              opCode == ObexRequestCode::PutFinal) {
     if (!ComposePacket(opCode, aMessage)) {
       return;
     }
@@ -1192,17 +1198,17 @@ BluetoothOppManager::ReplyToPut(bool aFi
   }
 
   SendObexData(req, opcode, index);
 }
 
 void
 BluetoothOppManager::ReplyError(uint8_t aError)
 {
-  if (!mConnected) return;
+  BT_LOGR("error: %d", aError);
 
   // Section 3.2 "Response Format", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
   uint8_t req[255];
   int index = 3;
 
   SendObexData(req, aError, index);
 }
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -336,19 +336,22 @@ Call::IsActive()
 {
   return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
 }
 #endif // MOZ_B2G_RIL
 
 /**
  *  BluetoothHfpManager
  */
-BluetoothHfpManager::BluetoothHfpManager() : mPhoneType(PhoneType::NONE)
-                                           , mController(nullptr)
+BluetoothHfpManager::BluetoothHfpManager() : mController(nullptr)
 {
+#ifdef MOZ_B2G_RIL
+  mPhoneType = PhoneType::NONE;
+#endif // MOZ_B2G_RIL
+
   Reset();
 }
 
 #ifdef MOZ_B2G_RIL
 void
 BluetoothHfpManager::ResetCallArray()
 {
   mCurrentCallArray.Clear();
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -814,35 +814,41 @@ BluetoothOppManager::ServerDataHandler(U
     }
   }
 
   ObexHeaderSet pktHeaders(opCode);
   if (opCode == ObexRequestCode::Connect) {
     // Section 3.3.1 "Connect", IrOBEX 1.2
     // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
     // [Headers:var]
-    ParseHeaders(&aMessage->mData[7],
-                 receivedLength - 7,
-                 &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToConnect();
     AfterOppConnected();
   } else if (opCode == ObexRequestCode::Abort) {
     // Section 3.3.5 "Abort", IrOBEX 1.2
     // [opcode:1][length:2][Headers:var]
-    ParseHeaders(&aMessage->mData[3],
-                receivedLength - 3,
-                &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToDisconnectOrAbort();
     DeleteReceivedFile();
   } else if (opCode == ObexRequestCode::Disconnect) {
     // Section 3.3.2 "Disconnect", IrOBEX 1.2
     // [opcode:1][length:2][Headers:var]
-    ParseHeaders(&aMessage->mData[3],
-                receivedLength - 3,
-                &pktHeaders);
+    if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
+      ReplyError(ObexResponseCode::BadRequest);
+      return;
+    }
+
     ReplyToDisconnectOrAbort();
     AfterOppDisconnected();
     FileTransferComplete();
   } else if (opCode == ObexRequestCode::Put ||
              opCode == ObexRequestCode::PutFinal) {
     if (!ComposePacket(opCode, aMessage)) {
       return;
     }
@@ -1208,17 +1214,17 @@ BluetoothOppManager::ReplyToPut(bool aFi
   }
 
   SendObexData(req, opcode, index);
 }
 
 void
 BluetoothOppManager::ReplyError(uint8_t aError)
 {
-  if (!mConnected) return;
+  BT_LOGR("error: %d", aError);
 
   // Section 3.2 "Response Format", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
   uint8_t req[255];
   int index = 3;
 
   SendObexData(req, aError, index);
 }
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -74,16 +74,17 @@ enum {
   CAMERA_PARAM_FOCALLENGTH,
   CAMERA_PARAM_FOCUSDISTANCENEAR,
   CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
   CAMERA_PARAM_FOCUSDISTANCEFAR,
   CAMERA_PARAM_EXPOSURECOMPENSATION,
   CAMERA_PARAM_PICTURESIZE,
   CAMERA_PARAM_THUMBNAILSIZE,
   CAMERA_PARAM_THUMBNAILQUALITY,
+  CAMERA_PARAM_SENSORANGLE,
 
   CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
   CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
   CAMERA_PARAM_SUPPORTED_PICTURESIZES,
   CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
   CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
   CAMERA_PARAM_SUPPORTED_SCENEMODES,
   CAMERA_PARAM_SUPPORTED_EFFECTS,
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -263,16 +263,24 @@ CameraControlImpl::Set(uint32_t aKey, co
 
 nsresult
 CameraControlImpl::Get(uint32_t aKey, idl::CameraSize& aSize)
 {
   GetParameter(aKey, aSize);
   return NS_OK;
 }
 
+nsresult
+CameraControlImpl::Get(uint32_t aKey, int32_t* aValue)
+{
+  MOZ_ASSERT(aValue);
+  *aValue = GetParameterInt32(aKey);
+  return NS_OK;
+}
+
 already_AddRefed<RecorderProfileManager>
 CameraControlImpl::GetRecorderProfileManager()
 {
   return GetRecorderProfileManagerImpl();
 }
 
 void
 CameraControlImpl::Shutdown()
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -70,16 +70,17 @@ public:
   nsresult Set(nsICameraClosedCallback* aOnClosed);
   nsresult Get(nsICameraClosedCallback** aOnClosed);
   nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange);
   nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange);
   nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange);
   nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange);
   nsresult Set(uint32_t aKey, const idl::CameraSize& aSize);
   nsresult Get(uint32_t aKey, idl::CameraSize& aSize);
+  nsresult Get(uint32_t aKey, int32_t* aValue);
 
   nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
   {
     return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
   }
 
   nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
   {
@@ -87,16 +88,17 @@ public:
   }
 
   already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
   uint32_t GetCameraId() { return mCameraId; }
 
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
+  virtual int32_t GetParameterInt32(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions) = 0;
   virtual void GetParameter(uint32_t aKey, idl::CameraSize& aSize) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions) = 0;
   virtual void SetParameter(uint32_t aKey, const idl::CameraSize& aSize) = 0;
   virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -280,16 +280,24 @@ nsDOMCameraControl::SetExposureCompensat
 double
 nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
 {
   double compensation;
   aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, &compensation);
   return compensation;
 }
 
+int32_t
+nsDOMCameraControl::SensorAngle()
+{
+  int32_t angle;
+  mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, &angle);
+  return angle;
+}
+
 already_AddRefed<nsICameraShutterCallback>
 nsDOMCameraControl::GetOnShutter(ErrorResult& aRv)
 {
   nsCOMPtr<nsICameraShutterCallback> cb;
   aRv = mCameraControl->Get(getter_AddRefs(cb));
   return cb.forget();
 }
 
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -71,16 +71,17 @@ public:
   JS::Value GetThumbnailSize(JSContext* aCx, ErrorResult& aRv);
   void SetThumbnailSize(JSContext* aCx, JS::Handle<JS::Value> aSize, ErrorResult& aRv);
   double GetFocalLength(ErrorResult& aRv);
   double GetFocusDistanceNear(ErrorResult& aRv);
   double GetFocusDistanceOptimum(ErrorResult& aRv);
   double GetFocusDistanceFar(ErrorResult& aRv);
   void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
   double GetExposureCompensation(ErrorResult& aRv);
+  int32_t SensorAngle();
   already_AddRefed<nsICameraShutterCallback> GetOnShutter(ErrorResult& aRv);
   void SetOnShutter(nsICameraShutterCallback* aCb, ErrorResult& aRv);
   already_AddRefed<nsICameraClosedCallback> GetOnClosed(ErrorResult& aRv);
   void SetOnClosed(nsICameraClosedCallback* aCb, ErrorResult& aRv);
   already_AddRefed<nsICameraRecorderStateChange> GetOnRecorderStateChange(ErrorResult& aRv);
   void SetOnRecorderStateChange(nsICameraRecorderStateChange* aCb, ErrorResult& aRv);
   void AutoFocus(nsICameraAutoFocusCallback* aOnSuccess, const dom::Optional<nsICameraErrorCallback*>& aOnErro, ErrorResult& aRvr);
   void TakePicture(JSContext* aCx, const dom::CameraPictureOptions& aOptions,
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -19,16 +19,17 @@ class RecorderProfileManager;
 class nsFallbackCameraControl : public CameraControlImpl
 {
 public:
   nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
+  int32_t GetParameterInt32(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
   void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
   void SetParameter(uint32_t aKey, const idl::CameraSize& aSize);
   nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes);
@@ -96,16 +97,22 @@ nsFallbackCameraControl::GetParameterCon
 }
 
 double
 nsFallbackCameraControl::GetParameterDouble(uint32_t aKey)
 {
   return NAN;
 }
 
+int32_t
+nsFallbackCameraControl::GetParameterInt32(uint32_t aKey)
+{
+  return 0;
+}
+
 void
 nsFallbackCameraControl::GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions)
 {
 }
 
 void
 nsFallbackCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize)
 {
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -429,16 +429,35 @@ nsGonkCameraControl::GetParameterDouble(
       DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val);
       return val;
 
     default:
       return mParams.getFloat(key);
   }
 }
 
+int32_t
+nsGonkCameraControl::GetParameterInt32(uint32_t aKey)
+{
+  if (aKey == CAMERA_PARAM_SENSORANGLE) {
+    if (!mCameraHw.get()) {
+      return 0;
+    }
+    return mCameraHw->GetSensorOrientation();
+  }
+
+  const char* key = getKeyText(aKey);
+  if (!key) {
+    return 0;
+  }
+
+  RwAutoLockRead lock(mRwLock);
+  return mParams.getInt(key);
+}
+
 void
 nsGonkCameraControl::GetParameter(uint32_t aKey,
                                   nsTArray<idl::CameraRegion>& aRegions)
 {
   aRegions.Clear();
 
   const char* key = getKeyText(aKey);
   if (!key) {
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -48,16 +48,17 @@ class nsGonkCameraControl : public Camer
 public:
   nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
   void DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
   nsresult Init();
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
+  int32_t GetParameterInt32(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
   void GetParameter(uint32_t aKey, nsTArray<idl::CameraSize>& aSizes);
   void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
   void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
   void SetParameter(uint32_t aKey, int aValue);
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -41,16 +41,17 @@ public:
   virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0;
   virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0;
   virtual nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0;
   virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0;
   virtual nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange) = 0;
   virtual nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange) = 0;
   virtual nsresult Set(uint32_t aKey, const idl::CameraSize& aSize) = 0;
   virtual nsresult Get(uint32_t aKey, idl::CameraSize& aSize) = 0;
+  virtual nsresult Get(uint32_t aKey, int32_t* aValue) = 0;
   virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
   virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
   virtual uint32_t GetCameraId() = 0;
 
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -57,16 +57,19 @@ this.SystemMessagePermissionsTable = {
   },
   "connection": { },
   "dummy-system-message": { }, // for system message testing framework
   "headset-button": { },
   "icc-stkcommand": {
     "settings": ["read", "write"]
   },
   "media-button": { },
+  "networkstats-alarm": {
+    "networkstats-manage": []
+  },
   "notification": {
     "desktop-notification": []
   },
   "push": {
   	"push": []
   },
   "push-register": {
   	"push": []
--- a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
+++ b/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
@@ -15,17 +15,32 @@ interface nsIDOMMozNetworkStatsInterface
   readonly attribute long type;
 
   /**
    * Id value is '0' for wifi or the iccid for mobile (SIM).
    */
   readonly attribute DOMString id;
 };
 
-[scriptable, uuid(5f033d31-c9a2-4e2d-83aa-6a807f1e0c11)]
+dictionary NetworkStatsAlarmOptions
+{
+  jsval startTime; // Date object
+  jsval data;
+};
+
+[scriptable, builtinclass, uuid(063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c)]
+interface nsIDOMMozNetworkStatsAlarm : nsISupports
+{
+  readonly attribute unsigned long alarmId;
+  readonly attribute nsIDOMMozNetworkStatsInterface network;
+  readonly attribute long threshold;
+  readonly attribute jsval data;
+};
+
+[scriptable,  uuid(50d109b8-0d7f-4208-81fe-5f07a759f159)]
 interface nsIDOMMozNetworkStatsManager : nsISupports
 {
   /**
    * Constants for known interface types.
    */
   const long WIFI = 0;
   const long MOBILE = 1;
 
@@ -38,16 +53,51 @@ interface nsIDOMMozNetworkStatsManager :
    * If success, the request result will be an nsIDOMMozNetworkStats object.
    */
   nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network,
                               in jsval start,
                               in jsval end,
                               [optional] in DOMString manifestURL);
 
   /**
+   * Install an alarm on a network. The network must be in the return of
+   * getAvailableNetworks() otherwise an "InvalidNetwork" exception will
+   * be raised.
+   *
+   * When total data usage reaches threshold bytes, a "networkstats-alarm"
+   * system message is sent to the application, where the optional parameter
+   * |data| must be a cloneable object.
+   *
+   * If success, the |result| field of the DOMRequest keeps the alarm Id.
+   */
+  nsIDOMDOMRequest addAlarm(in nsIDOMMozNetworkStatsInterface network,
+                            in long threshold,
+                            [optional] in jsval options /* NetworkStatsAlarmOptions */);
+
+  /**
+   * Obtain all alarms for those networks returned by getAvailableNetworks().
+   * If a network is provided, only retrieves the alarms for that network.
+   * The network must be one of those returned by getAvailebleNetworks() or an
+   * "InvalidNetwork" exception will be raised.
+   *
+   * Each alarm object has the same fields as that in the system message:
+   *  - alarmId
+   *  - network
+   *  - threshold
+   *  - data
+   */
+  nsIDOMDOMRequest getAllAlarms([optional] in nsIDOMMozNetworkStatsInterface network);
+
+  /**
+   * Remove all network alarms. If an |alarmId| is provided, then only that
+   * alarm is removed.
+   */
+  nsIDOMDOMRequest removeAlarms([optional] in long alarmId);
+
+  /**
    * Remove all stats related with the provided network from DB.
    */
   nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network);
 
   /**
    * Remove all stats in the database.
    */
   nsIDOMDOMRequest clearAllStats();
--- a/dom/network/src/NetworkStatsDB.jsm
+++ b/dom/network/src/NetworkStatsDB.jsm
@@ -11,59 +11,60 @@ function debug(s) { dump("-*- NetworkSta
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.importGlobalProperties(["indexedDB"]);
 
 const DB_NAME = "net_stats";
-const DB_VERSION = 4;
-const STORE_NAME = "net_stats";
+const DB_VERSION = 5;
+const STATS_STORE_NAME = "net_stats";
+const ALARMS_STORE_NAME = "net_alarm";
 
 // Constant defining the maximum values allowed per interface. If more, older
 // will be erased.
 const VALUES_MAX_LENGTH = 6 * 30;
 
 // Constant defining the rate of the samples. Daily.
 const SAMPLE_RATE = 1000 * 60 * 60 * 24;
 
 this.NetworkStatsDB = function NetworkStatsDB() {
   if (DEBUG) {
     debug("Constructor");
   }
-  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
+  this.initDBHelper(DB_NAME, DB_VERSION, [STATS_STORE_NAME, ALARMS_STORE_NAME]);
 }
 
 NetworkStatsDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
-  dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) {
+  dbNewTxn: function dbNewTxn(store_name, txn_type, callback, txnCb) {
     function successCb(result) {
       txnCb(null, result);
     }
     function errorCb(error) {
       txnCb(error, null);
     }
-    return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
+    return this.newTxn(txn_type, store_name, callback, successCb, errorCb);
   },
 
   upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     if (DEBUG) {
       debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!");
     }
     let db = aDb;
     let objectStore;
     for (let currVersion = aOldVersion; currVersion < aNewVersion; currVersion++) {
       if (currVersion == 0) {
         /**
          * Create the initial database schema.
          */
 
-        objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["connectionType", "timestamp"] });
+        objectStore = db.createObjectStore(STATS_STORE_NAME, { keyPath: ["connectionType", "timestamp"] });
         objectStore.createIndex("connectionType", "connectionType", { unique: false });
         objectStore.createIndex("timestamp", "timestamp", { unique: false });
         objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
         objectStore.createIndex("txBytes", "txBytes", { unique: false });
         objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
         objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
         if (DEBUG) {
           debug("Created object stores and indexes");
@@ -72,34 +73,34 @@ NetworkStatsDB.prototype = {
         // In order to support per-app traffic data storage, the original
         // objectStore needs to be replaced by a new objectStore with new
         // key path ("appId") and new index ("appId").
         // Also, since now networks are identified by their
         // [networkId, networkType] not just by their connectionType,
         // to modify the keyPath is mandatory to delete the object store
         // and create it again. Old data is going to be deleted because the
         // networkId for each sample can not be set.
-        db.deleteObjectStore(STORE_NAME);
+        db.deleteObjectStore(STATS_STORE_NAME);
 
-        objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
+        objectStore = db.createObjectStore(STATS_STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
         objectStore.createIndex("appId", "appId", { unique: false });
         objectStore.createIndex("network", "network", { unique: false });
         objectStore.createIndex("networkType", "networkType", { unique: false });
         objectStore.createIndex("timestamp", "timestamp", { unique: false });
         objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
         objectStore.createIndex("txBytes", "txBytes", { unique: false });
         objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
         objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
 
         if (DEBUG) {
           debug("Created object stores and indexes for version 3");
         }
       } else if (currVersion == 3) {
         // Delete redundent indexes (leave "network" only).
-        objectStore = aTransaction.objectStore(STORE_NAME);
+        objectStore = aTransaction.objectStore(STATS_STORE_NAME);
         if (objectStore.indexNames.contains("appId")) {
           objectStore.deleteIndex("appId");
         }
         if (objectStore.indexNames.contains("networkType")) {
           objectStore.deleteIndex("networkType");
         }
         if (objectStore.indexNames.contains("timestamp")) {
           objectStore.deleteIndex("timestamp");
@@ -115,28 +116,92 @@ NetworkStatsDB.prototype = {
         }
         if (objectStore.indexNames.contains("txTotalBytes")) {
           objectStore.deleteIndex("txTotalBytes");
         }
 
         if (DEBUG) {
           debug("Deleted redundent indexes for version 4");
         }
+      } else if (currVersion == 4) {
+        // In order to manage alarms, it is necessary to use a global counter
+        // (totalBytes) that will increase regardless of the system reboot.
+        objectStore = aTransaction.objectStore(STATS_STORE_NAME);
+
+        // Now, systemBytes will hold the old totalBytes and totalBytes will
+        // keep the increasing counter. |counters| will keep the track of
+        // accumulated values.
+        let counters = {};
+
+        objectStore.openCursor().onsuccess = function(event) {
+          let cursor = event.target.result;
+          if (!cursor){
+            return;
+          }
+
+          cursor.value.rxSystemBytes = cursor.value.rxTotalBytes;
+          cursor.value.txSystemBytes = cursor.value.txTotalBytes;
+
+          if (cursor.value.appId == 0) {
+            let netId = cursor.value.network[0] + '' + cursor.value.network[1];
+            if (!counters[netId]) {
+              counters[netId] = {
+                rxCounter: 0,
+                txCounter: 0,
+                lastRx: 0,
+                lastTx: 0
+              };
+            }
+
+            let rxDiff = cursor.value.rxSystemBytes - counters[netId].lastRx;
+            let txDiff = cursor.value.txSystemBytes - counters[netId].lastTx;
+            if (rxDiff < 0 || txDiff < 0) {
+              // System reboot between samples, so take the current one.
+              rxDiff = cursor.value.rxSystemBytes;
+              txDiff = cursor.value.txSystemBytes;
+            }
+
+            counters[netId].rxCounter += rxDiff;
+            counters[netId].txCounter += txDiff;
+            cursor.value.rxTotalBytes = counters[netId].rxCounter;
+            cursor.value.txTotalBytes = counters[netId].txCounter;
+
+            counters[netId].lastRx = cursor.value.rxSystemBytes;
+            counters[netId].lastTx = cursor.value.txSystemBytes;
+          } else {
+            cursor.value.rxTotalBytes = cursor.value.rxSystemBytes;
+            cursor.value.txTotalBytes = cursor.value.txSystemBytes;
+          }
+
+          cursor.update(cursor.value);
+          cursor.continue();
+        };
+
+        // Create object store for alarms.
+        objectStore = db.createObjectStore(ALARMS_STORE_NAME, { keyPath: "id", autoIncrement: true });
+        objectStore.createIndex("alarm", ['networkId','threshold'], { unique: false });
+        objectStore.createIndex("manifestURL", "manifestURL", { unique: false });
+
+        if (DEBUG) {
+          debug("Created alarms store for version 5");
+        }
       }
     }
   },
 
   importData: function importData(aStats) {
-    let stats = { appId:        aStats.appId,
-                  network:      [aStats.networkId, aStats.networkType],
-                  timestamp:    aStats.timestamp,
-                  rxBytes:      aStats.rxBytes,
-                  txBytes:      aStats.txBytes,
-                  rxTotalBytes: aStats.rxTotalBytes,
-                  txTotalBytes: aStats.txTotalBytes };
+    let stats = { appId:         aStats.appId,
+                  network:       [aStats.networkId, aStats.networkType],
+                  timestamp:     aStats.timestamp,
+                  rxBytes:       aStats.rxBytes,
+                  txBytes:       aStats.txBytes,
+                  rxSystemBytes: aStats.rxSystemBytes,
+                  txSystemBytes: aStats.txSystemBytes,
+                  rxTotalBytes:  aStats.rxTotalBytes,
+                  txTotalBytes:  aStats.txTotalBytes };
 
     return stats;
   },
 
   exportData: function exportData(aStats) {
     let stats = { appId:        aStats.appId,
                   networkId:    aStats.network[0],
                   networkType:  aStats.network[1],
@@ -155,28 +220,30 @@ NetworkStatsDB.prototype = {
     let timestamp = aDate.getTime() - aDate.getTimezoneOffset() * 60 * 1000;
     timestamp = Math.floor(timestamp / SAMPLE_RATE) * SAMPLE_RATE;
     return timestamp;
   },
 
   saveStats: function saveStats(aStats, aResultCb) {
     let timestamp = this.normalizeDate(aStats.date);
 
-    let stats = { appId:        aStats.appId,
-                  networkId:    aStats.networkId,
-                  networkType:  aStats.networkType,
-                  timestamp:    timestamp,
-                  rxBytes:      (aStats.appId == 0) ? 0 : aStats.rxBytes,
-                  txBytes:      (aStats.appId == 0) ? 0 : aStats.txBytes,
-                  rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
-                  txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
+    let stats = { appId:         aStats.appId,
+                  networkId:     aStats.networkId,
+                  networkType:   aStats.networkType,
+                  timestamp:     timestamp,
+                  rxBytes:       (aStats.appId == 0) ? 0 : aStats.rxBytes,
+                  txBytes:       (aStats.appId == 0) ? 0 : aStats.txBytes,
+                  rxSystemBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
+                  txSystemBytes: (aStats.appId == 0) ? aStats.txBytes : 0,
+                  rxTotalBytes:  (aStats.appId == 0) ? aStats.rxBytes : 0,
+                  txTotalBytes:  (aStats.appId == 0) ? aStats.txBytes : 0 };
 
     stats = this.importData(stats);
 
-    this.dbNewTxn("readwrite", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readwrite", function(aTxn, aStore) {
       if (DEBUG) {
         debug("Filtered time: " + new Date(timestamp));
         debug("New stats: " + JSON.stringify(stats));
       }
 
     let request = aStore.index("network").openCursor(stats.network, "prev");
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
@@ -236,31 +303,39 @@ NetworkStatsDB.prototype = {
             lastSample.timestamp + " - diff: " + diff);
     }
 
     // If the incoming data is obtained from netd (|newSample.appId| is 0),
     // the new |txBytes|/|rxBytes| is assigend by the differnce between the new
     // |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
     // Else, the incoming data is per-app data (|newSample.appId| is not 0),
     // the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
+    let rxDiff = 0;
+    let txDiff = 0;
     if (aNewSample.appId == 0) {
-      let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes;
-      let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes;
+      rxDiff = aNewSample.rxSystemBytes - lastSample.rxSystemBytes;
+      txDiff = aNewSample.txSystemBytes - lastSample.txSystemBytes;
       if (rxDiff < 0 || txDiff < 0) {
-        rxDiff = aNewSample.rxTotalBytes;
-        txDiff = aNewSample.txTotalBytes;
+        rxDiff = aNewSample.rxSystemBytes;
+        txDiff = aNewSample.txSystemBytes;
       }
       aNewSample.rxBytes = rxDiff;
       aNewSample.txBytes = txDiff;
+
+      aNewSample.rxTotalBytes = lastSample.rxTotalBytes + rxDiff;
+      aNewSample.txTotalBytes = lastSample.txTotalBytes + txDiff;
+    } else {
+      rxDiff = aNewSample.rxBytes;
+      txDiff = aNewSample.txBytes;
     }
 
     if (diff == 1) {
       // New element.
 
-      // If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
+      // If the incoming data is per-app data, new |rxTotalBytes|/|txTotalBytes|
       // needs to be obtained by adding new |rxBytes|/|txBytes| to last
       // |rxTotalBytes|/|txTotalBytes|.
       if (aNewSample.appId != 0) {
         aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes;
         aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes;
       }
 
       this._saveStats(aTxn, aStore, aNewSample);
@@ -272,53 +347,48 @@ NetworkStatsDB.prototype = {
       // Add lost samples with 0 bytes and the actual one.
       if (diff > VALUES_MAX_LENGTH) {
         diff = VALUES_MAX_LENGTH;
       }
 
       let data = [];
       for (let i = diff - 2; i >= 0; i--) {
         let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1);
-        let sample = { appId:        aNewSample.appId,
-                       network:      aNewSample.network,
-                       timestamp:    time,
-                       rxBytes:      0,
-                       txBytes:      0,
-                       rxTotalBytes: lastSample.rxTotalBytes,
-                       txTotalBytes: lastSample.txTotalBytes };
+        let sample = { appId:         aNewSample.appId,
+                       network:       aNewSample.network,
+                       timestamp:     time,
+                       rxBytes:       0,
+                       txBytes:       0,
+                       rxSystemBytes: lastSample.rxSystemBytes,
+                       txSystemBytes: lastSample.txSystemBytes,
+                       rxTotalBytes:  lastSample.rxTotalBytes,
+                       txTotalBytes:  lastSample.txTotalBytes };
 
         data.push(sample);
       }
 
       data.push(aNewSample);
       this._saveStats(aTxn, aStore, data);
       return;
     }
     if (diff == 0 || diff < 0) {
-      // New element received before samplerate period.
-      // It means that device has been restarted (or clock / timezone change).
-      // Update element.
-
-      // If diff < 0, clock or timezone changed back. Place data in the last sample.
-
-      lastSample.rxBytes += aNewSample.rxBytes;
-      lastSample.txBytes += aNewSample.txBytes;
+      // New element received before samplerate period. It means that device has
+      // been restarted (or clock / timezone change).
+      // Update element. If diff < 0, clock or timezone changed back. Place data
+      // in the last sample.
 
-      // If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
-      // needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
-      if (aNewSample.appId == 0) {
-        lastSample.rxTotalBytes = aNewSample.rxTotalBytes;
-        lastSample.txTotalBytes = aNewSample.txTotalBytes;
-      } else {
-        // Else, the incoming data is per-app data, old |rxTotalBytes|/
-        // |txTotalBytes| needs to get updated by adding the new
-        // |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
-        lastSample.rxTotalBytes += aNewSample.rxBytes;
-        lastSample.txTotalBytes += aNewSample.txBytes;
-      }
+      // Old |rxTotalBytes|/|txTotalBytes| needs to get updated by adding the
+      // last |rxTotalBytes|/|txTotalBytes|.
+      lastSample.rxBytes += rxDiff;
+      lastSample.txBytes += txDiff;
+      lastSample.rxSystemBytes = aNewSample.rxSystemBytes;
+      lastSample.txSystemBytes = aNewSample.txSystemBytes;
+      lastSample.rxTotalBytes += rxDiff;
+      lastSample.txTotalBytes += txDiff;
+
       if (DEBUG) {
         debug("Update: " + JSON.stringify(lastSample));
       }
       let req = aLastSampleCursor.update(lastSample);
     }
   },
 
   _saveStats: function _saveStats(aTxn, aStore, aNetworkStats) {
@@ -374,17 +444,17 @@ NetworkStatsDB.prototype = {
     };
   },
 
   clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) {
     let network = [aNetwork.id, aNetwork.type];
     let self = this;
 
     // Clear and save an empty sample to keep sync with system counters
-    this.dbNewTxn("readwrite", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readwrite", function(aTxn, aStore) {
       let sample = null;
       let request = aStore.index("network").openCursor(network, "prev");
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
         if (cursor) {
           if (!sample && cursor.value.appId == 0) {
             sample = cursor.value;
           }
@@ -426,29 +496,56 @@ NetworkStatsDB.prototype = {
 
     if (!aNetworks[index]) {
       aResultCb(null, true);
       return;
     }
     this.clearInterfaceStats(aNetworks[index], callback);
   },
 
+  getCurrentStats: function getCurrentStats(aNetwork, aDate, aResultCb) {
+    if (DEBUG) {
+      debug("Get current stats for " + JSON.stringify(aNetwork) + " since " + aDate);
+    }
+
+    this.dbNewTxn(STATS_STORE_NAME, "readonly", function(txn, store) {
+      let request = null;
+      let network = [aNetwork.id, aNetwork.type];
+      if (aDate) {
+        let start = this.normalizeDate(aDate);
+        let lowerFilter = [0, network, start];
+        let range = this.dbGlobal.IDBKeyRange.lowerBound(lowerFilter, false);
+        request = store.openCursor(range);
+      } else {
+        request = store.index("network").openCursor(network, "prev");
+      }
+
+      request.onsuccess = function onsuccess(event) {
+        txn.result = null;
+        let cursor = event.target.result;
+        if (cursor) {
+          txn.result = cursor.value;
+        }
+      };
+    }.bind(this), aResultCb);
+  },
+
   find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) {
     let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
     let start = this.normalizeDate(aStart);
     let end = this.normalizeDate(aEnd);
 
     if (DEBUG) {
       debug("Find samples for appId: " + aAppId + " network " +
             JSON.stringify(aNetwork) + " from " + start + " until " + end);
       debug("Start time: " + new Date(start));
       debug("End time: " + new Date(end));
     }
 
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
       let network = [aNetwork.id, aNetwork.type];
       let lowerFilter = [aAppId, network, start];
       let upperFilter = [aAppId, network, end];
       let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let data = [];
 
       if (!aTxn.result) {
@@ -498,17 +595,17 @@ NetworkStatsDB.prototype = {
     while (aEnd > aData[aData.length - 1].date.getTime()) {
       aData.push({ rxBytes: undefined,
                    txBytes: undefined,
                    date: new Date(aData[aData.length - 1].date.getTime() + SAMPLE_RATE) });
     }
   },
 
   getAvailableNetworks: function getAvailableNetworks(aResultCb) {
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
       if (!aTxn.result) {
         aTxn.result = [];
       }
 
       let request = aStore.index("network").openKeyCursor(null, "nextunique");
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
         if (cursor) {
@@ -517,17 +614,17 @@ NetworkStatsDB.prototype = {
           cursor.continue();
           return;
         }
       };
     }, aResultCb);
   },
 
   isNetworkAvailable: function isNetworkAvailable(aNetwork, aResultCb) {
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
       if (!aTxn.result) {
         aTxn.result = false;
       }
 
       var network = [aNetwork.id, aNetwork.type];
       let request = aStore.index("network").openKeyCursor(IDBKeyRange.only(network));
       request.onsuccess = function onsuccess(event) {
         if (event.target.result) {
@@ -541,15 +638,164 @@ NetworkStatsDB.prototype = {
     return SAMPLE_RATE;
   },
 
   get maxStorageSamples () {
     return VALUES_MAX_LENGTH;
   },
 
   logAllRecords: function logAllRecords(aResultCb) {
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
+    this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
       aStore.mozGetAll().onsuccess = function onsuccess(event) {
         aTxn.result = event.target.result;
       };
     }, aResultCb);
   },
+
+  alarmToRecord: function alarmToRecord(aAlarm) {
+    let record = { networkId: aAlarm.networkId,
+                   threshold: aAlarm.threshold,
+                   data: aAlarm.data,
+                   manifestURL: aAlarm.manifestURL,
+                   pageURL: aAlarm.pageURL };
+
+    if (aAlarm.id) {
+      record.id = aAlarm.id;
+    }
+
+    return record;
+  },
+
+  recordToAlarm: function recordToalarm(aRecord) {
+    let alarm = { networkId: aRecord.networkId,
+                  threshold: aRecord.threshold,
+                  data: aRecord.data,
+                  manifestURL: aRecord.manifestURL,
+                  pageURL: aRecord.pageURL };
+
+    if (aRecord.id) {
+      alarm.id = aRecord.id;
+    }
+
+    return alarm;
+  },
+
+  addAlarm: function addAlarm(aAlarm, aResultCb) {
+    this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
+      if (DEBUG) {
+        debug("Going to add " + JSON.stringify(aAlarm));
+      }
+
+      let record = this.alarmToRecord(aAlarm);
+      store.put(record).onsuccess = function setResult(aEvent) {
+        txn.result = aEvent.target.result;
+        if (DEBUG) {
+          debug("Request successful. New record ID: " + txn.result);
+        }
+      };
+    }.bind(this), aResultCb);
+  },
+
+  getFirstAlarm: function getFirstAlarm(aNetworkId, aResultCb) {
+    let self = this;
+
+    this.dbNewTxn(ALARMS_STORE_NAME, "readonly", function(txn, store) {
+      if (DEBUG) {
+        debug("Get first alarm for network " + aNetworkId);
+      }
+
+      let lowerFilter = [aNetworkId, 0];
+      let upperFilter = [aNetworkId, ""];
+      let range = IDBKeyRange.bound(lowerFilter, upperFilter);
+
+      store.index("alarm").openCursor(range).onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        txn.result = null;
+        if (cursor) {
+          txn.result = self.recordToAlarm(cursor.value);
+        }
+      };
+    }, aResultCb);
+  },
+
+  removeAlarm: function removeAlarm(aAlarmId, aManifestURL, aResultCb) {
+    this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
+      if (DEBUG) {
+        debug("Remove alarm " + aAlarmId);
+      }
+
+      store.get(aAlarmId).onsuccess = function onsuccess(event) {
+        let record = event.target.result;
+        txn.result = false;
+        if (!record || (aManifestURL && record.manifestURL != aManifestURL)) {
+          return;
+        }
+
+        store.delete(aAlarmId);
+        txn.result = true;
+      }
+    }, aResultCb);
+  },
+
+  removeAlarms: function removeAlarms(aManifestURL, aResultCb) {
+    this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
+      if (DEBUG) {
+        debug("Remove alarms of " + aManifestURL);
+      }
+
+      store.index("manifestURL").openCursor(aManifestURL)
+                                .onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          cursor.delete();
+          cursor.continue();
+        }
+      }
+    }, aResultCb);
+  },
+
+  updateAlarm: function updateAlarm(aAlarm, aResultCb) {
+    let self = this;
+    this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
+      if (DEBUG) {
+        debug("Update alarm " + aAlarm.id);
+      }
+
+      let record = self.alarmToRecord(aAlarm);
+      store.openCursor(record.id).onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        txn.result = false;
+        if (cursor) {
+          cursor.update(record);
+          txn.result = true;
+        }
+      }
+    }, aResultCb);
+  },
+
+  getAlarms: function getAlarms(aNetworkId, aManifestURL, aResultCb) {
+    this.dbNewTxn(ALARMS_STORE_NAME, "readonly", function(txn, store) {
+      if (DEBUG) {
+        debug("Get alarms for " + aManifestURL);
+      }
+
+      txn.result = [];
+      store.index("manifestURL").openCursor(aManifestURL)
+                                .onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        if (!cursor) {
+          return;
+        }
+
+        if (!aNetworkId || cursor.value.networkId == aNetworkId) {
+          let alarm = { id: cursor.value.id,
+                        networkId: cursor.value.networkId,
+                        threshold: cursor.value.threshold,
+                        data: cursor.value.data };
+
+          txn.result.push(alarm);
+        }
+
+        cursor.continue();
+      }
+    }, aResultCb);
+  }
 };
--- a/dom/network/src/NetworkStatsManager.js
+++ b/dom/network/src/NetworkStatsManager.js
@@ -25,74 +25,74 @@ if (isParentProcess) {
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 // NetworkStatsData
 const nsIClassInfo              = Ci.nsIClassInfo;
 const NETWORKSTATSDATA_CID      = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}");
-const nsIDOMMozNetworkStatsData = Components.interfaces.nsIDOMMozNetworkStatsData;
+const nsIDOMMozNetworkStatsData = Ci.nsIDOMMozNetworkStatsData;
 
 function NetworkStatsData(aData) {
   this.rxBytes = aData.rxBytes;
   this.txBytes = aData.txBytes;
   this.date = aData.date;
 }
 
 NetworkStatsData.prototype = {
   __exposedProps__: {
-                      rxBytes: 'r',
-                      txBytes: 'r',
-                      date:  'r',
-                     },
+    rxBytes: 'r',
+    txBytes: 'r',
+    date:  'r',
+  },
 
   classID : NETWORKSTATSDATA_CID,
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSDATA_CID,
                                      contractID:"@mozilla.org/networkstatsdata;1",
                                      classDescription: "NetworkStatsData",
                                      interfaces: [nsIDOMMozNetworkStatsData],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
 };
 
 // NetworkStatsInterface
 const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
 const NETWORKSTATSINTERFACE_CID        = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
-const nsIDOMMozNetworkStatsInterface   = Components.interfaces.nsIDOMMozNetworkStatsInterface;
+const nsIDOMMozNetworkStatsInterface   = Ci.nsIDOMMozNetworkStatsInterface;
 
 function NetworkStatsInterface(aNetwork) {
   if (DEBUG) {
     debug("NetworkStatsInterface Constructor");
   }
   this.type = aNetwork.type;
   this.id = aNetwork.id;
 }
 
 NetworkStatsInterface.prototype = {
   __exposedProps__: {
-                      id: 'r',
-                      type: 'r',
-                    },
+    id: 'r',
+    type: 'r',
+  },
 
   classID : NETWORKSTATSINTERFACE_CID,
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
                                      contractID: NETWORKSTATSINTERFACE_CONTRACTID,
                                      classDescription: "NetworkStatsInterface",
                                      interfaces: [nsIDOMMozNetworkStatsInterface],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
 }
 
 // NetworkStats
 const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
 const NETWORKSTATS_CID        = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
-const nsIDOMMozNetworkStats   = Components.interfaces.nsIDOMMozNetworkStats;
+const nsIDOMMozNetworkStats   = Ci.nsIDOMMozNetworkStats;
 
 function NetworkStats(aWindow, aStats) {
   if (DEBUG) {
     debug("NetworkStats Constructor");
   }
   this.manifestURL = aStats.manifestURL || null;
   this.network = new NetworkStatsInterface(aStats.network);
   this.start = aStats.start || null;
@@ -101,40 +101,69 @@ function NetworkStats(aWindow, aStats) {
   let samples = this.data = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aStats.data.length; i++) {
     samples.push(new NetworkStatsData(aStats.data[i]));
   }
 }
 
 NetworkStats.prototype = {
   __exposedProps__: {
-                      manifestURL: 'r',
-                      network: 'r',
-                      start: 'r',
-                      end:  'r',
-                      data:  'r',
-                    },
+    manifestURL: 'r',
+    network: 'r',
+    start: 'r',
+    end:  'r',
+    data:  'r',
+  },
 
   classID : NETWORKSTATS_CID,
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID,
                                      contractID: NETWORKSTATS_CONTRACTID,
                                      classDescription: "NetworkStats",
                                      interfaces: [nsIDOMMozNetworkStats],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
                                           nsIDOMMozNetworkStatsData,
                                           nsIDOMMozNetworkStatsInterface])
 }
 
+// NetworkStatsAlarm
+const NETWORKSTATSALARM_CID      = Components.ID("{063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}");
+const nsIDOMMozNetworkStatsAlarm = Ci.nsIDOMMozNetworkStatsAlarm;
+
+function NetworkStatsAlarm(aAlarm) {
+  this.alarmId = aAlarm.id;
+  this.network = new NetworkStatsInterface(aAlarm.network);
+  this.threshold = aAlarm.threshold;
+  this.data = aAlarm.data;
+}
+
+NetworkStatsAlarm.prototype = {
+  __exposedProps__: {
+    alarmId: 'r',
+    network: 'r',
+    threshold: 'r',
+    data: 'r',
+  },
+
+  classID : NETWORKSTATSALARM_CID,
+  classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSALARM_CID,
+                                     contractID:"@mozilla.org/networkstatsalarm;1",
+                                     classDescription: "NetworkStatsAlarm",
+                                     interfaces: [nsIDOMMozNetworkStatsAlarm],
+                                     flags: nsIClassInfo.DOM_OBJECT}),
+
+  QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsAlarm])
+};
+
 // NetworkStatsManager
 
 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
-const NETWORKSTATSMANAGER_CID        = Components.ID("{5f033d31-c9a2-4e2d-83aa-6a807f1e0c11}");
-const nsIDOMMozNetworkStatsManager   = Components.interfaces.nsIDOMMozNetworkStatsManager;
+const NETWORKSTATSMANAGER_CID        = Components.ID("{50d109b8-0d7f-4208-81fe-5f07a759f159}");
+const nsIDOMMozNetworkStatsManager   = Ci.nsIDOMMozNetworkStatsManager;
 
 function NetworkStatsManager() {
   if (DEBUG) {
     debug("Constructor");
   }
 }
 
 NetworkStatsManager.prototype = {
@@ -184,16 +213,62 @@ NetworkStatsManager.prototype = {
     this.checkPrivileges();
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:ClearAll",
                           {id: this.getRequestId(request)});
     return request;
   },
 
+  addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
+    this.checkPrivileges();
+
+    if (!aOptions) {
+      aOptions = Object.create(null);
+    }
+
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
+                          {id: this.getRequestId(request),
+                           data: {network: aNetwork,
+                                  threshold: aThreshold,
+                                  startTime: aOptions.startTime,
+                                  data: aOptions.data,
+                                  manifestURL: this.manifestURL,
+                                  pageURL: this.pageURL}});
+    return request;
+  },
+
+  getAllAlarms: function getAllAlarms(aNetwork) {
+    this.checkPrivileges();
+
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
+                          {id: this.getRequestId(request),
+                           data: {network: aNetwork,
+                                  manifestURL: this.manifestURL}});
+    return request;
+  },
+
+  removeAlarms: function removeAlarms(aAlarmId) {
+    this.checkPrivileges();
+
+    if (aAlarmId == 0) {
+      aAlarmId = -1;
+    }
+
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
+                          {id: this.getRequestId(request),
+                           data: {alarmId: aAlarmId,
+                                  manifestURL: this.manifestURL}});
+
+    return request;
+  },
+
   getAvailableNetworks: function getAvailableNetworks() {
     this.checkPrivileges();
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks",
                           { id: this.getRequestId(request) });
     return request;
   },
@@ -207,18 +282,18 @@ NetworkStatsManager.prototype = {
     this.checkPrivileges();
     return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
   },
 
   receiveMessage: function(aMessage) {
     if (DEBUG) {
       debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
     }
+
     let msg = aMessage.json;
-
     let req = this.takeRequest(msg.id);
     if (!req) {
       if (DEBUG) {
         debug("No request stored with id " + msg.id);
       }
       return;
     }
 
@@ -255,16 +330,40 @@ NetworkStatsManager.prototype = {
         if (msg.error) {
           Services.DOMRequest.fireError(req, msg.error);
           return;
         }
 
         Services.DOMRequest.fireSuccess(req, true);
         break;
 
+      case "NetworkStats:SetAlarm:Return":
+      case "NetworkStats:RemoveAlarms:Return":
+        if (msg.error) {
+          Services.DOMRequest.fireError(req, msg.error);
+          return;
+        }
+
+        Services.DOMRequest.fireSuccess(req, msg.result);
+        break;
+
+      case "NetworkStats:GetAlarms:Return":
+        if (msg.error) {
+          Services.DOMRequest.fireError(req, msg.error);
+          return;
+        }
+
+        let alarms = Cu.createArrayIn(this._window);
+        for (let i = 0; i < msg.result.length; i++) {
+          alarms.push(new NetworkStatsAlarm(msg.result[i]));
+        }
+
+        Services.DOMRequest.fireSuccess(req, alarms);
+        break;
+
       default:
         if (DEBUG) {
           debug("Wrong message: " + aMessage.name);
         }
     }
   },
 
   init: function(aWindow) {
@@ -288,17 +387,31 @@ NetworkStatsManager.prototype = {
 
     if (!this.hasPrivileges) {
       return null;
     }
 
     this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
                                         "NetworkStats:GetAvailableNetworks:Return",
                                         "NetworkStats:Clear:Return",
-                                        "NetworkStats:ClearAll:Return"]);
+                                        "NetworkStats:ClearAll:Return",
+                                        "NetworkStats:SetAlarm:Return",
+                                        "NetworkStats:GetAlarms:Return",
+                                        "NetworkStats:RemoveAlarms:Return"]);
+
+    // Init app properties.
+    let appsService = Cc["@mozilla.org/AppsService;1"]
+                        .getService(Ci.nsIAppsService);
+
+    this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
+
+    let isApp = !!this.manifestURL.length;
+    if (isApp) {
+      this.pageURL = principal.URI.spec;
+    }
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) {
       debug("uninit call");
     }
   },
@@ -311,12 +424,13 @@ NetworkStatsManager.prototype = {
 
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
                                      contractID: NETWORKSTATSMANAGER_CONTRACTID,
                                      classDescription: "NetworkStatsManager",
                                      interfaces: [nsIDOMMozNetworkStatsManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
+                                                     NetworkStatsData,
                                                      NetworkStatsInterface,
                                                      NetworkStats,
                                                      NetworkStatsManager]);
--- a/dom/network/src/NetworkStatsManager.manifest
+++ b/dom/network/src/NetworkStatsManager.manifest
@@ -2,11 +2,14 @@ component {3b16fe17-5583-483a-b486-b64a3
 contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
 
 component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js
 contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}
 
 component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
 contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
 
-component {5f033d31-c9a2-4e2d-83aa-6a807f1e0c11} NetworkStatsManager.js
-contract @mozilla.org/networkStatsManager;1 {5f033d31-c9a2-4e2d-83aa-6a807f1e0c11}
+component {063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c} NetworkStatsManager.js
+contract @mozilla.org/networkstatsalarm;1 {063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}
+
+component {50d109b8-0d7f-4208-81fe-5f07a759f159} NetworkStatsManager.js
+contract @mozilla.org/networkStatsManager;1 {50d109b8-0d7f-4208-81fe-5f07a759f159}
 category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -17,16 +17,18 @@ this.EXPORTED_SYMBOLS = ["NetworkStatsSe
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
 const NET_NETWORKSTATSSERVICE_CONTRACTID = "@mozilla.org/network/netstatsservice;1";
 const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471e7f6c0a4}");
 
+const TOPIC_BANDWIDTH_CONTROL = "netd-bandwidth-control"
+
 const TOPIC_INTERFACE_REGISTERED   = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
 const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
 
 // The maximum traffic amount can be saved in the |cachedAppStats|.
 const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
 
@@ -45,23 +47,28 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "messenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
 this.NetworkStatsService = {
   init: function() {
     debug("Service started");
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false);
+    Services.obs.addObserver(this, TOPIC_BANDWIDTH_CONTROL, false);
     Services.obs.addObserver(this, "profile-after-change", false);
 
     this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
     // Object to store network interfaces, each network interface is composed
     // by a network object (network type and network Id) and a interfaceName
     // that contains the name of the physical interface (wlan0, rmnet0, etc.).
     // The network type can be 0 for wifi or 1 for mobile. On the other hand,
@@ -82,16 +89,19 @@ this.NetworkStatsService = {
     let netId = this.getNetworkId('0', NET_TYPE_WIFI);
     this._networks[netId] = { network:       { id: '0',
                                                type: NET_TYPE_WIFI },
                               interfaceName: null };
 
     this.messages = ["NetworkStats:Get",
                      "NetworkStats:Clear",
                      "NetworkStats:ClearAll",
+                     "NetworkStats:SetAlarm",
+                     "NetworkStats:GetAlarms",
+                     "NetworkStats:RemoveAlarms",
                      "NetworkStats:GetAvailableNetworks",
                      "NetworkStats:SampleRate",
                      "NetworkStats:MaxStorageAge"];
 
     this.messages.forEach(function(aMsgName) {
       ppmm.addMessageListener(aMsgName, this);
     }, this);
 
@@ -102,16 +112,19 @@ this.NetworkStatsService = {
                                 Ci.nsITimer.TYPE_REPEATING_PRECISE);
 
     // App stats are firstly stored in the cached.
     this.cachedAppStats = Object.create(null);
     this.cachedAppStatsDate = new Date();
 
     this.updateQueue = [];
     this.isQueueRunning = false;
+
+    this._currentAlarms = {};
+    this.initAlarms();
   },
 
   receiveMessage: function(aMessage) {
     if (!aMessage.target.assertPermission("networkstats-manage")) {
       return;
     }
 
     debug("receiveMessage " + aMessage.name);
@@ -124,16 +137,25 @@ this.NetworkStatsService = {
         this.getSamples(mm, msg);
         break;
       case "NetworkStats:Clear":
         this.clearInterfaceStats(mm, msg);
         break;
       case "NetworkStats:ClearAll":
         this.clearDB(mm, msg);
         break;
+      case "NetworkStats:SetAlarm":
+        this.setAlarm(mm, msg);
+        break;
+      case "NetworkStats:GetAlarms":
+        this.getAlarms(mm, msg);
+        break;
+      case "NetworkStats:RemoveAlarms":
+        this.removeAlarms(mm, msg);
+        break;
       case "NetworkStats:GetAvailableNetworks":
         this.getAvailableNetworks(mm, msg);
         break;
       case "NetworkStats:SampleRate":
         // This message is sync.
         return this._db.sampleRate;
       case "NetworkStats:MaxStorageAge":
         // This message is sync.
@@ -141,42 +163,61 @@ this.NetworkStatsService = {
     }
   },
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case TOPIC_INTERFACE_REGISTERED:
       case TOPIC_INTERFACE_UNREGISTERED:
 
-        // If new interface is registered (notified from NetworkManager),
+        // If new interface is registered (notified from NetworkService),
         // the stats are updated for the new interface without waiting to
         // complete the updating period.
 
         let network = aSubject.QueryInterface(Ci.nsINetworkInterface);
         debug("Network " + network.name + " of type " + network.type + " status change");
 
         let netId = this.convertNetworkInterface(network);
         if (!netId) {
           break;
         }
 
+        this._updateCurrentAlarm(netId);
+
         debug("NetId: " + netId);
         this.updateStats(netId);
         break;
+
+      case TOPIC_BANDWIDTH_CONTROL:
+        debug("Bandwidth message from netd: " + JSON.stringify(aData));
+
+        let interfaceName = aData.substring(aData.lastIndexOf(" ") + 1);
+        for (let networkId in this._networks) {
+          if (interfaceName == this._networks[networkId].interfaceName) {
+            let currentAlarm = this._currentAlarms[networkId];
+            if (Object.getOwnPropertyNames(currentAlarm).length !== 0) {
+              this._fireAlarm(currentAlarm.alarm);
+            }
+            break;
+          }
+        }
+        break;
+
       case "xpcom-shutdown":
         debug("Service shutdown");
 
         this.messages.forEach(function(aMsgName) {
           ppmm.removeMessageListener(aMsgName, this);
         }, this);
 
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, "profile-after-change");
         Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
         Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
+        Services.obs.removeObserver(this, TOPIC_BANDWIDTH_CONTROL);
 
         this.timer.cancel();
         this.timer = null;
 
         // Update stats before shutdown
         this.updateAllStats();
         break;
     }
@@ -261,16 +302,35 @@ this.NetworkStatsService = {
         }
       }
 
       mm.sendAsyncMessage("NetworkStats:GetAvailableNetworks:Return",
                           { id: msg.id, error: aError, result: aResult });
     });
   },
 
+  initAlarms: function initAlarms() {
+    debug("Init usage alarms");
+    let self = this;
+
+    for (let netId in this._networks) {
+      this._currentAlarms[netId] = Object.create(null);
+
+      this._db.getFirstAlarm(netId, function getResult(error, result) {
+        if (!error && result) {
+          self._setAlarm(result, function onSet(error, success) {
+            if (error == "InvalidStateError") {
+              self._fireAlarm(result);
+            }
+          });
+        }
+      });
+    }
+  },
+
   /*
    * Function called from manager to get stats from database.
    * In order to return updated stats, first is performed a call to
    * updateAllStats function, which will get last stats from netd
    * and update the database.
    * Then, depending on the request (stats per appId or total stats)
    * it retrieve them from database and return to the manager.
    */
@@ -477,18 +537,18 @@ this.NetworkStatsService = {
       let item = this.updateQueue.shift();
       for (let callback of item.callbacks) {
         if(callback) {
           callback(aResult, aMessage);
         }
       }
     } else {
       // The caller is a function that has pushed new elements to the queue,
-      // if isQueueRunning is false it means there is no processing currently being
-      // done, so start.
+      // if isQueueRunning is false it means there is no processing currently
+      // being done, so start.
       if (this.isQueueRunning) {
         if(this.updateQueue.length > 1) {
           return;
         }
       } else {
         this.isQueueRunning = true;
       }
     }
@@ -510,17 +570,17 @@ this.NetworkStatsService = {
         aCallback(false, "Invalid network " + aNetId);
       }
       return;
     }
 
     let interfaceName = this._networks[aNetId].interfaceName;
     debug("Update stats for " + interfaceName);
 
-    // Request stats to NetworkManager, which will get stats from netd, passing
+    // Request stats to NetworkService, which will get stats from netd, passing
     // 'networkStatsAvailable' as a callback.
     if (interfaceName) {
       networkService.getNetworkInterfaceStats(interfaceName,
                 this.networkStatsAvailable.bind(this, aCallback, aNetId));
       return;
     }
 
     if (aCallback) {
@@ -701,11 +761,267 @@ this.NetworkStatsService = {
         return;
       }
 
       debug("===== LOG =====");
       debug("There are " + aResult.length + " items");
       debug(JSON.stringify(aResult));
     });
   },
+
+  getAlarms: function getAlarms(mm, msg) {
+    let network = msg.data.network;
+    let manifestURL = msg.data.manifestURL;
+
+    let netId = null;
+    if (network) {
+      netId = this.getNetworkId(network.id, network.type);
+      if (!this._networks[netId]) {
+        mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
+                            { id: msg.id, error: "InvalidInterface", result: null });
+        return;
+      }
+    }
+
+    let self = this;
+    this._db.getAlarms(netId, manifestURL, function onCompleted(error, result) {
+      if (error) {
+        mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
+                            { id: msg.id, error: error, result: result });
+        return;
+      }
+
+      let alarms = []
+      // NetworkStatsManager must return the network instead of the networkId.
+      for (let i = 0; i < result.length; i++) {
+        let alarm = result[i];
+        alarms.push({ id: alarm.id,
+                      network: self._networks[alarm.networkId].network,
+                      threshold: alarm.threshold,
+                      data: alarm.data });
+      }
+
+      mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
+                          { id: msg.id, error: null, result: alarms });
+    });
+  },
+
+  removeAlarms: function removeAlarms(mm, msg) {
+    let alarmId = msg.data.alarmId;
+    let manifestURL = msg.data.manifestURL;
+
+    let self = this;
+    let callback = function onRemove(error, result) {
+      if (error) {
+        mm.sendAsyncMessage("NetworkStats:RemoveAlarms:Return",
+                            { id: msg.id, error: error, result: result });
+        return;
+      }
+
+      for (let i in self._currentAlarms) {
+        let currentAlarm = self._currentAlarms[i].alarm;
+        if (currentAlarm && ((alarmId == currentAlarm.id) ||
+            (alarmId == -1 && currentAlarm.manifestURL == manifestURL))) {
+
+          self._updateCurrentAlarm(currentAlarm.networkId);
+        }
+      }
+
+      mm.sendAsyncMessage("NetworkStats:RemoveAlarms:Return",
+                          { id: msg.id, error: error, result: true });
+    };
+
+    if (alarmId == -1) {
+      this._db.removeAlarms(manifestURL, callback);
+    } else {
+      this._db.removeAlarm(alarmId, manifestURL, callback);
+    }
+  },
+
+  /*
+   * Function called from manager to set an alarm.
+   */
+  setAlarm: function setAlarm(mm, msg) {
+    let options = msg.data;
+    let network = options.network;
+    let threshold = options.threshold;
+
+    debug("Set alarm at " + threshold + " for " + JSON.stringify(network));
+
+    if (threshold < 0) {
+      mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
+                          { id: msg.id, error: "InvalidThresholdValue", result: null });
+      return;
+    }
+
+    let netId = this.getNetworkId(network.id, network.type);
+    if (!this._networks[netId]) {
+      mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
+                          { id: msg.id, error: "InvalidiConnectionType", result: null });
+      return;
+    }
+
+    let newAlarm = {
+      id: null,
+      networkId: netId,
+      threshold: threshold,
+      absoluteThreshold: null,
+      startTime: options.startTime,
+      data: options.data,
+      pageURL: options.pageURL,
+      manifestURL: options.manifestURL
+    };
+
+    let self = this;
+    this._updateThreshold(newAlarm, function onUpdate(error, _threshold) {
+      if (error) {
+        mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
+                            { id: msg.id, error: error, result: null });
+        return;
+      }
+
+      newAlarm.absoluteThreshold = _threshold.absoluteThreshold;
+      self._db.addAlarm(newAlarm, function addSuccessCb(error, newId) {
+        if (error) {
+          mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
+                              { id: msg.id, error: error, result: null });
+          return;
+        }
+
+        newAlarm.id = newId;
+        self._setAlarm(newAlarm, function onSet(error, success) {
+          mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
+                              { id: msg.id, error: error, result: newId });
+
+          if (error == "InvalidStateError") {
+            self._fireAlarm(newAlarm);
+          }
+        });
+      });
+    });
+  },
+
+  _setAlarm: function _setAlarm(aAlarm, aCallback) {
+    let currentAlarm = this._currentAlarms[aAlarm.networkId];
+    if (Object.getOwnPropertyNames(currentAlarm).length !== 0 &&
+        aAlarm.absoluteThreshold > currentAlarm.alarm.absoluteThreshold) {
+      aCallback(null, true);
+      return;
+    }
+
+    let self = this;
+
+    this._updateThreshold(aAlarm, function onUpdate(aError, aThreshold) {
+      if (aError) {
+        aCallback(aError, null);
+        return;
+      }
+
+      let callback = function onAlarmSet(aError) {
+        if (aError) {
+          debug("Set alarm error: " + aError);
+          aCallback("netdError", null);
+          return;
+        }
+
+        self._currentAlarms[aAlarm.networkId].alarm = aAlarm;
+
+        aCallback(null, true);
+      };
+
+      debug("Set alarm " + JSON.stringify(aAlarm));
+      let interfaceName = self._networks[aAlarm.networkId].interfaceName;
+      if (interfaceName) {
+        networkService.setNetworkInterfaceAlarm(interfaceName,
+                                                aThreshold.systemThreshold,
+                                                callback);
+        return;
+      }
+
+      aCallback(null, true);
+    });
+  },
+
+  _updateThreshold: function _updateThreshold(aAlarm, aCallback) {
+    let self = this;
+    this.updateStats(aAlarm.networkId, function onStatsUpdated(aResult, aMessage) {
+      self._db.getCurrentStats(self._networks[aAlarm.networkId].network,
+                               aAlarm.startTime,
+                               function onStatsFound(error, result) {
+        if (error) {
+          debug("Error getting stats for " +
+                JSON.stringify(self._networks[aAlarm.networkId]) + ": " + error);
+          aCallback(error, result);
+          return;
+        }
+
+        let offset = aAlarm.threshold - result.rxTotalBytes - result.txTotalBytes;
+
+        // Alarm set to a threshold lower than current rx/tx bytes.
+        if (offset <= 0) {
+          aCallback("InvalidStateError", null);
+          return;
+        }
+
+        let threshold = {
+          systemThreshold: result.rxSystemBytes + result.txSystemBytes + offset,
+          absoluteThreshold: result.rxTotalBytes + result.txTotalBytes + offset
+        };
+
+        aCallback(null, threshold);
+      });
+    });
+  },
+
+  _fireAlarm: function _fireAlarm(aAlarm) {
+    debug("Fire alarm");
+
+    let self = this;
+    this._db.removeAlarm(aAlarm.id, null, function onRemove(aError, aResult){
+      if (!aError && !aResult) {
+        return;
+      }
+
+      self._fireSystemMessage(aAlarm);
+      self._updateCurrentAlarm(aAlarm.networkId);
+    });
+  },
+
+  _updateCurrentAlarm: function _updateCurrentAlarm(aNetworkId) {
+    this._currentAlarms[aNetworkId] = Object.create(null);
+
+    let self = this;
+    this._db.getFirstAlarm(aNetworkId, function onGet(error, result){
+      if (error) {
+        debug("Error getting the first alarm");
+        return;
+      }
+
+      if (!result) {
+        let interfaceName = self._networks[aNetworkId].interfaceName;
+        networkService.setNetworkInterfaceAlarm(interfaceName, -1,
+                                                function onComplete(){});
+        return;
+      }
+
+      self._setAlarm(result, function onSet(error, success){
+        if (error == "InvalidStateError") {
+          self._fireAlarm(result);
+          return;
+        }
+      });
+    });
+  },
+
+  _fireSystemMessage: function _fireSystemMessage(aAlarm) {
+    debug("Fire system message: " + JSON.stringify(aAlarm));
+
+    let manifestURI = Services.io.newURI(aAlarm.manifestURL, null, null);
+    let pageURI = Services.io.newURI(aAlarm.pageURL, null, null);
+
+    let alarm = { "id":        aAlarm.id,
+                  "threshold": aAlarm.threshold,
+                  "data":      aAlarm.data };
+    messenger.sendMessage("networkstats-alarm", alarm, pageURI, manifestURI);
+  }
 };
 
 NetworkStatsService.init();
--- a/dom/network/tests/Makefile.in
+++ b/dom/network/tests/Makefile.in
@@ -10,10 +10,11 @@ MOCHITEST_FILES = \
   $(NULL)
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 MOCHITEST_FILES = \
   test_networkstats_basics.html \
   test_networkstats_disabled.html \
   test_networkstats_enabled_no_perm.html \
   test_networkstats_enabled_perm.html \
+  test_networkstats_alarms.html \
   $(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/test_networkstats_alarms.html
@@ -0,0 +1,175 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for NetworkStats alarms</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function test() {
+  ok(true, "Checking if no alarms are set.");
+
+  req = navigator.mozNetworkStats.getAllAlarms();
+
+  req.onsuccess = function () {
+    ok(true, "Succeeded to get alarms.");
+    ok(Array.isArray(req.result) && req.result.length == 0,
+       "There are no alarms set.");
+    next();
+  };
+
+  req.onerror = function () {
+    ok(false, "getAllAlarms() shouldn't fail!");
+  }
+}
+
+var req;
+var index = -1;
+
+var wifi = {'type': 0, 'id': '0'};
+var mobile = {'type': 1, 'id': '1'};
+
+var steps = [
+  function () {
+    ok(true, "Calling getAllAlarms() with invalid network.");
+
+    req = navigator.mozNetworkStats.getAllAlarms(mobile);
+
+    req.onsuccess = function () {
+      ok(false, "getAllAlarms() shouldn't succeed!");
+    };
+
+    req.onerror = function () {
+      ok(req.error.name == "InvalidInterface", "Get InvalidInterface error");
+      next();
+    }
+  },
+  function () {
+    ok(true, "Calling addAlarm() with invalid network or parameters.");
+
+    try {
+      navigator.mozNetworkStats.addAlarm();
+    } catch(ex) {
+      ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
+         "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no parameters");
+    }
+
+    try {
+      navigator.mozNetworkStats.addAlarm(100000);
+    } catch(ex) {
+      ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
+         "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no network");
+    }
+
+    try {
+      navigator.mozNetworkStats.addAlarm(wifi);
+    } catch(ex) {
+      ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
+         "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no threshold");
+    }
+
+    req = navigator.mozNetworkStats.addAlarm(mobile, -100000);
+
+    req.onsuccess = function () {
+      ok(false, "addAlarm() shouldn't succeed with negative threshold.");
+    };
+
+    req.onerror = function () {
+      ok(req.error.name == "InvalidThresholdValue", "Get InvalidThresholdValue error");
+      next();
+    }
+  },
+  function () {
+    ok(true, "Calling addAlarm()");
+
+    req = navigator.mozNetworkStats.addAlarm(wifi, 1000000);
+
+    req.onsuccess = function () {
+      ok(true, "Succeeded to add alarm. AlarmId: " + req.result);
+      next();
+    };
+    req.onerror = function () {
+      ok(false, "addAlarm() shouldn't fail.");
+    };
+  },
+  function () {
+    ok(true, "Calling getAllAlarms()");
+
+    req = navigator.mozNetworkStats.getAllAlarms(wifi);
+
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "Only one alarm");
+      ok(req.result[0].alarmId == 1, "Get correct alarmId");
+      next();
+    };
+
+    req.onerror = function () {
+      ok(false, "getAllAlarms() shouldn't fail.");
+    }
+  },
+  function () {
+    ok(true, "Calling removeAlarms() to remove alarms.");
+
+    req = navigator.mozNetworkStats.removeAlarms();
+
+    req.onsuccess = function () {
+      ok(req.result, "Succeeded to remove alarms.");
+      next();
+    };
+
+    req.onerror = function () {
+      ok(false, "removeAlarms() shouldn't fail.");
+    }
+  },
+  function () {
+    ok(true, "Checking if all alarms are removed.");
+
+    req = navigator.mozNetworkStats.getAllAlarms();
+
+    req.onsuccess = function () {
+      ok(Array.isArray(req.result) && req.result.length == 0,
+         "Succeeded to remove all alarms.");
+      next();
+    };
+
+    req.onerror = function () {
+      ok(false, "getAllAlarms() shouldn't fail.");
+    }
+  },
+  function () {
+    ok(true, "all done!\n");
+    SpecialPowers.removePermission("networkstats-manage", document);
+    SimpleTest.finish();
+    return;
+  }
+];
+
+function next() {
+  index += 1;
+  if (index >= steps.length) {
+    ok(false, "Shouldn't get here!");
+    return;
+  }
+  try {
+    steps[index]();
+  } catch(ex) {
+    ok(false, "Caught exception", ex);
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.addPermission("networkstats-manage", true, document);
+SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", true]]}, test);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/network/tests/test_networkstats_basics.html
+++ b/dom/network/tests/test_networkstats_basics.html
@@ -8,20 +8,18 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 function test() {
-  ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
-  ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
-
-  netStats = navigator.mozNetworkStats;
+  netStats = window.navigator.mozNetworkStats;
+  ok(netStats, "mozNetworkStats exists");
 
   // Test IDL attributes
   ok('sampleRate' in netStats,
    "sampleRate should be a NetworkStats attribute");
   ok(netStats.sampleRate > 0,
    "sampleRate is greater than 0.");
 
   ok('maxStorageAge' in netStats,
--- a/dom/network/tests/unit_stats/test_networkstats_db.js
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -2,22 +2,38 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
 const netStatsDb = new NetworkStatsDB();
 
-function clearWholeDB(callback) {
-  netStatsDb.dbNewTxn("readwrite", function(aTxn, aStore) {
-    aStore.delete();
+function clearStore(store, callback) {
+  netStatsDb.dbNewTxn(store, "readwrite", function(aTxn, aStore) {
+    aStore.openCursor().onsuccess = function (event) {
+      let cursor = event.target.result;
+      if (cursor){
+        cursor.delete();
+        cursor.continue();
+      }
+    };
   }, callback);
 }
 
+add_test(function prepareDatabase() {
+  // Clear whole database to avoid starting tests with unknown state
+  // due to the previous tests.
+  clearStore('net_stats', function() {
+    clearStore('net_alarm', function() {
+      run_next_test();
+    });
+  });
+});
+
 function filterTimestamp(date) {
   var sampleRate = netStatsDb.sampleRate;
   var offset = date.getTimezoneOffset() * 60 * 1000;
   return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
 }
 
 function getNetworks() {
   return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI },
@@ -117,37 +133,41 @@ add_test(function test_clear_interface()
     do_check_eq(error, null);
     run_next_test();
   });
 });
 
 add_test(function test_internalSaveStats_singleSample() {
   var networks = getNetworks();
 
-  var stats = { appId:        0,
-                network:      [networks[0].id, networks[0].type],
-                timestamp:    Date.now(),
-                rxBytes:      0,
-                txBytes:      0,
-                rxTotalBytes: 1234,
-                txTotalBytes: 1234 };
+  var stats = { appId:         0,
+                network:       [networks[0].id, networks[0].type],
+                timestamp:     Date.now(),
+                rxBytes:       0,
+                txBytes:       0,
+                rxSystemBytes: 1234,
+                txSystemBytes: 1234,
+                rxTotalBytes:  1234,
+                txTotalBytes:  1234 };
 
-  netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+  netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
     netStatsDb._saveStats(txn, store, stats);
   }, function(error, result) {
     do_check_eq(error, null);
 
     netStatsDb.logAllRecords(function(error, result) {
       do_check_eq(error, null);
       do_check_eq(result.length, 1);
       do_check_eq(result[0].appId, stats.appId);
       do_check_true(compareNetworks(result[0].network, stats.network));
       do_check_eq(result[0].timestamp, stats.timestamp);
       do_check_eq(result[0].rxBytes, stats.rxBytes);
       do_check_eq(result[0].txBytes, stats.txBytes);
+      do_check_eq(result[0].rxSystemBytes, stats.rxSystemBytes);
+      do_check_eq(result[0].txSystemBytes, stats.txSystemBytes);
       do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
       do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
       run_next_test();
     });
   });
 });
 
 add_test(function test_internalSaveStats_arraySamples() {
@@ -156,45 +176,48 @@ add_test(function test_internalSaveStats
   netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
 
     var network = [networks[0].id, networks[0].type];
 
     var samples = 2;
     var stats = [];
     for (var i = 0; i < samples; i++) {
-      stats.push({ appId:        0,
-                   network:      network,
-                   timestamp:    Date.now() + (10 * i),
-                   rxBytes:      0,
-                   txBytes:      0,
-                   rxTotalBytes: 1234,
-                   txTotalBytes: 1234 });
+      stats.push({ appId:         0,
+                   network:       network,
+                   timestamp:     Date.now() + (10 * i),
+                   rxBytes:       0,
+                   txBytes:       0,
+                   rxSystemBytes: 1234,
+                   txSystemBytes: 1234,
+                   rxTotalBytes:  1234,
+                   txTotalBytes:  1234 });
     }
 
-    netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+    netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
 
         // Result has one sample more than samples because clear inserts
         // an empty sample to keep totalBytes synchronized with netd counters
         result.shift();
         do_check_eq(result.length, samples);
-
         var success = true;
         for (var i = 1; i < samples; i++) {
           if (result[i].appId != stats[i].appId ||
               !compareNetworks(result[i].network, stats[i].network) ||
               result[i].timestamp != stats[i].timestamp ||
               result[i].rxBytes != stats[i].rxBytes ||
               result[i].txBytes != stats[i].txBytes ||
+              result[i].rxSystemBytes != stats[i].rxSystemBytes ||
+              result[i].txSystemBytes != stats[i].txSystemBytes ||
               result[i].rxTotalBytes != stats[i].rxTotalBytes ||
               result[i].txTotalBytes != stats[i].txTotalBytes) {
             success = false;
             break;
           }
         }
         do_check_true(success);
         run_next_test();
@@ -208,28 +231,30 @@ add_test(function test_internalRemoveOld
 
   netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
 
     var network = [networks[0].id, networks[0].type];
     var samples = 10;
     var stats = [];
     for (var i = 0; i < samples - 1; i++) {
-      stats.push({ appId:              0,
-                   network:      network, timestamp:    Date.now() + (10 * i),
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 });
+      stats.push({ appId:               0,
+                   network:       network, timestamp:     Date.now() + (10 * i),
+                   rxBytes:             0, txBytes:       0,
+                   rxSystemBytes:    1234, txSystemBytes: 1234,
+                   rxTotalBytes:     1234, txTotalBytes:  1234 });
     }
 
-    stats.push({ appId:              0,
-                 network:      network, timestamp:    Date.now() + (10 * samples),
-                 rxBytes:            0, txBytes:      0,
-                 rxTotalBytes:    1234, txTotalBytes: 1234 });
+    stats.push({ appId:               0,
+                 network:       network, timestamp:     Date.now() + (10 * samples),
+                 rxBytes:             0, txBytes:       0,
+                 rxSystemBytes:    1234, txSystemBytes: 1234,
+                 rxTotalBytes:     1234, txTotalBytes:  1234 });
 
-    netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+    netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
       var date = stats[stats.length - 1].timestamp
                  + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
       netStatsDb._removeOldStats(txn, store, 0, network, date);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
@@ -240,20 +265,20 @@ add_test(function test_internalRemoveOld
       });
     });
   });
 });
 
 function processSamplesDiff(networks, lastStat, newStat, callback) {
   netStatsDb.clearStats(networks, function (error, result){
     do_check_eq(error, null);
-    netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+    netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, lastStat);
     }, function(error, result) {
-      netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+      netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
         let request = store.index("network").openCursor(newStat.network, "prev");
         request.onsuccess = function onsuccess(event) {
           let cursor = event.target.result;
           do_check_neq(cursor, null);
           netStatsDb._processSamplesDiff(txn, store, cursor, newStat);
         };
       }, function(error, result) {
         do_check_eq(error, null);
@@ -268,122 +293,135 @@ function processSamplesDiff(networks, la
 
 add_test(function test_processSamplesDiffSameSample() {
   var networks = getNetworks();
   var network = [networks[0].id, networks[0].type];
 
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
 
-  var lastStat = { appId:              0,
-                   network:      network, timestamp:    date,
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 };
+  var lastStat = { appId:               0,
+                   network:       network, timestamp:     date,
+                   rxBytes:             0, txBytes:       0,
+                   rxSystemBytes:    1234, txSystemBytes: 1234,
+                   rxTotalBytes:     2234, txTotalBytes:  2234 };
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date,
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:    2234, txTotalBytes: 2234 };
+  var newStat = { appId:               0,
+                  network:       network, timestamp:     date,
+                  rxBytes:             0, txBytes:       0,
+                  rxSystemBytes:    2234, txSystemBytes: 2234,
+                  rxTotalBytes:     2234, txTotalBytes:  2234 };
 
   processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, 1);
     do_check_eq(result[0].appId, newStat.appId);
     do_check_true(compareNetworks(result[0].network, newStat.network));
     do_check_eq(result[0].timestamp, newStat.timestamp);
-    do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
-    do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
-    do_check_eq(result[0].rxTotalBytes, newStat.rxTotalBytes);
-    do_check_eq(result[0].txTotalBytes, newStat.txTotalBytes);
+    do_check_eq(result[0].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
+    do_check_eq(result[0].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
+    do_check_eq(result[0].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+    do_check_eq(result[0].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
+    do_check_eq(result[0].rxSystemBytes, newStat.rxSystemBytes);
+    do_check_eq(result[0].txSystemBytes, newStat.txSystemBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffNextSample() {
   var networks = getNetworks();
   var network = [networks[0].id, networks[0].type];
 
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
 
-  var lastStat = { appId:              0,
-                   network:      network, timestamp:    date,
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 };
+  var lastStat = { appId:               0,
+                   network:       network, timestamp:     date,
+                   rxBytes:             0, txBytes:       0,
+                   rxSystemBytes:    1234, txSystemBytes: 1234,
+                   rxTotalBytes:     2234, txTotalBytes:  2234 };
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date + sampleRate,
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:     500, txTotalBytes: 500 };
+  var newStat = { appId:               0,
+                  network:       network, timestamp:     date + sampleRate,
+                  rxBytes:             0, txBytes:       0,
+                  rxSystemBytes:    1734, txSystemBytes: 1734,
+                  rxTotalBytes:        0, txTotalBytes:  0 };
 
   processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, 2);
     do_check_eq(result[1].appId, newStat.appId);
     do_check_true(compareNetworks(result[1].network, newStat.network));
     do_check_eq(result[1].timestamp, newStat.timestamp);
-    do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
-    do_check_eq(result[1].txBytes, newStat.txTotalBytes);
-    do_check_eq(result[1].rxTotalBytes, newStat.rxTotalBytes);
-    do_check_eq(result[1].txTotalBytes, newStat.txTotalBytes);
+    do_check_eq(result[1].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
+    do_check_eq(result[1].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
+    do_check_eq(result[1].rxSystemBytes, newStat.rxSystemBytes);
+    do_check_eq(result[1].txSystemBytes, newStat.txSystemBytes);
+    do_check_eq(result[1].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+    do_check_eq(result[1].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffSamplesLost() {
   var networks = getNetworks();
   var network = [networks[0].id, networks[0].type];
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-  var lastStat = { appId:             0,
-                   network:     network, timestamp:    date,
-                   rxBytes:           0, txBytes:      0,
-                   rxTotalBytes:   1234, txTotalBytes: 1234 };
+  var lastStat = { appId:              0,
+                   network:      network, timestamp:     date,
+                   rxBytes:            0, txBytes:       0,
+                   rxSystemBytes:   1234, txSystemBytes: 1234,
+                   rxTotalBytes:    2234, txTotalBytes:  2234};
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date + (sampleRate * samples),
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:    2234, txTotalBytes: 2234 };
+  var newStat = { appId:               0,
+                  network:       network, timestamp:     date + (sampleRate * samples),
+                  rxBytes:             0, txBytes:       0,
+                  rxSystemBytes:    2234, txSystemBytes: 2234,
+                  rxTotalBytes:        0, txTotalBytes:  0 };
 
   processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, samples + 1);
     do_check_eq(result[0].appId, newStat.appId);
     do_check_true(compareNetworks(result[samples].network, newStat.network));
     do_check_eq(result[samples].timestamp, newStat.timestamp);
     do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
     do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
-    do_check_eq(result[samples].rxTotalBytes, newStat.rxTotalBytes);
-    do_check_eq(result[samples].txTotalBytes, newStat.txTotalBytes);
+    do_check_eq(result[samples].rxSystemBytes, newStat.rxSystemBytes);
+    do_check_eq(result[samples].txSystemBytes, newStat.txSystemBytes);
+    do_check_eq(result[samples].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
+    do_check_eq(result[samples].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
     run_next_test();
   });
 });
 
 add_test(function test_saveStats() {
   var networks = getNetworks();
   var network = [networks[0].id, networks[0].type];
 
   var stats = { appId:       0,
                 networkId:   networks[0].id,
                 networkType: networks[0].type,
                 date:        new Date(),
                 rxBytes:     2234,
                 txBytes:     2234};
 
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
+  clearStore('net_stats', function() {
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
         do_check_eq(result.length, 1);
         do_check_eq(result[0].appId, stats.appId);
         do_check_true(compareNetworks(result[0].network, network));
         let timestamp = filterTimestamp(stats.date);
         do_check_eq(result[0].timestamp, timestamp);
-        do_check_eq(result[0].rxBytes, 0);
-        do_check_eq(result[0].txBytes, 0);
+        do_check_eq(result[0].rxBytes, stats.rxBytes);
+        do_check_eq(result[0].txBytes, stats.txBytes);
+        do_check_eq(result[0].rxSystemBytes, stats.rxBytes);
+        do_check_eq(result[0].txSystemBytes, stats.txBytes);
         do_check_eq(result[0].rxTotalBytes, stats.rxBytes);
         do_check_eq(result[0].txTotalBytes, stats.txBytes);
         run_next_test();
       });
     });
   });
 });
 
@@ -411,28 +449,30 @@ add_test(function test_saveAppStats() {
         // past tests and the new one for appId 1
         do_check_eq(result.length, 2);
         do_check_eq(result[1].appId, stats.appId);
         do_check_true(compareNetworks(result[1].network, network));
         let timestamp = filterTimestamp(stats.date);
         do_check_eq(result[1].timestamp, timestamp);
         do_check_eq(result[1].rxBytes, stats.rxBytes);
         do_check_eq(result[1].txBytes, stats.txBytes);
+        do_check_eq(result[1].rxSystemBytes, 0);
+        do_check_eq(result[1].txSystemBytes, 0);
         do_check_eq(result[1].rxTotalBytes, 0);
         do_check_eq(result[1].txTotalBytes, 0);
         run_next_test();
       });
     });
   });
 });
 
 function prepareFind(network, stats, callback) {
   netStatsDb.clearStats(network, function (error, result) {
     do_check_eq(error, null);
-    netStatsDb.dbNewTxn("readwrite", function(txn, store) {
+    netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
         callback(error, result);
     });
   });
 }
 
 add_test(function test_find () {
@@ -444,25 +484,28 @@ add_test(function test_find () {
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var start = Date.now();
   var saveDate = filterTimestamp(new Date());
   var end = new Date(start + (sampleRate * (samples - 1)));
   start = new Date(start - sampleRate);
   var stats = [];
   for (var i = 0; i < samples; i++) {
-    stats.push({ appId:              appId,
-                 network:      networkWifi, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                0, txBytes:      10,
-                 rxTotalBytes:           0, txTotalBytes: 0 });
+    stats.push({ appId:               appId,
+                 network:       networkWifi, timestamp:     saveDate + (sampleRate * i),
+                 rxBytes:                 0, txBytes:       10,
+                 rxSystemBytes:           0, txSystemBytes: 0,
+                 rxTotalBytes:            0, txTotalBytes:  0});
 
-    stats.push({ appId:                appId,
-                 network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                  0, txBytes:      10,
-                 rxTotalBytes:             0, txTotalBytes: 0 });
+
+    stats.push({ appId:                 appId,
+                 network:       networkMobile, timestamp:     saveDate + (sampleRate * i),
+                 rxBytes:                   0, txBytes:       10,
+                 rxSystemBytes:             0, txSystemBytes: 0,
+                 rxTotalBytes:              0, txTotalBytes:  0});
   }
 
   prepareFind(networks[0], stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
       do_check_eq(result.network.id, networks[0].id);
       do_check_eq(result.network.type, networks[0].type);
@@ -549,40 +592,217 @@ add_test(function test_saveMultipleAppSt
   netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(cached[keys[index]],
       function callback(error, result) {
         do_check_eq(error, null);
 
         if (index == keys.length - 1) {
           netStatsDb.logAllRecords(function(error, result) {
-          // Again, result has two samples more than expected samples because
-          // clear inserts one empty sample for each network to keep totalBytes
-          // synchronized with netd counters. so the first two samples have to
-          // be discarted.
-          result.shift();
-          result.shift();
+            // Again, result has two samples more than expected samples because
+            // clear inserts one empty sample for each network to keep totalBytes
+            // synchronized with netd counters. so the first two samples have to
+            // be discarted.
+            result.shift();
+            result.shift();
 
-          do_check_eq(error, null);
-          do_check_eq(result.length, 4);
-          do_check_eq(result[0].appId, 1);
-          do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
-          do_check_eq(result[0].rxBytes, 0);
-          do_check_eq(result[0].txBytes, 10);
-          run_next_test();
+            do_check_eq(error, null);
+            do_check_eq(result.length, 4);
+            do_check_eq(result[0].appId, 1);
+            do_check_true(compareNetworks(result[0].network, [networkWifi.id, networkWifi.type]));
+            do_check_eq(result[0].rxBytes, 0);
+            do_check_eq(result[0].txBytes, 10);
+            run_next_test();
           });
+         return;
         }
 
         index += 1;
         netStatsDb.saveStats(cached[keys[index]], callback);
     });
   });
 });
 
+var networkWifi = '00';
+var networkMobile = '11';
+
+var examplePageURL = "http://example.com/index.html";
+var exampleManifestURL = "http://example.com/manifest.webapp";
+
+var testPageURL = "http://test.com/index.html";
+var testManifestURL = "http://test.com/manifest.webapp";
+
+var alarms = [{ id:             null,
+                networkId:      networkWifi,
+                threshold:      10000,
+                data:           {foo: "something"},
+                pageURL:        examplePageURL,
+                manifestURL:    exampleManifestURL },
+              { id:             null,
+                networkId:      networkWifi,
+                threshold:      1000,
+                data:           {foo: "else"},
+                pageURL:        examplePageURL,
+                manifestURL:    exampleManifestURL },
+              { id:             null,
+                networkId:      networkMobile,
+                threshold:      100,
+                data:           {foo: "to"},
+                pageURL:        examplePageURL,
+                manifestURL:    exampleManifestURL },
+              { id:             null,
+                networkId:      networkMobile,
+                threshold:      10,
+                data:           {foo: "test"},
+                pageURL:        testPageURL,
+                manifestURL:    testManifestURL }];
+
+var alarmsDbId = 1;
+
+add_test(function test_addAlarm() {
+  // Add alarms[0] -> DB: [ alarms[0] (id: 1) ]
+  // Check the insertion is OK.
+  netStatsDb.addAlarm(alarms[0], function(error, result) {
+    do_check_eq(error, null);
+    alarmsDbId = result;
+    netStatsDb.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, exampleManifestURL, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.length, 1);
+      do_check_eq(result[0].id, alarmsDbId);
+      do_check_eq(result[0].networkId, alarms[0].networkId);
+      do_check_eq(result[0].threshold, alarms[0].threshold);
+      do_check_eq(result[0].data.foo, alarms[0].data.foo);
+      run_next_test();
+    });
+  });
+});
+
+add_test(function test_getFirstAlarm() {
+  // Add alarms[1] -> DB: [ alarms[0] (id: 1), alarms[1] (id: 2) ]
+  // Check first alarm is alarms[1] because threshold is lower.
+  alarmsDbId += 1;
+  netStatsDb.addAlarm(alarms[1], function (error, result) {
+    do_check_eq(error, null);
+    do_check_eq(result, alarmsDbId);
+    netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.id, alarmsDbId);
+      do_check_eq(result.networkId, alarms[1].networkId);
+      do_check_eq(result.threshold, alarms[1].threshold);
+      do_check_eq(result.data.foo, alarms[1].data.foo);
+      do_check_eq(result.pageURL, alarms[1].pageURL);
+      do_check_eq(result.manifestURL, alarms[1].manifestURL);
+      run_next_test();
+    });
+  });
+});
+
+add_test(function test_removeAlarm() {
+  // Remove alarms[1] (id: 2) -> DB: [ alarms[0] (id: 1) ]
+  // Check get first return alarms[0].
+  netStatsDb.removeAlarm(alarmsDbId, alarms[0].manifestURL, function (error, result) {
+    do_check_eq(error, null);
+    netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.id, alarmsDbId - 1);
+      do_check_eq(result.networkId, alarms[0].networkId);
+      do_check_eq(result.threshold, alarms[0].threshold);
+      do_check_eq(result.data.foo, alarms[0].data.foo);
+      do_check_eq(result.pageURL, alarms[0].pageURL);
+      do_check_eq(result.manifestURL, alarms[0].manifestURL);
+      run_next_test();
+    });
+  });
+});
+
+add_test(function test_removeAppAlarm() {
+  // Remove alarms[0] (id: 1) -> DB: [ ]
+  netStatsDb.removeAlarm(alarmsDbId - 1, alarms[0].manifestURL, function (error, result) {
+    do_check_eq(error, null);
+    netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.length, 0);
+      run_next_test();
+    });
+  });
+});
+
+add_test(function test_getAlarms() {
+  // Add all alarms -> DB: [ alarms[0] (id: 3),
+  //                         alarms[1] (id: 4),
+  //                         alarms[2] (id: 5),
+  //                         alarms[3] (id: 6) ]
+  // Check that getAlarms for wifi returns 2 alarms.
+  // Check that getAlarms for all connections returns 3 alarms.
+
+  var callback = function () {
+    netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.length, 2);
+      netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
+        do_check_eq(error, null);
+        do_check_eq(result.length, 3);
+        run_next_test();
+      });
+    });
+  };
+
+  var index = 0;
+
+  var addFunction = function () {
+    alarmsDbId += 1;
+    netStatsDb.addAlarm(alarms[index], function (error, result) {
+      do_check_eq(error, null);
+      index += 1;
+      do_check_eq(result, alarmsDbId);
+      if (index >= alarms.length) {
+        callback();
+        return;
+      }
+      addFunction();
+    });
+  };
+
+  addFunction();
+});
+
+add_test(function test_removeAppAllAlarms() {
+  // Remove all alarms for exampleManifestURL -> DB: [ alarms[3] (id: 6) ]
+  netStatsDb.removeAlarms(exampleManifestURL, function (error, result) {
+    do_check_eq(error, null);
+    netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.length, 0);
+      netStatsDb.getAlarms(null, testManifestURL, function(error, result) {
+        do_check_eq(error, null);
+        do_check_eq(result.length, 1);
+        run_next_test();
+      });
+    });
+  });
+});
+
+add_test(function test_updateAlarm() {
+  // Update alarms[3] (id: 6) -> DB: [ alarms[3]* (id: 6) ]
+
+  var updatedAlarm = alarms[1];
+  updatedAlarm.id = alarmsDbId;
+  updatedAlarm.threshold = 10;
+
+  netStatsDb.updateAlarm(updatedAlarm, function (error, result) {
+    do_check_eq(error, null);
+    netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.id, updatedAlarm.id);
+      do_check_eq(result.networkId, updatedAlarm.networkId);
+      do_check_eq(result.threshold, updatedAlarm.threshold);
+      do_check_eq(result.data.foo, updatedAlarm.data.foo);
+      do_check_eq(result.pageURL, updatedAlarm.pageURL);
+      do_check_eq(result.manifestURL, updatedAlarm.manifestURL);
+      run_next_test();
+    });
+  });
+});
+
 function run_test() {
   do_get_profile();
-
-  // Clear whole database to avoid start tests with unknown state
-  // due to previous tests.
-  clearWholeDB(function(){
-    run_next_test();
-  });
+  run_next_test();
 }
--- a/dom/network/tests/unit_stats/test_networkstats_service.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -134,17 +134,105 @@ add_test(function test_queue() {
   NetworkStatsService.updateStats(netId1, callback);
   NetworkStatsService.updateStats(netId2, callback);
 
   do_check_eq(NetworkStatsService.updateQueue.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null);
   do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null);
 
+  // Clear queue because in test environment requests for mobile networks
+  // can not be handled.
+  NetworkStatsService.updateQueue =  [];
   run_next_test();
 });
 
+var wifiId = '00';
+
+add_test(function test_updateThreshold() {
+  let alarm = { networkId: wifiId, threshold: 10000 };
+
+  NetworkStatsService._updateThreshold(alarm, function onSet(error, threshold){
+    do_check_eq(error, null);
+    do_check_neq(threshold.systemThreshold, undefined);
+    do_check_neq(threshold.absoluteThreshold, undefined);
+    run_next_test();
+  });
+});
+
+var testPageURL = "http://test.com";
+var testManifestURL = "http://test.com/manifest.webapp";
+
+add_test(function test_setAlarm() {
+  let alarm = { id: null,
+                networkId: wifiId,
+                threshold: 10000,
+                absoluteThreshold: null,
+                alarmStart: null,
+                alarmEnd: null,
+                data: null,
+                pageURL: testPageURL,
+                manifestURL: testManifestURL };
+
+  NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
+    do_check_eq(result, 1);
+    run_next_test();
+  });
+});
+
+add_test(function test_setAlarm_invalid_threshold() {
+  let alarm = { id: null,
+                networkId: wifiId,
+                threshold: -10000,
+                absoluteThreshold: null,
+                alarmStart: null,
+                alarmEnd: null,
+                data: null,
+                pageURL: testPageURL,
+                manifestURL: testManifestURL };
+
+  NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
+    do_check_eq(error, "InvalidStateError");
+    run_next_test();
+  });
+});
+
+add_test(function test_fireAlarm() {
+  // Add a fake alarm into database.
+  let alarm = { id: null,
+                networkId: wifiId,
+                threshold: 10000,
+                absoluteThreshold: null,
+                alarmStart: null,
+                alarmEnd: null,
+                data: null,
+                pageURL: testPageURL,
+                manifestURL: testManifestURL };
+
+  NetworkStatsService._db.addAlarm(alarm, function addSuccessCb(error, newId) {
+    NetworkStatsService._db.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                                      testManifestURL, function onGet(error, result) {
+      do_check_eq(error, null);
+      do_check_eq(result.length, 1);
+
+      // Result of getAlarms is based on expected child's data format, so
+      // some changes are needed to be able to use it.
+      result[0].networkId = wifiId;
+      result[0].pageURL = testPageURL;
+      result[0].manifestURL = testManifestURL;
+
+      NetworkStatsService._fireAlarm(result[0], false);
+      NetworkStatsService._db.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                                        testManifestURL, function onGet(error, result) {
+        do_check_eq(error, undefined);
+        do_check_eq(result.length, 0);
+        run_next_test();
+      });
+    });
+  });
+});
+
 function run_test() {
   do_get_profile();
 
   Cu.import("resource://gre/modules/NetworkStatsService.jsm");
   run_next_test();
 }
--- a/dom/nfc/MozNdefRecord.cpp
+++ b/dom/nfc/MozNdefRecord.cpp
@@ -3,60 +3,112 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 /* Copyright © 2013 Deutsche Telekom, Inc. */
 
 #include "MozNdefRecord.h"
 #include "mozilla/dom/MozNdefRecordBinding.h"
+#include "mozilla/HoldDropJSObjects.h"
 #include "nsContentUtils.h"
 
+
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(MozNdefRecord, mWindow)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MozNdefRecord)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MozNdefRecord)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MozNdefRecord)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MozNdefRecord)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mType)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mId)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPayload)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MozNdefRecord)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MozNdefRecord)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozNdefRecord)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+void
+MozNdefRecord::HoldData()
+{
+  mozilla::HoldJSObjects(this);
+}
+
+void
+MozNdefRecord::DropData()
+{
+  if (mType) {
+    mType = nullptr;
+  }
+  if (mId) {
+    mId = nullptr;
+  }
+  if (mPayload) {
+    mPayload = nullptr;
+  }
+  mozilla::DropJSObjects(this);
+}
+
 /* static */
 already_AddRefed<MozNdefRecord>
 MozNdefRecord::Constructor(const GlobalObject& aGlobal,
-                           uint8_t aTnf, const nsAString& aType,
-                           const nsAString& aId, const nsAString& aPayload,
+                           uint8_t aTnf, const Uint8Array& aType,
+                           const Uint8Array& aId, const Uint8Array& aPayload,
                            ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
   if (!win) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
-  nsRefPtr<MozNdefRecord> ndefrecord =
-    new MozNdefRecord(win, aTnf, aType, aId, aPayload);
+
+  nsRefPtr<MozNdefRecord> ndefrecord = new MozNdefRecord(aGlobal.GetContext(),
+                                                         win, aTnf, aType, aId,
+                                                         aPayload);
+  if (!ndefrecord) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
   return ndefrecord.forget();
 }
 
-MozNdefRecord::MozNdefRecord(nsPIDOMWindow* aWindow,
-                             uint8_t aTnf, const nsAString& aType,
-                             const nsAString& aId, const nsAString& aPayload)
+MozNdefRecord::MozNdefRecord(JSContext* aCx, nsPIDOMWindow* aWindow,
+                             uint8_t aTnf, const Uint8Array& aType,
+                             const Uint8Array& aId, const Uint8Array& aPayload)
   : mTnf(aTnf)
-  , mType(aType)
-  , mId(aId)
-  , mPayload(aPayload)
 {
-  mWindow = aWindow;
+  mWindow = aWindow; // For GetParentObject()
+
+  mType = Uint8Array::Create(aCx, this, aType.Length(), aType.Data());
+  mId = Uint8Array::Create(aCx, this, aId.Length(), aId.Data());
+  mPayload = Uint8Array::Create(aCx, this, aPayload.Length(), aPayload.Data());
+
   SetIsDOMBinding();
+  HoldData();
 }
 
 MozNdefRecord::~MozNdefRecord()
 {
+  DropData();
 }
 
 JSObject*
 MozNdefRecord::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return MozNdefRecordBinding::Wrap(aCx, aScope, this);
 }
 
--- a/dom/nfc/MozNdefRecord.h
+++ b/dom/nfc/MozNdefRecord.h
@@ -12,77 +12,96 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "jsapi.h"
 
 #include "nsIDocument.h"
 
+#include "mozilla/dom/TypedArray.h"
+#include "jsfriendapi.h"
+#include "js/GCAPI.h"
+
 struct JSContext;
 
 namespace mozilla {
 namespace dom {
 
 class MozNdefRecord MOZ_FINAL : public nsISupports,
                                 public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozNdefRecord)
 
 public:
 
-  MozNdefRecord(nsPIDOMWindow* aWindow,
-                uint8_t aTnf, const nsAString& aType,
-                const nsAString& aId, const nsAString& aPlayload);
+  MozNdefRecord(JSContext* aCx, nsPIDOMWindow* aWindow, uint8_t aTnf,
+                const Uint8Array& aType, const Uint8Array& aId,
+                const Uint8Array& aPlayload);
 
   ~MozNdefRecord();
 
   nsIDOMWindow* GetParentObject() const
   {
     return mWindow;
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
-  static already_AddRefed<MozNdefRecord> Constructor(
-                                           const GlobalObject& aGlobal,
-                                           uint8_t aTnf, const nsAString& aType,
-                                           const nsAString& aId,
-                                           const nsAString& aPayload,
-                                           ErrorResult& aRv);
+  static already_AddRefed<MozNdefRecord>
+                  Constructor(const GlobalObject& aGlobal, uint8_t aTnf,
+                              const Uint8Array& aType, const Uint8Array& aId,
+                              const Uint8Array& aPayload, ErrorResult& aRv);
 
   uint8_t Tnf() const
   {
     return mTnf;
   }
 
-  void GetType(nsString& aType) const
+  JSObject* Type(JSContext* cx) const
   {
-    aType = mType;
+    return GetTypeObject();
+  }
+  JSObject* GetTypeObject() const
+  {
+    JS::ExposeObjectToActiveJS(mType);
+    return mType;
   }
 
-  void GetId(nsString& aId) const
+  JSObject* Id(JSContext* cx) const
   {
-    aId = mId;
+    return GetIdObject();
+  }
+  JSObject* GetIdObject() const
+  {
+    JS::ExposeObjectToActiveJS(mId);
+    return mId;
   }
 
-  void GetPayload(nsString& aPayload) const
+  JSObject* Payload(JSContext* cx) const
   {
-    aPayload = mPayload;
+    return GetPayloadObject();
+  }
+  JSObject* GetPayloadObject() const
+  {
+    JS::ExposeObjectToActiveJS(mPayload);
+    return mPayload;
   }
 
 private:
   MozNdefRecord() MOZ_DELETE;
   nsRefPtr<nsPIDOMWindow> mWindow;
+  void HoldData();
+  void DropData();
 
   uint8_t mTnf;
-  nsString mType;
-  nsString mId;
-  nsString mPayload;
+  JS::Heap<JSObject*> mType;
+  JS::Heap<JSObject*> mId;
+  JS::Heap<JSObject*> mPayload;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MozNdefRecord_h__
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
-const NETWORKSERVICE_CID = Components.ID("{a6c58260-46df-11e3-8f96-0800200c9a66}");
+const NETWORKSERVICE_CID = Components.ID("{c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c}");
 
 // 1xx - Requested action is proceeding
 const NETD_COMMAND_PROCEEDING   = 100;
 // 2xx - Requested action has been successfully completed
 const NETD_COMMAND_OKAY         = 200;
 // 4xx - The command is accepted but the requested action didn't
 // take place.
 const NETD_COMMAND_FAIL         = 400;
@@ -112,23 +112,99 @@ NetworkService.prototype = {
       cmd: "getNetworkInterfaceStats",
       ifname: networkName
     };
 
     params.report = true;
     params.isAsync = true;
 
     this.controlMessage(params, function(result) {
-      let success = result.resultCode >= NETD_COMMAND_OKAY &&
-                    result.resultCode < NETD_COMMAND_ERROR;
+      let success = !isError(result.resultCode);
       callback.networkStatsAvailable(success, result.rxBytes,
                                      result.txBytes, result.date);
     });
   },
 
+  setNetworkInterfaceAlarm: function setNetworkInterfaceAlarm(networkName, threshold, callback) {
+    if (!networkName) {
+      callback.networkUsageAlarmResult(-1);
+      return;
+    }
+
+    if (threshold < 0) {
+      this._disableNetworkInterfaceAlarm(networkName, callback);
+      return;
+    }
+
+    this._setNetworkInterfaceAlarm(networkName, threshold, callback);
+  },
+
+  _setNetworkInterfaceAlarm: function _setNetworkInterfaceAlarm(networkName, threshold, callback) {
+    debug("setNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
+
+    let params = {
+      cmd: "setNetworkInterfaceAlarm",
+      ifname: networkName,
+      threshold: threshold
+    };
+
+    params.report = true;
+    params.isAsync = true;
+
+    this.controlMessage(params, function(result) {
+      if (!isError(result.resultCode)) {
+        callback.networkUsageAlarmResult(null);
+        return;
+      }
+
+      this._enableNetworkInterfaceAlarm(networkName, threshold, callback);
+    });
+  },
+
+  _enableNetworkInterfaceAlarm: function _enableNetworkInterfaceAlarm(networkName, threshold, callback) {
+    debug("enableNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
+
+    let params = {
+      cmd: "enableNetworkInterfaceAlarm",
+      ifname: networkName,
+      threshold: threshold
+    };
+
+    params.report = true;
+    params.isAsync = true;
+
+    this.controlMessage(params, function(result) {
+      if (!isError(result.resultCode)) {
+        callback.networkUsageAlarmResult(null);
+        return;
+      }
+      callback.networkUsageAlarmResult(result.reason);
+    });
+  },
+
+  _disableNetworkInterfaceAlarm: function _disableNetworkInterfaceAlarm(networkName, callback) {
+    debug("disableNetworkInterfaceAlarm for " + networkName);
+
+    let params = {
+      cmd: "disableNetworkInterfaceAlarm",
+      ifname: networkName,
+    };
+
+    params.report = true;
+    params.isAsync = true;
+
+    this.controlMessage(params, function(result) {
+      if (!isError(result.resultCode)) {
+        callback.networkUsageAlarmResult(null);
+        return;
+      }
+      callback.networkUsageAlarmResult(result.reason);
+    });
+  },
+
   setWifiOperationMode: function setWifiOperationMode(interfaceName, mode, callback) {
     if(DEBUG) debug("setWifiOperationMode on " + interfaceName + " to " + mode);
 
     let params = {
       cmd: "setWifiOperationMode",
       ifname: interfaceName,
       mode: mode
     };
--- a/dom/system/gonk/NetworkService.manifest
+++ b/dom/system/gonk/NetworkService.manifest
@@ -1,3 +1,3 @@
 # NetworkService.js
-component {a6c58260-46df-11e3-8f96-0800200c9a66} NetworkService.js
-contract @mozilla.org/network/service;1 {a6c58260-46df-11e3-8f96-0800200c9a66}
+component {c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c} NetworkService.js
+contract @mozilla.org/network/service;1 {c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c}
--- a/dom/system/gonk/net_worker.js
+++ b/dom/system/gonk/net_worker.js
@@ -146,17 +146,29 @@ function networkInterfaceStatsFail(param
   // Notify the main thread.
   postMessage(params);
   return true;
 }
 
 function networkInterfaceStatsSuccess(params) {
   // Notify the main thread.
   params.txBytes = parseFloat(params.resultReason);
+  postMessage(params);
+  return true;
+}
 
+function networkInterfaceAlarmFail(params) {
+  // Notify the main thread.
+  postMessage(params);
+  return true;
+}
+
+function networkInterfaceAlarmSuccess(params) {
+  // Notify the main thread.
+  params.error = parseFloat(params.resultReason);
   postMessage(params);
   return true;
 }
 
 function updateUpStreamSuccess(params) {
   // Notify the main thread.
   postMessage(params);
   return true;
@@ -602,16 +614,41 @@ function getRxBytes(params, callback) {
 
 function getTxBytes(params, callback) {
   params.rxBytes = parseFloat(params.resultReason);
 
   let command = "interface readtxcounter " + params.ifname;
   return doCommand(command, callback);
 }
 
+function enableAlarm(params, callback) {
+  let command = "bandwidth enable";
+  return doCommand(command, callback);
+}
+
+function disableAlarm(params, callback) {
+  let command = "bandwidth disable";
+  return doCommand(command, callback);
+}
+
+function setQuota(params, callback) {
+  let command = "bandwidth setiquota " + params.ifname + " " + parseInt('0xffffffffffffffff');
+  return doCommand(command, callback);
+}
+
+function removeQuota(params, callback) {
+  let command = "bandwidth removeiquota " + params.ifname;
+  return doCommand(command, callback);
+}
+
+function setAlarm(params, callback) {
+  let command = "bandwidth setinterfacealert " + params.ifname + " " + params.threshold;
+  return doCommand(command, callback);
+}
+
 function escapeQuote(str) {
   str = str.replace(/\\/g, "\\\\");
   return str.replace(/"/g, "\\\"");
 }
 
 /**
  * Command format for sdk version < 16
  *   Arguments:
@@ -909,16 +946,49 @@ function getNetworkInterfaceStats(params
   params.rxBytes = -1;
   params.txBytes = -1;
   params.date = new Date();
 
   chain(params, gNetworkInterfaceStatsChain, networkInterfaceStatsFail);
   return true;
 }
 
+let gNetworkInterfaceEnableAlarmChain = [enableAlarm,
+                                         setQuota,
+                                         setAlarm,
+                                         networkInterfaceAlarmSuccess];
+
+function enableNetworkInterfaceAlarm(params) {
+  debug("enableNetworkInterfaceAlarms: " + params.ifname);
+
+  chain(params, gNetworkInterfaceEnableAlarmChain, networkInterfaceAlarmFail);
+  return true;
+}
+
+let gNetworkInterfaceDisableAlarmChain = [removeQuota,
+                                          disableAlarm,
+                                          networkInterfaceAlarmSuccess];
+
+function disableNetworkInterfaceAlarm(params) {
+  debug("disableNetworkInterfaceAlarms: " + params.ifname);
+
+  chain(params, gNetworkInterfaceDisableAlarmChain, networkInterfaceAlarmFail);
+  return true;
+}
+
+let gNetworkInterfaceSetAlarmChain = [setAlarm,
+                                      networkInterfaceAlarmSuccess];
+
+function setNetworkInterfaceAlarm(params) {
+  debug("setNetworkInterfaceAlarms: " + params.ifname);
+
+  chain(params, gNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail);
+  return true;
+}
+
 let gWifiOperationModeChain = [wifiFirmwareReload,
                                wifiOperationModeSuccess];
 
 /**
  * handling main thread's reload Wifi firmware request
  */
 function setWifiOperationMode(params) {
   debug("setWifiOperationMode: " + params.ifname + " " + params.mode);
--- a/dom/system/gonk/nfc_consts.js
+++ b/dom/system/gonk/nfc_consts.js
@@ -43,18 +43,17 @@ this.NFC_RESPONSE_READ_NDEF = 1003;
 this.NFC_NOTIFICATION_INITIALIZED = 2000;
 this.NFC_NOTIFICATION_TECH_DISCOVERED = 2001;
 this.NFC_NOTIFICATION_TECH_LOST = 2002;
 
 this.NFC_TECHS = {
   0:'NDEF',
   1:'NDEF_WRITEABLE',
   2:'NDEF_FORMATABLE',
-  3:'P2P',
-  4:'NFC_A'
+  3:'P2P'
 };
 
 // TODO: Bug 933595. Fill-in all error codes for Gonk/nfcd protocol
 this.GECKO_NFC_ERROR_SUCCESS             = 0;
 this.GECKO_NFC_ERROR_GENERIC_FAILURE     = 1;
 
 // NFC powerlevels must match config PDUs.
 this.NFC_POWER_LEVEL_UNKNOWN        = -1;
--- a/dom/system/gonk/nfc_worker.js
+++ b/dom/system/gonk/nfc_worker.js
@@ -107,43 +107,34 @@ let NfcWorker = {
     let numOfRecords = Buf.readInt32();
     debug("numOfRecords = " + numOfRecords);
     if (numOfRecords <= 0) {
       return null;
     }
     let records = [];
 
     for (let i = 0; i < numOfRecords; i++) {
-      let tnf        = Buf.readInt32();
+      let tnf        = Buf.readInt32() & 0xff;
       let typeLength = Buf.readInt32();
-      let type = [];
-      for (let i = 0; i < typeLength; i++) {
-        type.push(Buf.readUint8());
-      }
+      let type       = Buf.readUint8Array(typeLength);
       let padding    = getPaddingLen(typeLength);
       for (let i = 0; i < padding; i++) {
         Buf.readUint8();
       }
 
       let idLength = Buf.readInt32();
-      let id = [];
-      for (let i = 0; i < idLength; i++) {
-        id.push(Buf.readUint8());
-      }
+      let id       = Buf.readUint8Array(idLength);
       padding      = getPaddingLen(idLength);
       for (let i = 0; i < padding; i++) {
         Buf.readUint8();
       }
 
       let payloadLength = Buf.readInt32();
-      let payload = [];
-      for (let i = 0; i < payloadLength; i++) {
-        payload.push(Buf.readUint8());
-      }
-      padding = getPaddingLen(payloadLength);
+      let payload       = Buf.readUint8Array(payloadLength);
+      padding           = getPaddingLen(payloadLength);
       for (let i = 0; i < padding; i++) {
         Buf.readUint8();
       }
       records.push({tnf: tnf,
                     type: type,
                     id: id,
                     payload: payload});
     }
@@ -191,40 +182,40 @@ let NfcWorker = {
     Buf.writeInt32(message.sessionId);
     let records    = message.records;
     let numRecords = records.length;
     Buf.writeInt32(numRecords);
     for (let i = 0; i < numRecords; i++) {
       let record = records[i];
       Buf.writeInt32(record.tnf);
 
-      let typeLength = record.type.length;
+      let typeLength = record.type ? record.type.length : 0;
       Buf.writeInt32(typeLength);
       for (let j = 0; j < typeLength; j++) {
-        Buf.writeUint8(record.type.charCodeAt(j));
+        Buf.writeUint8(record.type[j]);
       }
       let padding = getPaddingLen(typeLength);
       for (let i = 0; i < padding; i++) {
         Buf.writeUint8(0x00);
       }
 
-      let idLength = record.id.length;
+      let idLength = record.id ? record.id.length : 0;
       Buf.writeInt32(idLength);
       for (let j = 0; j < idLength; j++) {
-        Buf.writeUint8(record.id.charCodeAt(j));
+        Buf.writeUint8(record.id[j]);
       }
       padding = getPaddingLen(idLength);
       for (let i = 0; i < padding; i++) {
         Buf.writeUint8(0x00);
       }
 
-      let payloadLength = record.payload && record.payload.length;
+      let payloadLength = record.payload ? record.payload.length : 0;
       Buf.writeInt32(payloadLength);
       for (let j = 0; j < payloadLength; j++) {
-        Buf.writeUint8(record.payload.charCodeAt(j));
+        Buf.writeUint8(record.payload[j]);
       }
       padding = getPaddingLen(payloadLength);
       for (let i = 0; i < padding; i++) {
         Buf.writeUint8(0x00);
       }
     }
 
     Buf.sendParcel();
@@ -377,17 +368,20 @@ NfcWorker[NFC_NOTIFICATION_INITIALIZED] 
 NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() {
   debug("NFC_NOTIFICATION_TECH_DISCOVERED");
   let techs     = [];
   let ndefMsgs  = [];
 
   let sessionId = Buf.readInt32();
   let techCount = Buf.readInt32();
   for (let count = 0; count < techCount; count++) {
-    techs.push(NFC_TECHS[Buf.readUint8()]);
+    let tech = NFC_TECHS[Buf.readUint8()];
+    if (tech) {
+      techs.push(tech);
+    }
   }
 
   let padding   = getPaddingLen(techCount);
   for (let i = 0; i < padding; i++) {
     Buf.readUint8();
   }
 
   let ndefMsgCount = Buf.readInt32();
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -23,16 +23,22 @@ interface nsIWifiTetheringCallback : nsI
 interface nsINetworkStatsCallback : nsISupports
 {
   void networkStatsAvailable(in boolean success,
                              in unsigned long rxBytes,
                              in unsigned long txBytes,
                              in jsval date);
 };
 
+[scriptable, function, uuid(0706bfa2-ac2d-11e2-9a8d-7b6d988d4767)]
+interface nsINetworkUsageAlarmCallback : nsISupports
+{
+  void networkUsageAlarmResult(in jsval error);
+};
+
 [scriptable, function, uuid(9ede8720-f8bc-11e2-b778-0800200c9a66)]
 interface nsIWifiOperationModeCallback : nsISupports
 {
   /**
    * Callback function used to report result to WifiManager.
    *
    * @param error
    *        An error message if the operation wasn't successful,
@@ -93,17 +99,17 @@ interface nsIUpdateUpStreamCallback : ns
    *        The external interface name.
    */
   void updateUpStreamResult(in boolean success, in DOMString externalIfname);
 };
 
 /**
  * Provide network services.
  */
-[scriptable, uuid(a6c58260-46df-11e3-8f96-0800200c9a66)]
+[scriptable, uuid(c14cabaf-bb8e-470d-a2f1-2cb6de6c5e5c)]
 interface nsINetworkService : nsISupports
 {
   /**
    * Enable or disable Wifi Tethering
    *
    * @param enabled
    *        Boolean that indicates whether tethering should be enabled (true) or disabled (false).
    * @param config
@@ -147,16 +153,34 @@ interface nsINetworkService : nsISupport
    *
    * @param callback
    *        Callback to notify result and provide stats, connectionType
    *        and the date when stats are retrieved
    */
   void getNetworkInterfaceStats(in DOMString networkName, in nsINetworkStatsCallback callback);
 
   /**
+   * Set Alarm of usage per interface
+   *
+   * @param networkName
+   *        Select the Network interface to set an alarm.
+   *
+   * @param threshold
+   *        Amount of data that will trigger the alarm.
+   *
+   * @param callback
+   *        Callback to notify the result.
+   *
+   * @return false if there is no interface registered for the networkType param.
+   */
+  boolean setNetworkInterfaceAlarm(in DOMString networkName,
+                                   in long threshold,
+                                   in nsINetworkUsageAlarmCallback callback);
+
+  /**
    * Reload Wifi firmware to specific operation mode.
    *
    * @param interfaceName
    *        Wifi Network interface name.
    *
    * @param mode
    *        AP  - Access pointer mode.
    *        P2P - Peer to peer connection mode.
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -3009,17 +3009,16 @@ let RIL = {
       // Other types of ICC we can send Terminal_Profile immediately.
       if (this.appType == CARD_APPTYPE_SIM) {
         SimRecordHelper.readSimPhase();
       } else if (RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD) {
         this.sendStkTerminalProfile(STK_SUPPORTED_TERMINAL_PROFILE);
       }
 
       ICCRecordHelper.fetchICCRecords();
-      this.reportStkServiceIsRunning();
     }
 
     this.cardState = newCardState;
     this.sendChromeMessage({rilMessageType: "cardstatechange",
                             cardState: this.cardState});
   },
 
    /**
@@ -11220,16 +11219,17 @@ let ICCRecordHelper = {
       let strLen = Buf.readInt32();
       let octetLen = strLen / 2;
       RIL.iccInfo.iccid = GsmPDUHelper.readSwappedNibbleBcdString(octetLen);
       Buf.readStringDelimiter(strLen);
 
       if (DEBUG) debug("ICCID: " + RIL.iccInfo.iccid);
       if (RIL.iccInfo.iccid) {
         ICCUtilsHelper.handleICCInfoChange();
+        RIL.reportStkServiceIsRunning();
       }
     }
 
     ICCIOHelper.loadTransparentEF({fileId: ICC_EF_ICCID,
                                    callback: callback.bind(this)});
   },
 
   /**
--- a/dom/webidl/CameraControl.webidl
+++ b/dom/webidl/CameraControl.webidl
@@ -147,16 +147,22 @@ interface CameraControl {
        one; an object with 'height' and 'width' properties that corresponds
        to one of the options returned by capabilities.pictureSizes.
        
        this setting should be considered a hint: the implementation will
        respect it when possible, and override it if necessary. */
     [Throws]
     attribute any thumbnailSize;
 
+    /* the angle, in degrees, that the image sensor is mounted relative
+       to the display; e.g. if 'sensorAngle' is 270 degrees (or -90 degrees),
+       then the preview stream needs to be rotated +90 degrees to have the
+       same orientation as the real world. */
+    readonly attribute long sensorAngle;
+
     /* tell the camera to attempt to focus the image */
     [Throws]
     void autoFocus(CameraAutoFocusCallback onSuccess, optional CameraErrorCallback onError);
 
     /* capture an image and return it as a blob to the 'onSuccess' callback;
        if the camera supports it, this may be invoked while the camera is
        already recording video.
 
--- a/dom/webidl/MozNdefRecord.webidl
+++ b/dom/webidl/MozNdefRecord.webidl
@@ -1,38 +1,43 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 /* Copyright © 2013 Deutsche Telekom, Inc. */
 
-[Constructor(octet tnf, DOMString type, DOMString id, DOMString payload)]
+[Constructor(octet tnf, Uint8Array type, Uint8Array id, Uint8Array payload)]
 interface MozNdefRecord
 {
   /**
    * Type Name Field (3-bits) - Specifies the NDEF record type in general.
    *   tnf_empty: 0x00
    *   tnf_well_known: 0x01
    *   tnf_mime_media: 0x02
    *   tnf_absolute_uri: 0x03
    *   tnf_external type: 0x04
    *   tnf_unknown: 0x05
    *   tnf_unchanged: 0x06
    *   tnf_reserved: 0x07
    */
+  [Constant]
   readonly attribute octet tnf;
 
   /**
    * type - Describes the content of the payload. This can be a mime type.
    */
-  readonly attribute DOMString type;
+  [Constant]
+  readonly attribute Uint8Array type;
 
   /**
    * id - Identifer is application dependent.
    */
-  readonly attribute DOMString id;
+  [Constant]
+  readonly attribute Uint8Array id;
 
   /**
-   * payload - Binary data blob. The meaning of this field is application dependent.
+   * payload - Binary data blob. The meaning of this field is application
+   * dependent.
    */
-  readonly attribute DOMString payload;
+  [Constant]
+  readonly attribute Uint8Array payload;
 };
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -26,17 +26,17 @@ namespace layers {
  * or we get a touch point very far away from the previous position for some
  * reason.
  */
 static float gMaxEventAcceleration = 999.0f;
 
 /**
  * Amount of friction applied during flings.
  */
-static float gFlingFriction = 0.006f;
+static float gFlingFriction = 0.002f;
 
 /**
  * Threshold for velocity beneath which we turn off any acceleration we had
  * during repeated flings.
  */
 static float gVelocityThreshold = 0.14f;
 
 /**
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -79,17 +79,17 @@ ThreadStackHelper::~ThreadStackHelper()
 #endif
 }
 
 void
 ThreadStackHelper::GetStack(Stack& aStack)
 {
   // Always run PrepareStackBuffer first to clear aStack
   if (!PrepareStackBuffer(aStack)) {
-    MOZ_ASSERT(false);
+    // Skip and return empty aStack
     return;
   }
 
 #if defined(XP_LINUX)
   if (!sInitialized) {
     MOZ_ASSERT(false);
     return;
   }
@@ -144,23 +144,33 @@ ThreadStackHelper::SigAction(int aSignal
   sCurrent = nullptr;
   ::sem_post(&sSem);
 }
 
 #endif // XP_LINUX
 
 bool
 ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
+  // Return false to skip getting the stack and return an empty stack
   aStack.clear();
 #ifdef MOZ_ENABLE_PROFILER_SPS
+  /* Normally, provided the profiler is enabled, it would be an error if we
+     don't have a pseudostack here (the thread probably forgot to call
+     profiler_register_thread). However, on B2G, profiling secondary threads
+     may be disabled despite profiler being enabled. This is by-design and
+     is not an error. */
+#ifdef MOZ_WIDGET_GONK
   if (!mPseudoStack) {
     return false;
   }
+#endif
+  MOZ_ASSERT(mPseudoStack);
   mStackBuffer.clear();
-  return mStackBuffer.reserve(mMaxStackSize);
+  MOZ_ALWAYS_TRUE(mStackBuffer.reserve(mMaxStackSize));
+  return true;
 #else
   return false;
 #endif
 }
 
 void
 ThreadStackHelper::FillStackBuffer() {
 #ifdef MOZ_ENABLE_PROFILER_SPS