Bug 766324 - Add a volume IDL to make volumes scriptable r=qdot
authorDave Hylands <dhylands@gmail.com>
Mon, 16 Jul 2012 12:38:18 -0400
changeset 104924 413699472929757ab4ccf309b73bf29346205bea
parent 104923 c0a8cbdb0a7088d1b139cc36e4a307ca1a5b7d96
child 104925 f4aae5703855cfecf483829553f7a9fda34f8ffd
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-beta@f335e7dacdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs766324
milestone16.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
Bug 766324 - Add a volume IDL to make volumes scriptable r=qdot
dom/system/gonk/AutoMounter.cpp
dom/system/gonk/Makefile.in
dom/system/gonk/Volume.cpp
dom/system/gonk/Volume.h
dom/system/gonk/VolumeManager.cpp
dom/system/gonk/VolumeManager.h
dom/system/gonk/VolumeManagerLog.h
dom/system/gonk/VolumeServiceIOThread.cpp
dom/system/gonk/VolumeServiceIOThread.h
dom/system/gonk/VolumeServiceTest.cpp
dom/system/gonk/VolumeServiceTest.h
dom/system/gonk/nsIVolume.idl
dom/system/gonk/nsIVolumeService.idl
dom/system/gonk/nsIVolumeStat.idl
dom/system/gonk/nsVolume.cpp
dom/system/gonk/nsVolume.h
dom/system/gonk/nsVolumeService.cpp
dom/system/gonk/nsVolumeService.h
dom/system/gonk/nsVolumeStat.cpp
dom/system/gonk/nsVolumeStat.h
layout/build/nsLayoutModule.cpp
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -203,36 +203,38 @@ public:
 
   typedef nsTArray<RefPtr<Volume> > VolumeArray;
 
   AutoMounter()
     : mResponseCallback(new AutoMounterResponseCallback),
       mMode(AUTOMOUNTER_DISABLE)
   {
     VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver);
+    Volume::RegisterObserver(&mVolumeEventObserver);
 
     for (size_t i = 0; i < NS_ARRAY_LENGTH(sAutoVolumeName); i++) {
       RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(sAutoVolumeName[i]);
-      if (vol != NULL) {
+      if (vol) {
         vol->RegisterObserver(&mVolumeEventObserver);
         mAutoVolume.AppendElement(vol);
       }
     }
 
     DBG("Calling UpdateState from constructor");
     UpdateState();
   }
 
   ~AutoMounter()
   {
     VolumeArray::index_type volIndex;
     VolumeArray::size_type  numVolumes = mAutoVolume.Length();
     for (volIndex = 0; volIndex < numVolumes; volIndex++) {
       mAutoVolume[volIndex]->UnregisterObserver(&mVolumeEventObserver);
     }
+    Volume::UnregisterObserver(&mVolumeEventObserver);
     VolumeManager::UnregisterStateObserver(&mVolumeManagerStateObserver);
   }
 
   void UpdateState();
 
   void SetMode(int32_t aMode)
   {
     if ((aMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) &&
@@ -372,44 +374,44 @@ AutoMounter::UpdateState()
     if (!vol->MediaPresent()) {
       // No media - nothing we can do
       continue;
     }
 
     if (tryToShare) {
       // We're going to try to unmount and share the volumes
       switch (volState) {
-        case Volume::STATE_MOUNTED: {
+        case nsIVolume::STATE_MOUNTED: {
           // Volume is mounted, we need to unmount before
           // we can share.
           DBG("UpdateState: Unmounting %s", vol->NameStr());
           vol->StartUnmount(mResponseCallback);
           return;
         }
-        case Volume::STATE_IDLE: {
+        case nsIVolume::STATE_IDLE: {
           // Volume is unmounted. We can go ahead and share.
           DBG("UpdateState: Sharing %s", vol->NameStr());
           vol->StartShare(mResponseCallback);
           return;
         }
         default: {
           // Not in a state that we can do anything about.
           break;
         }
       }
     } else {
       // We're going to try and unshare and remount the volumes
       switch (volState) {
-        case Volume::STATE_SHARED: {
+        case nsIVolume::STATE_SHARED: {
           // Volume is shared. We can go ahead and unshare.
           DBG("UpdateState: Unsharing %s", vol->NameStr());
           vol->StartUnshare(mResponseCallback);
           return;
         }
-        case Volume::STATE_IDLE: {
+        case nsIVolume::STATE_IDLE: {
           // Volume is unmounted, try to mount.
 
           DBG("UpdateState: Mounting %s", vol->NameStr());
           vol->StartMount(mResponseCallback);
           return;
         }
         default: {
           // Not in a state that we can do anything about.
--- a/dom/system/gonk/Makefile.in
+++ b/dom/system/gonk/Makefile.in
@@ -22,16 +22,19 @@ include $(topsrcdir)/dom/dom-config.mk
 CPPSRCS = \
   SystemWorkerManager.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIAudioManager.idl \
   nsINetworkManager.idl \
   nsIRadioInterfaceLayer.idl \
+  nsIVolume.idl \
+  nsIVolumeService.idl \
+  nsIVolumeStat.idl \
   nsIWorkerHolder.idl \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/src/geolocation \
   -I$(topsrcdir)/dom/telephony \
   -I$(topsrcdir)/dom/wifi \
@@ -40,19 +43,24 @@ LOCAL_INCLUDES = \
   $(NULL)
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   AudioManager.cpp \
   AutoMounter.cpp \
   AutoMounterSetting.cpp \
   GonkGPSGeolocationProvider.cpp \
+  nsVolume.cpp \
+  nsVolumeService.cpp \
+  nsVolumeStat.cpp \
   Volume.cpp \
   VolumeCommand.cpp \
   VolumeManager.cpp \
+  VolumeServiceIOThread.cpp \
+  VolumeServiceTest.cpp \
   $(NULL)
 # for our local copy of AudioSystem.h
 LOCAL_INCLUDES += -I$(topsrcdir)/media/libsydneyaudio/src
 endif
 
 EXTRA_COMPONENTS = \
   NetworkManager.manifest \
   NetworkManager.js \
--- a/dom/system/gonk/Volume.cpp
+++ b/dom/system/gonk/Volume.cpp
@@ -1,28 +1,31 @@
 /* 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/. */
 
 #include "Volume.h"
 #include "VolumeCommand.h"
 #include "VolumeManager.h"
 #include "VolumeManagerLog.h"
+#include "nsIVolume.h"
 #include "nsXULAppAPI.h"
 
 #include <vold/ResponseCode.h>
 
 namespace mozilla {
 namespace system {
 
+Volume::EventObserverList Volume::mEventObserverList;
+
 // We don't get media inserted/removed events at startup. So we
 // assume it's present, and we'll be told that it's missing.
 Volume::Volume(const nsCSubstring &aName)
   : mMediaPresent(true),
-    mState(STATE_INIT),
+    mState(nsIVolume::STATE_INIT),
     mName(aName)
 {
   DBG("Volume %s: created", NameStr());
 }
 
 void
 Volume::SetMediaPresent(bool aMediaPresent)
 {
@@ -62,17 +65,17 @@ Volume::SetState(Volume::STATE aNewState
 {
   if (aNewState == mState) {
     return;
   }
   LOG("Volume %s: changing state from %s to %s (%d observers)",
       NameStr(), StateStr(mState),
       StateStr(aNewState), mEventObserverList.Length());
 
-  if (aNewState == STATE_NOMEDIA) {
+  if (aNewState == nsIVolume::STATE_NOMEDIA) {
     // Cover the startup case where we don't get insertion/removal events
     mMediaPresent = false;
   }
   mState = aNewState;
   mEventObserverList.Broadcast(this);
 }
 
 void
@@ -110,26 +113,32 @@ Volume::StartUnshare(VolumeResponseCallb
 }
 
 void
 Volume::StartCommand(VolumeCommand *aCommand)
 {
   VolumeManager::PostCommand(aCommand);
 }
 
+//static
 void
 Volume::RegisterObserver(Volume::EventObserver *aObserver)
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
 
   mEventObserverList.AddObserver(aObserver);
-  // Send an initial event to the observer
-  aObserver->Notify(this);
+  // Send an initial event to the observer (for each volume)
+  size_t numVolumes = VolumeManager::NumVolumes();
+  for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
+    RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
+    aObserver->Notify(vol);
+  }
 }
 
+//static
 void
 Volume::UnregisterObserver(Volume::EventObserver *aObserver)
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
 
   mEventObserverList.RemoveObserver(aObserver);
 }
 
--- a/dom/system/gonk/Volume.h
+++ b/dom/system/gonk/Volume.h
@@ -1,87 +1,60 @@
 /* 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/. */
 
 #ifndef mozilla_system_volume_h__
 #define mozilla_system_volume_h__
 
 #include "VolumeCommand.h"
+#include "nsIVolume.h"
 #include "nsString.h"
-#include "nsWhitespaceTokenizer.h"
 #include "mozilla/Observer.h"
 #include "mozilla/RefPtr.h"
+#include "nsWhitespaceTokenizer.h"
 
 namespace mozilla {
 namespace system {
 
 /***************************************************************************
 *
 *   There is an instance of the Volume class for each volume reported
 *   from vold.
 *
 *   Each volume originates from the /system/etv/vold.fstab file.
 *
 ***************************************************************************/
 
 class Volume : public RefCounted<Volume>
 {
 public:
-  // These MUST match the states from android's system/vold/Volume.h header
-  enum STATE
-  {
-    STATE_INIT        = -1,
-    STATE_NOMEDIA     = 0,
-    STATE_IDLE        = 1,
-    STATE_PENDING     = 2,
-    STATE_CHECKING    = 3,
-    STATE_MOUNTED     = 4,
-    STATE_UNMOUNTING  = 5,
-    STATE_FORMATTING  = 6,
-    STATE_SHARED      = 7,
-    STATE_SHAREDMNT   = 8
-  };
-
   Volume(const nsCSubstring &aVolumeName);
 
-  const char *StateStr(STATE aState) const
-  {
-    switch (aState) {
-      case STATE_INIT:        return "Init";
-      case STATE_NOMEDIA:     return "NoMedia";
-      case STATE_IDLE:        return "Idle";
-      case STATE_PENDING:     return "Pending";
-      case STATE_CHECKING:    return "Checking";
-      case STATE_MOUNTED:     return "Mounted";
-      case STATE_UNMOUNTING:  return "Unmounting";
-      case STATE_FORMATTING:  return "Formatting";
-      case STATE_SHARED:      return "Shared";
-      case STATE_SHAREDMNT:   return "Shared-Mounted";
-    }
-    return "???";
-  }
+  typedef long STATE; // States are now defined in nsIVolume.idl
+
+  static const char *StateStr(STATE aState) { return NS_VolumeStateStr(aState); }
   const char *StateStr() const  { return StateStr(mState); }
   STATE State() const           { return mState; }
 
   const nsCString &Name() const { return mName; }
   const char *NameStr() const   { return mName.get(); }
 
   // The mount point is the name of the directory where the volume is mounted.
   // (i.e. path that leads to the files stored on the volume).
   const nsCString &MountPoint() const { return mMountPoint; }
 
   bool MediaPresent() const     { return mMediaPresent; }
 
   typedef mozilla::Observer<Volume *>     EventObserver;
   typedef mozilla::ObserverList<Volume *> EventObserverList;
 
   // NOTE: that observers must live in the IOThread.
-  void RegisterObserver(EventObserver *aObserver);
-  void UnregisterObserver(EventObserver *aObserver);
+  static void RegisterObserver(EventObserver *aObserver);
+  static void UnregisterObserver(EventObserver *aObserver);
 
 private:
   friend class AutoMounter;         // Calls StartXxx
   friend class VolumeManager;       // Calls HandleVoldResponse
   friend class VolumeListCallback;  // Calls SetMountPoint, SetState
 
   // The StartXxx functions will queue up a command to the VolumeManager.
   // You can queue up as many commands as you like, and aCallback will
@@ -97,15 +70,16 @@ private:
   void StartCommand(VolumeCommand *aCommand);
 
   void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer &aTokenizer);
 
   bool              mMediaPresent;
   STATE             mState;
   const nsCString   mName;
   nsCString         mMountPoint;
-  EventObserverList mEventObserverList;
+
+  static EventObserverList mEventObserverList;
 };
 
 } // system
 } // mozilla
 
 #endif  // mozilla_system_volumemanager_h__
--- a/dom/system/gonk/VolumeManager.cpp
+++ b/dom/system/gonk/VolumeManager.cpp
@@ -1,28 +1,30 @@
 /* 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/. */
 
+#include "VolumeManager.h"
+
+#include "Volume.h"
+#include "VolumeCommand.h"
+#include "VolumeManagerLog.h"
+#include "VolumeServiceTest.h"
+
+#include "nsWhitespaceTokenizer.h"
+#include "nsXULAppAPI.h"
+
+#include "base/message_loop.h"
+#include "mozilla/Scoped.h"
+
 #include <android/log.h>
 #include <cutils/sockets.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 
-#include "base/message_loop.h"
-#include "nsWhitespaceTokenizer.h"
-#include "nsXULAppAPI.h"
-
-#include "Volume.h"
-#include "VolumeCommand.h"
-#include "VolumeManager.h"
-#include "VolumeManagerLog.h"
-
-//using namespace mozilla::dom::gonk;
-
 namespace mozilla {
 namespace system {
 
 static RefPtr<VolumeManager> sVolumeManager;
 
 /***************************************************************************/
 
 VolumeManager::VolumeManager()
@@ -34,16 +36,34 @@ VolumeManager::VolumeManager()
   DBG("VolumeManager constructor called");
 }
 
 VolumeManager::~VolumeManager()
 {
 }
 
 //static
+size_t
+VolumeManager::NumVolumes()
+{
+  if (!sVolumeManager) {
+    return 0;
+  }
+  return sVolumeManager->mVolumeArray.Length();
+}
+
+//static
+TemporaryRef<Volume>
+VolumeManager::GetVolume(size_t aIndex)
+{
+  MOZ_ASSERT(aIndex < NumVolumes());
+  return sVolumeManager->mVolumeArray[aIndex];
+}
+
+//static
 VolumeManager::STATE
 VolumeManager::State()
 {
   if (!sVolumeManager) {
     return VolumeManager::UNINITIALIZED;
   }
   return sVolumeManager->mState;
 }
@@ -89,37 +109,38 @@ void VolumeManager::UnregisterStateObser
 
 //static
 TemporaryRef<Volume>
 VolumeManager::FindVolumeByName(const nsCSubstring &aName)
 {
   if (!sVolumeManager) {
     return NULL;
   }
-  for (VolumeArray::iterator volIter = sVolumeManager->mVolumeArray.begin();
-       volIter != sVolumeManager->mVolumeArray.end();
-       volIter++) {
-    if ((*volIter)->Name().Equals(aName)) {
-      return *volIter;
+  VolumeArray::size_type  numVolumes = NumVolumes();
+  VolumeArray::index_type volIndex;
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    RefPtr<Volume> vol = GetVolume(volIndex);
+    if (vol->Name().Equals(aName)) {
+      return vol;
     }
   }
   return NULL;
 }
 
 //static
 TemporaryRef<Volume>
 VolumeManager::FindAddVolumeByName(const nsCSubstring &aName)
 {
   RefPtr<Volume> vol = FindVolumeByName(aName);
   if (vol) {
     return vol;
   }
   // No volume found, create and add a new one.
   vol = new Volume(aName);
-  sVolumeManager->mVolumeArray.push_back(vol);
+  sVolumeManager->mVolumeArray.AppendElement(vol);
   return vol;
 }
 
 class VolumeListCallback : public VolumeResponseCallback
 {
   virtual void ResponseReceived(const VolumeCommand *aCommand)
   {
     switch (ResponseCode()) {
@@ -389,16 +410,18 @@ VolumeManager::Start()
 static void
 InitVolumeManagerIOThread()
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   MOZ_ASSERT(!sVolumeManager);
 
   sVolumeManager = new VolumeManager();
   VolumeManager::Start();
+
+  InitVolumeServiceTestIOThread();
 }
 
 static void
 ShutdownVolumeManagerIOThread()
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
 
   sVolumeManager = NULL;
@@ -419,15 +442,17 @@ InitVolumeManager()
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(InitVolumeManagerIOThread));
 }
 
 void
 ShutdownVolumeManager()
 {
+  ShutdownVolumeServiceTest();
+
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownVolumeManagerIOThread));
 }
 
 } // system
 } // mozilla
--- a/dom/system/gonk/VolumeManager.h
+++ b/dom/system/gonk/VolumeManager.h
@@ -8,16 +8,17 @@
 #include <vector>
 #include <queue>
 
 #include "base/message_loop.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Observer.h"
 #include "mozilla/RefPtr.h"
 #include "nsString.h"
+#include "nsTArray.h"
 
 #include "Volume.h"
 #include "VolumeCommand.h"
 
 namespace mozilla {
 namespace system {
 
 /***************************************************************************
@@ -72,17 +73,17 @@ namespace system {
 *
 ***************************************************************************/
 
 class VolumeManager : public MessageLoopForIO::Watcher,
                       public RefCounted<VolumeManager>
 {
 public:
 
-  typedef std::vector<RefPtr<Volume> > VolumeArray;
+  typedef nsTArray<RefPtr<Volume> > VolumeArray;
 
   VolumeManager();
   virtual ~VolumeManager();
 
   //-----------------------------------------------------------------------
   //
   // State related methods.
   //
@@ -115,16 +116,18 @@ public:
 
   static void RegisterStateObserver(StateObserver *aObserver);
   static void UnregisterStateObserver(StateObserver *aObserver);
 
   //-----------------------------------------------------------------------
 
   static void Start();
 
+  static VolumeArray::size_type NumVolumes();
+  static TemporaryRef<Volume> GetVolume(VolumeArray::index_type aIndex);
   static TemporaryRef<Volume> FindVolumeByName(const nsCSubstring &aName);
   static TemporaryRef<Volume> FindAddVolumeByName(const nsCSubstring &aName);
 
   static void       PostCommand(VolumeCommand *aCommand);
 
 protected:
 
   virtual void OnFileCanReadWithoutBlocking(int aFd);
--- a/dom/system/gonk/VolumeManagerLog.h
+++ b/dom/system/gonk/VolumeManagerLog.h
@@ -2,19 +2,23 @@
  * 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/. */
 
 #ifndef mozilla_system_volumemanagerlog_h__
 #define mozilla_system_volumemanagerlog_h__
 
 #define USE_DEBUG 0
 
+#if !defined(VOLUME_MANAGER_LOG_TAG)
+#define VOLUME_MANAGER_LOG_TAG  "VolumeManager"
+#endif
+
 #undef LOG
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO,  "VolumeManager" , ## args)
-#define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, "VolumeManager" , ## args)
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO,  VOLUME_MANAGER_LOG_TAG, ## args)
+#define ERR(args...)  __android_log_print(ANDROID_LOG_ERROR, VOLUME_MANAGER_LOG_TAG, ## args)
 
 #if USE_DEBUG
-#define DBG(args...)  __android_log_print(ANDROID_LOG_DEBUG, "VolumeManager" , ## args)
+#define DBG(args...)  __android_log_print(ANDROID_LOG_DEBUG, VOLUME_MANAGER_LOG_TAG, ## args)
 #else
 #define DBG(args...)
 #endif
 
 #endif  // mozilla_system_volumemanagerlog_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/VolumeServiceIOThread.cpp
@@ -0,0 +1,78 @@
+/* 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/. */
+
+#include "VolumeServiceIOThread.h"
+#include "base/message_loop.h"
+#include "nsVolumeService.h"
+#include "nsXULAppAPI.h"
+#include "Volume.h"
+#include "VolumeManager.h"
+
+namespace mozilla {
+namespace system {
+
+VolumeServiceIOThread::VolumeServiceIOThread()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+  VolumeManager::RegisterStateObserver(this);
+  Volume::RegisterObserver(this);
+  UpdateAllVolumes();
+}
+
+VolumeServiceIOThread::~VolumeServiceIOThread()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  Volume::UnregisterObserver(this);
+  VolumeManager::UnregisterStateObserver(this);
+}
+
+void
+VolumeServiceIOThread::Notify(Volume * const &aVolume)
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  nsVolumeService::UpdateVolumeIOThread(aVolume);
+}
+
+void
+VolumeServiceIOThread::Notify(const VolumeManager::StateChangedEvent &aEvent)
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  UpdateAllVolumes();
+}
+
+void
+VolumeServiceIOThread::UpdateAllVolumes()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
+    return;
+  }
+  VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
+  VolumeManager::VolumeArray::index_type volIndex;
+
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    RefPtr<Volume>  vol = VolumeManager::GetVolume(volIndex);
+    nsVolumeService::UpdateVolumeIOThread(vol);
+  }
+}
+
+static RefPtr<VolumeServiceIOThread> sVolumeServiceIOThread;
+
+void
+InitVolumeServiceIOThread()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  sVolumeServiceIOThread = new VolumeServiceIOThread();
+}
+
+void
+ShutdownVolumeServiceIOThread()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  sVolumeServiceIOThread = NULL;
+}
+
+} // system
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/VolumeServiceIOThread.h
@@ -0,0 +1,41 @@
+/* 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/. */
+
+#ifndef mozilla_system_volumeserviceiothread_h__
+#define mozilla_system_volumeserviceiothread_h__
+
+#include "Volume.h"
+#include "VolumeManager.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace system {
+
+/***************************************************************************
+* The nsVolumeServiceIOThread is a companion class to the nsVolumeService
+* class, but whose methods are called from IOThread.
+*/
+class VolumeServiceIOThread : public VolumeManager::StateObserver,
+                              public Volume::EventObserver,
+                              public RefCounted<VolumeServiceIOThread>
+{
+public:
+  VolumeServiceIOThread();
+  ~VolumeServiceIOThread();
+
+private:
+  void  UpdateAllVolumes();
+
+  virtual void Notify(const VolumeManager::StateChangedEvent &aEvent);
+  virtual void Notify(Volume * const &aVolume);
+
+};
+
+void InitVolumeServiceIOThread();
+void ShutdownVolumeServiceIOThread();
+
+} // system
+} // mozilla
+
+#endif  // mozilla_system_volumeserviceiothread_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/VolumeServiceTest.cpp
@@ -0,0 +1,201 @@
+/* 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/. */
+
+#include "VolumeServiceTest.h"
+
+#include "base/message_loop.h"
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsIVolume.h"
+#include "nsIVolumeService.h"
+#include "nsIVolumeStat.h"
+#include "nsXULAppAPI.h"
+
+#include "mozilla/Services.h"
+
+#define VOLUME_MANAGER_LOG_TAG  "VolumeServiceTest"
+#include "VolumeManagerLog.h"
+
+using namespace mozilla::services;
+
+namespace mozilla {
+namespace system {
+
+#define TEST_NSVOLUME_OBSERVER  1
+
+#if TEST_NSVOLUME_OBSERVER
+
+/***************************************************************************
+* A test class to verify that the Observer stuff is working properly.
+*/
+class VolumeTestObserver : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  VolumeTestObserver()
+  {
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (!obs) {
+      return;
+    }
+    obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
+  }
+  ~VolumeTestObserver()
+  {
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (!obs) {
+      return;
+    }
+    obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
+  }
+
+  void LogVolume(nsIVolume *vol)
+  {
+    nsString volName;
+    nsString mountPoint;
+    PRInt32 volState;
+
+    vol->GetName(volName);
+    vol->GetMountPoint(mountPoint);
+    vol->GetState(&volState);
+
+    LOG("  Volume: %s MountPoint: %s State: %s",
+        NS_LossyConvertUTF16toASCII(volName).get(),
+        NS_LossyConvertUTF16toASCII(mountPoint).get(),
+        NS_VolumeStateStr(volState));
+
+    nsCOMPtr<nsIVolumeStat> stat;
+    nsresult rv = vol->GetStats(getter_AddRefs(stat));
+    if (NS_SUCCEEDED(rv)) {
+      PRInt64 totalBytes;
+      PRInt64 freeBytes;
+
+      stat->GetTotalBytes(&totalBytes);
+      stat->GetFreeBytes(&freeBytes);
+
+      LOG("  Total Space: %llu Mb  Free Bytes: %llu Mb",
+          totalBytes / (1024LL * 1024LL), freeBytes / (1024LL * 1024LL));
+    }
+    else {
+      LOG("  Unable to retrieve stats");
+    }
+  }
+};
+static nsCOMPtr<VolumeTestObserver>  sTestObserver;
+
+NS_IMPL_ISUPPORTS1(VolumeTestObserver, nsIObserver)
+
+NS_IMETHODIMP
+VolumeTestObserver::Observe(nsISupports *aSubject,
+                            const char *aTopic,
+                            const PRUnichar *aData)
+{
+  LOG("TestObserver: topic: %s", aTopic);
+
+  if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
+  if (vol) {
+    LogVolume(vol);
+  }
+
+  // Since this observe method was called then we know that the service
+  // has been initialized so we can do the VolumeService tests.
+
+  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+  if (!vs) {
+    ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = vs->GetVolumeByName(NS_LITERAL_STRING("sdcard"), getter_AddRefs(vol));
+  if (NS_SUCCEEDED(rv)) {
+    LOG("GetVolumeByName( 'sdcard' ) succeeded (expected)");
+    LogVolume(vol);
+  } else {
+    ERR("GetVolumeByName( 'sdcard' ) failed (unexpected)");
+  }
+
+  rv = vs->GetVolumeByName(NS_LITERAL_STRING("foo"), getter_AddRefs(vol));
+  if (NS_SUCCEEDED(rv)) {
+    ERR("GetVolumeByName( 'foo' ) succeeded (unexpected)");
+  } else {
+    LOG("GetVolumeByName( 'foo' ) failed (expected)");
+  }
+
+  rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard"), getter_AddRefs(vol));
+  if (NS_SUCCEEDED(rv)) {
+    LOG("GetVolumeByPath( '/mnt/sdcard' ) succeeded (expected)");
+    LogVolume(vol);
+  } else {
+    ERR("GetVolumeByPath( '/mnt/sdcard' ) failed (unexpected");
+  }
+
+  rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcard/foo"), getter_AddRefs(vol));
+  if (NS_SUCCEEDED(rv)) {
+    LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) succeeded (expected)");
+    LogVolume(vol);
+  } else {
+    LOG("GetVolumeByPath( '/mnt/sdcard/foo' ) failed (unexpected)");
+  }
+
+  rv = vs->GetVolumeByPath(NS_LITERAL_STRING("/mnt/sdcardfoo"), getter_AddRefs(vol));
+  if (NS_SUCCEEDED(rv)) {
+    ERR("GetVolumeByPath( '/mnt/sdcardfoo' ) succeeded (unexpected)");
+  } else {
+    LOG("GetVolumeByPath( '/mnt/sdcardfoo' ) failed (expected)");
+  }
+
+  return NS_OK;
+}
+
+class InitVolumeServiceTestIO : public nsRunnable
+{
+public:
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DBG("InitVolumeServiceTest called");
+    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+    if (!vs) {
+      ERR("do_GetService('%s') failed", NS_VOLUMESERVICE_CONTRACTID);
+      return NS_ERROR_FAILURE;
+    }
+    sTestObserver = new VolumeTestObserver();
+
+    return NS_OK;
+  }
+};
+#endif // TEST_NSVOLUME_OBSERVER
+
+void
+InitVolumeServiceTestIOThread()
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+#if TEST_NSVOLUME_OBSERVER
+  // Now that the volume manager is initialized we can go
+  // ahead and do our test (on main thread).
+  NS_DispatchToMainThread(new InitVolumeServiceTestIO());
+#endif
+}
+
+void
+ShutdownVolumeServiceTest()
+{
+#if TEST_NSVOLUME_OBSERVER
+  DBG("ShutdownVolumeServiceTestIOThread called");
+  sTestObserver = NULL;
+#endif
+}
+
+} // system
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/VolumeServiceTest.h
@@ -0,0 +1,19 @@
+/* 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/. */
+
+#ifndef mozilla_system_volumeservicetest_h__
+#define mozilla_system_volumeservicetest_h__
+
+
+namespace mozilla {
+namespace system {
+
+void InitVolumeServiceTestIOThread();
+void ShutdownVolumeServiceTest();
+
+} // system
+} // mozilla
+
+#endif  // mozilla_system_volumeservicetest_h__
+
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsIVolume.idl
@@ -0,0 +1,44 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+#include "nsIVolumeStat.idl"
+
+[scriptable, uuid(3c9cae8d-9da2-4aa1-b8bf-4db8c8620808)]
+interface nsIVolume : nsISupports
+{
+  // These MUST match the states from android's system/vold/Volume.h header
+  const long STATE_INIT        = -1;
+  const long STATE_NOMEDIA     = 0;
+  const long STATE_IDLE        = 1;
+  const long STATE_PENDING     = 2;
+  const long STATE_CHECKING    = 3;
+  const long STATE_MOUNTED     = 4;
+  const long STATE_UNMOUNTING  = 5;
+  const long STATE_FORMATTING  = 6;
+  const long STATE_SHARED      = 7;
+  const long STATE_SHAREDMNT   = 8;
+
+  readonly attribute DOMString name;
+
+  readonly attribute DOMString mountPoint;
+
+  readonly attribute long state;
+
+  nsIVolumeStat getStats();
+};
+
+%{C++
+// For use with the ObserverService
+#define NS_VOLUME_STATE_CHANGED  "volume-state-changed"
+
+namespace mozilla {
+namespace system {
+
+// Convert a state into a loggable/printable string.
+const char *NS_VolumeStateStr(PRInt32 aState);
+
+} // system
+} // mozilla
+%}
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -0,0 +1,19 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+#include "nsIVolume.idl"
+
+[scriptable, uuid(b31bd379-4bd2-4189-a85b-c0927a266a85)]
+interface nsIVolumeService : nsISupports
+{
+    nsIVolume getVolumeByName(in DOMString volName);
+    nsIVolume getVolumeByPath(in DOMString path);
+};
+
+%{C++
+#define NS_VOLUMESERVICE_CID \
+  {0xb31bd379, 0x4bd2, 0x4189, {0xa8, 0x5b, 0xc0, 0x92, 0x7a, 0x26, 0x6a, 0x85}}
+#define NS_VOLUMESERVICE_CONTRACTID "@mozilla.org/telephony/volume-service;1"
+%}
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsIVolumeStat.idl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(b4c050d0-c57a-11e1-9b21-0800200c9a66)]
+interface nsIVolumeStat : nsISupports
+{
+    readonly attribute  long long   totalBytes;
+    readonly attribute  long long   freeBytes;
+};
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolume.cpp
@@ -0,0 +1,62 @@
+/* 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/. */
+
+#include "nsVolume.h"
+#include "nsISupportsUtils.h"
+#include "nsIVolume.h"
+#include "nsVolumeStat.h"
+
+namespace mozilla {
+namespace system {
+
+const char *
+NS_VolumeStateStr(PRInt32 aState)
+{
+  switch (aState) {
+    case nsIVolume::STATE_INIT:       return "Init";
+    case nsIVolume::STATE_NOMEDIA:    return "NoMedia";
+    case nsIVolume::STATE_IDLE:       return "Idle";
+    case nsIVolume::STATE_PENDING:    return "Pending";
+    case nsIVolume::STATE_CHECKING:   return "Checking";
+    case nsIVolume::STATE_MOUNTED:    return "Mounted";
+    case nsIVolume::STATE_UNMOUNTING: return "Unmounting";
+    case nsIVolume::STATE_FORMATTING: return "Formatting";
+    case nsIVolume::STATE_SHARED:     return "Shared";
+    case nsIVolume::STATE_SHAREDMNT:  return "Shared-Mounted";
+  }
+  return "???";
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsVolume, nsIVolume)
+
+NS_IMETHODIMP nsVolume::GetName(nsAString &aName)
+{
+  aName = mName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsVolume::GetMountPoint(nsAString &aMountPoint)
+{
+  aMountPoint = mMountPoint;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsVolume::GetState(PRInt32 *aState)
+{
+  *aState = mState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsVolume::GetStats(nsIVolumeStat **aResult NS_OUTPARAM)
+{
+  if (mState != STATE_MOUNTED) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  NS_IF_ADDREF(*aResult = new nsVolumeStat(mMountPoint));
+  return NS_OK;
+}
+
+} // system
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolume.h
@@ -0,0 +1,70 @@
+/* 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/. */
+
+#ifndef mozilla_system_nsvolume_h__
+#define mozilla_system_nsvolume_h__
+
+#include "Volume.h"
+#include "nsIVolume.h"
+
+namespace mozilla {
+namespace system {
+
+class nsVolume : public nsIVolume
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIVOLUME
+
+  nsVolume(const Volume *aVolume)
+    : mName(NS_ConvertUTF8toUTF16(aVolume->Name())),
+      mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())),
+      mState(aVolume->State())
+  {
+  }
+
+  nsVolume(const nsAString &aName)
+    : mName(aName),
+      mState(STATE_INIT)
+  {
+  }
+
+  bool Equals(const nsVolume *aVolume)
+  {
+    return mName.Equals(aVolume->mName)
+        && mMountPoint.Equals(aVolume->mMountPoint)
+        && (mState == aVolume->mState);
+  }
+
+  void Set(const nsVolume *aVolume)
+  {
+    mName = aVolume->mName;
+    mMountPoint = aVolume->mMountPoint;
+    mState = aVolume->mState;
+  }
+
+  const nsString &Name() const        { return mName; }
+  const char *NameStr() const         { return NS_LossyConvertUTF16toASCII(mName).get(); }
+
+  const nsString &MountPoint() const  { return mMountPoint; }
+  const char *MountPointStr() const   { return NS_LossyConvertUTF16toASCII(mMountPoint).get(); }
+
+  long State() const                  { return mState; }
+  const char *StateStr() const        { return Volume::StateStr((Volume::STATE)mState); }
+
+  typedef nsTArray<nsRefPtr<nsVolume> > Array;
+
+private:
+  ~nsVolume() {}
+
+protected:
+  nsString mName;
+  nsString mMountPoint;
+  long mState;
+};
+
+} // system
+} // mozilla
+
+#endif  // mozilla_system_nsvolume_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -0,0 +1,194 @@
+/* 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/. */
+
+#include "nsVolumeService.h"
+
+#include "Volume.h"
+#include "VolumeManager.h"
+#include "VolumeServiceIOThread.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDependentSubstring.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISupportsUtils.h"
+#include "nsIVolume.h"
+#include "nsIVolumeService.h"
+#include "nsLocalFile.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/Services.h"
+
+#define VOLUME_MANAGER_LOG_TAG  "nsVolumeService"
+#include "VolumeManagerLog.h"
+
+#include <stdlib.h>
+
+using namespace mozilla::services;
+
+namespace mozilla {
+namespace system {
+
+NS_IMPL_ISUPPORTS1(nsVolumeService, nsIVolumeService)
+
+nsVolumeService::nsVolumeService()
+{
+  // Startup the IOThread side of things. The actual volume changes
+  // are captured by the IOThread and forwarded to main thread.
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableFunction(InitVolumeServiceIOThread));
+}
+
+nsVolumeService::~nsVolumeService()
+{
+  XRE_GetIOMessageLoop()->PostTask(
+      FROM_HERE,
+      NewRunnableFunction(ShutdownVolumeServiceIOThread));
+}
+
+/* nsIVolume getVolumeByName (in DOMString volName); */
+NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString &aVolName, nsIVolume **aResult NS_OUTPARAM)
+{
+  nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
+  if (!vol) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  NS_ADDREF(*aResult = vol);
+  return NS_OK;
+}
+
+/* nsIVolume getVolumeByPath (in DOMString path); */
+NS_IMETHODIMP nsVolumeService::GetVolumeByPath(const nsAString &aPath, nsIVolume **aResult NS_OUTPARAM)
+{
+  nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
+  char realPathBuf[PATH_MAX];
+
+  if (!realpath(utf8Path.get(), realPathBuf)) {
+    return NSRESULT_FOR_ERRNO();
+  }
+
+  // The volume mount point is always a directory. Something like /mnt/sdcard
+  // Once we have a full qualified pathname with symlinks removed (which is
+  // what realpath does), we basically check if aPath starts with the mount
+  // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
+  // do want it to match /mnt/sdcard/foo
+  // So we add a trailing slash to the mount point and the pathname passed in
+  // prior to doing the comparison.
+
+  strlcat(realPathBuf, "/", sizeof(realPathBuf));
+
+  nsVolume::Array::size_type  numVolumes = mVolumeArray.Length();
+  nsVolume::Array::index_type volIndex;
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
+    nsCAutoString volMountPointSlash = NS_ConvertUTF16toUTF8(vol->MountPoint());
+    volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
+    nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
+    if (volMountPointSlash.Equals(testStr)) {
+      NS_ADDREF(*aResult = vol);
+      return NS_OK;
+    }
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+already_AddRefed<nsVolume> nsVolumeService::FindVolumeByName(const nsAString &aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsVolume::Array::size_type  numVolumes = mVolumeArray.Length();
+  nsVolume::Array::index_type volIndex;
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
+    if (vol->Name().Equals(aName)) {
+      return vol.forget();
+    }
+  }
+  return NULL;
+}
+
+already_AddRefed<nsVolume> nsVolumeService::FindAddVolumeByName(const nsAString &aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<nsVolume> vol;
+  vol = FindVolumeByName(aName);
+  if (vol) {
+    return vol.forget();
+  }
+  // Volume not found - add a new one
+  vol = new nsVolume(aName);
+  mVolumeArray.AppendElement(vol);
+  return vol.forget();
+}
+
+void nsVolumeService::UpdateVolume(const nsVolume *aVolume)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<nsVolume> vol = FindAddVolumeByName(aVolume->Name());
+  if (vol->Equals(aVolume)) {
+    // Nothing has really changed. Don't bother telling anybody.
+    return;
+  }
+  vol->Set(aVolume);
+  nsRefPtr<nsIObserverService> obs = GetObserverService();
+  if (!obs) {
+    return;
+  }
+  nsString stateStr(NS_ConvertUTF8toUTF16(vol->StateStr()));
+  obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
+}
+
+/***************************************************************************
+* The UpdateVolumeRunnable creates an nsVolume and updates the main thread
+* data structure while running on the main thread.
+*/
+class UpdateVolumeRunnable : public nsRunnable
+{
+public:
+  UpdateVolumeRunnable(const Volume *aVolume)
+    : mVolume(new nsVolume(aVolume))
+  {
+    MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    DBG("UpdateVolumeRunnable::Run '%s' state %s",
+        mVolume->NameStr(), mVolume->StateStr());
+
+    nsCOMPtr<nsIVolumeService> ivs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+    if (!ivs) {
+      return NS_OK;
+    }
+    nsCOMPtr<nsVolumeService> vs(do_QueryInterface(ivs));
+    if (!vs) {
+      return NS_OK;
+    }
+    vs->UpdateVolume(mVolume);
+    mVolume = NULL;
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsVolume>  mVolume;
+};
+
+//static
+void nsVolumeService::UpdateVolumeIOThread(const Volume *aVolume)
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+  NS_DispatchToMainThread(new UpdateVolumeRunnable(aVolume));
+}
+
+} // system
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolumeService.h
@@ -0,0 +1,45 @@
+/* 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/. */
+
+#ifndef mozilla_system_nsvolumeservice_h__
+#define mozilla_system_nsvolumeservice_h__
+
+#include "nsCOMPtr.h"
+#include "nsIVolume.h"
+#include "nsIVolumeService.h"
+#include "nsVolume.h"
+#include "Volume.h"
+
+namespace mozilla {
+namespace system {
+
+/***************************************************************************
+* The nsVolumeData class encapsulates the data that is updated/maintained
+* on the main thread in order to support the nsIVolume and nsIVolumeService
+* classes.
+*/
+
+class nsVolumeService : public nsIVolumeService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIVOLUMESERVICE
+
+  nsVolumeService();
+
+  already_AddRefed<nsVolume> FindVolumeByName(const nsAString &aName);
+  already_AddRefed<nsVolume> FindAddVolumeByName(const nsAString &aName);
+  void UpdateVolume(const nsVolume *aVolume);
+  static void UpdateVolumeIOThread(const Volume *aVolume);
+
+private:
+  ~nsVolumeService();
+
+  nsVolume::Array  mVolumeArray;
+};
+
+} // system
+} // mozilla
+
+#endif  // mozilla_system_nsvolumeservice_h__
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolumeStat.cpp
@@ -0,0 +1,41 @@
+/* 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/. */
+
+#include "nsVolumeStat.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace system {
+
+NS_IMPL_ISUPPORTS1(nsVolumeStat, nsIVolumeStat)
+
+nsVolumeStat::nsVolumeStat(const nsAString &aPath)
+{
+  nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
+
+  if (statfs(utf8Path.get(), &mStat) != 0) {
+    memset(&mStat, 0, sizeof(mStat));
+  }
+}
+
+nsVolumeStat::~nsVolumeStat()
+{
+}
+
+/* readonly attribute long long totalBytes; */
+NS_IMETHODIMP nsVolumeStat::GetTotalBytes(PRInt64 *aTotalBytes)
+{
+  *aTotalBytes = mStat.f_blocks * mStat.f_bsize;
+  return NS_OK;
+}
+
+/* readonly attribute long long freeBytes; */
+NS_IMETHODIMP nsVolumeStat::GetFreeBytes(PRInt64 *aFreeBytes)
+{
+  *aFreeBytes = mStat.f_bfree * mStat.f_bsize;
+  return NS_OK;
+}
+
+} // system
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/nsVolumeStat.h
@@ -0,0 +1,32 @@
+/* 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/. */
+
+#ifndef mozilla_system_nsvolumestat_h__
+#define mozilla_system_nsvolumestat_h__
+
+#include "nsIVolumeStat.h"
+#include "nsString.h"
+#include <sys/statfs.h>
+
+namespace mozilla {
+namespace system {
+
+class nsVolumeStat : public nsIVolumeStat
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIVOLUMESTAT
+
+  nsVolumeStat(const nsAString &aPath);
+
+private:
+  ~nsVolumeStat();
+
+  struct statfs mStat;
+};
+
+} // system
+} // mozilla
+
+#endif  // mozilla_system_nsvolumestat_h__
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -101,16 +101,18 @@ using mozilla::dom::gonk::SystemWorkerMa
   {0xd53b6524, 0x6ac3, 0x42b0, {0xae, 0xca, 0x62, 0xb3, 0xc4, 0xe5, 0x2b, 0x04}}
 #define SYSTEMWORKERMANAGER_CONTRACTID \
   "@mozilla.org/telephony/system-worker-manager;1"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "AudioManager.h"
 using mozilla::dom::gonk::AudioManager;
+#include "nsVolumeService.h"
+using mozilla::system::nsVolumeService;
 #endif
 #include "nsDOMMutationObserver.h"
 
 // Editor stuff
 #include "nsEditorCID.h"
 #include "nsEditor.h"
 #include "nsPlaintextEditor.h"
 #include "nsEditorController.h" //CID
@@ -260,16 +262,17 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
 #ifdef MOZ_B2G_RIL
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
                                          SystemWorkerManager::FactoryCreate)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMMutationObserver)
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_CONSTRUCTOR(AudioManager)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVolumeService)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceSensors)
 
 #ifndef MOZ_WIDGET_GONK
 #if defined(ANDROID) || defined(MOZ_PLATFORM_MAEMO)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHapticFeedback)
 #endif
 #endif
@@ -737,16 +740,17 @@ NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
 #ifdef MOZ_B2G_RIL
 NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_CID);
 #endif
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(NS_AUDIOMANAGER_CID);
+NS_DEFINE_NAMED_CID(NS_VOLUMESERVICE_CID);
 #endif
 #ifdef ENABLE_EDITOR_API_LOG
 NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 #else
 NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
@@ -1007,16 +1011,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
   { &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, NULL, DOMRequestServiceConstructor },
 #ifdef MOZ_B2G_RIL
   { &kSYSTEMWORKERMANAGER_CID, true, NULL, SystemWorkerManagerConstructor },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { &kNS_AUDIOMANAGER_CID, true, NULL, AudioManagerConstructor },
+  { &kNS_VOLUMESERVICE_CID, true, NULL, nsVolumeServiceConstructor },
 #endif
 #ifdef ENABLE_EDITOR_API_LOG
   { &kNS_HTMLEDITOR_CID, false, NULL, nsHTMLEditorLogConstructor },
 #else
   { &kNS_HTMLEDITOR_CID, false, NULL, nsHTMLEditorConstructor },
 #endif
   { &kNS_EDITORCONTROLLER_CID, false, NULL, nsEditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, NULL, nsEditingControllerConstructor },
@@ -1142,16 +1147,17 @@ static const mozilla::Module::ContractID
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
 #ifdef MOZ_B2G_RIL  
   { SYSTEMWORKERMANAGER_CONTRACTID, &kSYSTEMWORKERMANAGER_CID },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { NS_AUDIOMANAGER_CONTRACTID, &kNS_AUDIOMANAGER_CID },
+  { NS_VOLUMESERVICE_CONTRACTID, &kNS_VOLUMESERVICE_CID },
 #endif
 #ifdef ENABLE_EDITOR_API_LOG
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
 #else
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
 #endif
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },