From 6c49401e4b85a3afbf8d34834e0f26595b7eea92 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Mon, 16 Jul 2012 12:38:18 -0400 Subject: [PATCH] Bug 766324 - Add a volume IDL to make volumes scriptable r=qdot --- dom/system/gonk/AutoMounter.cpp | 12 +- dom/system/gonk/Makefile.in | 8 + dom/system/gonk/Volume.cpp | 17 +- dom/system/gonk/Volume.h | 44 +---- dom/system/gonk/VolumeManager.cpp | 59 +++++-- dom/system/gonk/VolumeManager.h | 5 +- dom/system/gonk/VolumeManagerLog.h | 10 +- dom/system/gonk/VolumeServiceIOThread.cpp | 78 +++++++++ dom/system/gonk/VolumeServiceIOThread.h | 41 +++++ dom/system/gonk/VolumeServiceTest.cpp | 201 ++++++++++++++++++++++ dom/system/gonk/VolumeServiceTest.h | 19 ++ dom/system/gonk/nsIVolume.idl | 44 +++++ dom/system/gonk/nsIVolumeService.idl | 19 ++ dom/system/gonk/nsIVolumeStat.idl | 12 ++ dom/system/gonk/nsVolume.cpp | 62 +++++++ dom/system/gonk/nsVolume.h | 70 ++++++++ dom/system/gonk/nsVolumeService.cpp | 194 +++++++++++++++++++++ dom/system/gonk/nsVolumeService.h | 45 +++++ dom/system/gonk/nsVolumeStat.cpp | 41 +++++ dom/system/gonk/nsVolumeStat.h | 32 ++++ layout/build/nsLayoutModule.cpp | 6 + 21 files changed, 954 insertions(+), 65 deletions(-) create mode 100644 dom/system/gonk/VolumeServiceIOThread.cpp create mode 100644 dom/system/gonk/VolumeServiceIOThread.h create mode 100644 dom/system/gonk/VolumeServiceTest.cpp create mode 100644 dom/system/gonk/VolumeServiceTest.h create mode 100644 dom/system/gonk/nsIVolume.idl create mode 100644 dom/system/gonk/nsIVolumeService.idl create mode 100644 dom/system/gonk/nsIVolumeStat.idl create mode 100644 dom/system/gonk/nsVolume.cpp create mode 100644 dom/system/gonk/nsVolume.h create mode 100644 dom/system/gonk/nsVolumeService.cpp create mode 100644 dom/system/gonk/nsVolumeService.h create mode 100644 dom/system/gonk/nsVolumeStat.cpp create mode 100644 dom/system/gonk/nsVolumeStat.h diff --git a/dom/system/gonk/AutoMounter.cpp b/dom/system/gonk/AutoMounter.cpp index fecc78f88d0c..138633eb369e 100644 --- a/dom/system/gonk/AutoMounter.cpp +++ b/dom/system/gonk/AutoMounter.cpp @@ -208,10 +208,11 @@ public: mMode(AUTOMOUNTER_DISABLE) { VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver); + Volume::RegisterObserver(&mVolumeEventObserver); for (size_t i = 0; i < NS_ARRAY_LENGTH(sAutoVolumeName); i++) { RefPtr vol = VolumeManager::FindAddVolumeByName(sAutoVolumeName[i]); - if (vol != NULL) { + if (vol) { vol->RegisterObserver(&mVolumeEventObserver); mAutoVolume.AppendElement(vol); } @@ -228,6 +229,7 @@ public: for (volIndex = 0; volIndex < numVolumes; volIndex++) { mAutoVolume[volIndex]->UnregisterObserver(&mVolumeEventObserver); } + Volume::UnregisterObserver(&mVolumeEventObserver); VolumeManager::UnregisterStateObserver(&mVolumeManagerStateObserver); } @@ -377,14 +379,14 @@ AutoMounter::UpdateState() 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); @@ -398,13 +400,13 @@ AutoMounter::UpdateState() } 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()); diff --git a/dom/system/gonk/Makefile.in b/dom/system/gonk/Makefile.in index b843afeef3fe..b49a42838774 100644 --- a/dom/system/gonk/Makefile.in +++ b/dom/system/gonk/Makefile.in @@ -27,6 +27,9 @@ XPIDLSRCS = \ nsIAudioManager.idl \ nsINetworkManager.idl \ nsIRadioInterfaceLayer.idl \ + nsIVolume.idl \ + nsIVolumeService.idl \ + nsIVolumeStat.idl \ nsIWorkerHolder.idl \ $(NULL) @@ -45,9 +48,14 @@ CPPSRCS += \ 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 diff --git a/dom/system/gonk/Volume.cpp b/dom/system/gonk/Volume.cpp index 34427c2d8c59..ffa596a07870 100644 --- a/dom/system/gonk/Volume.cpp +++ b/dom/system/gonk/Volume.cpp @@ -6,6 +6,7 @@ #include "VolumeCommand.h" #include "VolumeManager.h" #include "VolumeManagerLog.h" +#include "nsIVolume.h" #include "nsXULAppAPI.h" #include @@ -13,11 +14,13 @@ 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()); @@ -67,7 +70,7 @@ Volume::SetState(Volume::STATE aNewState) 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; } @@ -115,16 +118,22 @@ 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 vol = VolumeManager::GetVolume(volIndex); + aObserver->Notify(vol); + } } +//static void Volume::UnregisterObserver(Volume::EventObserver *aObserver) { diff --git a/dom/system/gonk/Volume.h b/dom/system/gonk/Volume.h index 5d83347885ed..e353acb6b0c3 100644 --- a/dom/system/gonk/Volume.h +++ b/dom/system/gonk/Volume.h @@ -6,10 +6,11 @@ #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 { @@ -26,39 +27,11 @@ namespace system { class Volume : public RefCounted { 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; } @@ -75,8 +48,8 @@ public: typedef mozilla::ObserverList 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 @@ -102,7 +75,8 @@ private: STATE mState; const nsCString mName; nsCString mMountPoint; - EventObserverList mEventObserverList; + + static EventObserverList mEventObserverList; }; } // system diff --git a/dom/system/gonk/VolumeManager.cpp b/dom/system/gonk/VolumeManager.cpp index 139e4fef1fde..5e70484fc617 100644 --- a/dom/system/gonk/VolumeManager.cpp +++ b/dom/system/gonk/VolumeManager.cpp @@ -2,22 +2,24 @@ * 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 #include #include #include -#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 { @@ -38,6 +40,24 @@ VolumeManager::~VolumeManager() { } +//static +size_t +VolumeManager::NumVolumes() +{ + if (!sVolumeManager) { + return 0; + } + return sVolumeManager->mVolumeArray.Length(); +} + +//static +TemporaryRef +VolumeManager::GetVolume(size_t aIndex) +{ + MOZ_ASSERT(aIndex < NumVolumes()); + return sVolumeManager->mVolumeArray[aIndex]; +} + //static VolumeManager::STATE VolumeManager::State() @@ -94,11 +114,12 @@ 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 vol = GetVolume(volIndex); + if (vol->Name().Equals(aName)) { + return vol; } } return NULL; @@ -114,7 +135,7 @@ VolumeManager::FindAddVolumeByName(const nsCSubstring &aName) } // No volume found, create and add a new one. vol = new Volume(aName); - sVolumeManager->mVolumeArray.push_back(vol); + sVolumeManager->mVolumeArray.AppendElement(vol); return vol; } @@ -394,6 +415,8 @@ InitVolumeManagerIOThread() sVolumeManager = new VolumeManager(); VolumeManager::Start(); + + InitVolumeServiceTestIOThread(); } static void @@ -424,6 +447,8 @@ InitVolumeManager() void ShutdownVolumeManager() { + ShutdownVolumeServiceTest(); + XRE_GetIOMessageLoop()->PostTask( FROM_HERE, NewRunnableFunction(ShutdownVolumeManagerIOThread)); diff --git a/dom/system/gonk/VolumeManager.h b/dom/system/gonk/VolumeManager.h index 411cfaa73c03..dbc94e4ebb4f 100644 --- a/dom/system/gonk/VolumeManager.h +++ b/dom/system/gonk/VolumeManager.h @@ -13,6 +13,7 @@ #include "mozilla/Observer.h" #include "mozilla/RefPtr.h" #include "nsString.h" +#include "nsTArray.h" #include "Volume.h" #include "VolumeCommand.h" @@ -77,7 +78,7 @@ class VolumeManager : public MessageLoopForIO::Watcher, { public: - typedef std::vector > VolumeArray; + typedef nsTArray > VolumeArray; VolumeManager(); virtual ~VolumeManager(); @@ -120,6 +121,8 @@ public: static void Start(); + static VolumeArray::size_type NumVolumes(); + static TemporaryRef GetVolume(VolumeArray::index_type aIndex); static TemporaryRef FindVolumeByName(const nsCSubstring &aName); static TemporaryRef FindAddVolumeByName(const nsCSubstring &aName); diff --git a/dom/system/gonk/VolumeManagerLog.h b/dom/system/gonk/VolumeManagerLog.h index 6486aade0bbb..cde28cc962ed 100644 --- a/dom/system/gonk/VolumeManagerLog.h +++ b/dom/system/gonk/VolumeManagerLog.h @@ -7,12 +7,16 @@ #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 diff --git a/dom/system/gonk/VolumeServiceIOThread.cpp b/dom/system/gonk/VolumeServiceIOThread.cpp new file mode 100644 index 000000000000..2ec5c17ca15a --- /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 vol = VolumeManager::GetVolume(volIndex); + nsVolumeService::UpdateVolumeIOThread(vol); + } +} + +static RefPtr sVolumeServiceIOThread; + +void +InitVolumeServiceIOThread() +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + sVolumeServiceIOThread = new VolumeServiceIOThread(); +} + +void +ShutdownVolumeServiceIOThread() +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + sVolumeServiceIOThread = NULL; +} + +} // system +} // mozilla diff --git a/dom/system/gonk/VolumeServiceIOThread.h b/dom/system/gonk/VolumeServiceIOThread.h new file mode 100644 index 000000000000..4e92f8a4796a --- /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 +{ +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__ diff --git a/dom/system/gonk/VolumeServiceTest.cpp b/dom/system/gonk/VolumeServiceTest.cpp new file mode 100644 index 000000000000..4c3745c54084 --- /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 obs = GetObserverService(); + if (!obs) { + return; + } + obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false); + } + ~VolumeTestObserver() + { + nsCOMPtr 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 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 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 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 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 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 diff --git a/dom/system/gonk/VolumeServiceTest.h b/dom/system/gonk/VolumeServiceTest.h new file mode 100644 index 000000000000..71a92bf6c666 --- /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__ + diff --git a/dom/system/gonk/nsIVolume.idl b/dom/system/gonk/nsIVolume.idl new file mode 100644 index 000000000000..f696f9728ef9 --- /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 +%} diff --git a/dom/system/gonk/nsIVolumeService.idl b/dom/system/gonk/nsIVolumeService.idl new file mode 100644 index 000000000000..e793349c6b71 --- /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" +%} diff --git a/dom/system/gonk/nsIVolumeStat.idl b/dom/system/gonk/nsIVolumeStat.idl new file mode 100644 index 000000000000..1d725689d304 --- /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; +}; diff --git a/dom/system/gonk/nsVolume.cpp b/dom/system/gonk/nsVolume.cpp new file mode 100644 index 000000000000..a46c4a9795b2 --- /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 diff --git a/dom/system/gonk/nsVolume.h b/dom/system/gonk/nsVolume.h new file mode 100644 index 000000000000..7559fa6d99f7 --- /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 > Array; + +private: + ~nsVolume() {} + +protected: + nsString mName; + nsString mMountPoint; + long mState; +}; + +} // system +} // mozilla + +#endif // mozilla_system_nsvolume_h__ diff --git a/dom/system/gonk/nsVolumeService.cpp b/dom/system/gonk/nsVolumeService.cpp new file mode 100644 index 000000000000..a970c92dc3b9 --- /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 + +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 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 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 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 vol = mVolumeArray[volIndex]; + if (vol->Name().Equals(aName)) { + return vol.forget(); + } + } + return NULL; +} + +already_AddRefed nsVolumeService::FindAddVolumeByName(const nsAString &aName) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr 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 vol = FindAddVolumeByName(aVolume->Name()); + if (vol->Equals(aVolume)) { + // Nothing has really changed. Don't bother telling anybody. + return; + } + vol->Set(aVolume); + nsRefPtr 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 ivs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + if (!ivs) { + return NS_OK; + } + nsCOMPtr vs(do_QueryInterface(ivs)); + if (!vs) { + return NS_OK; + } + vs->UpdateVolume(mVolume); + mVolume = NULL; + return NS_OK; + } + +private: + nsRefPtr mVolume; +}; + +//static +void nsVolumeService::UpdateVolumeIOThread(const Volume *aVolume) +{ + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); + NS_DispatchToMainThread(new UpdateVolumeRunnable(aVolume)); +} + +} // system +} // mozilla diff --git a/dom/system/gonk/nsVolumeService.h b/dom/system/gonk/nsVolumeService.h new file mode 100644 index 000000000000..03d5e35aadf0 --- /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 FindVolumeByName(const nsAString &aName); + already_AddRefed 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__ diff --git a/dom/system/gonk/nsVolumeStat.cpp b/dom/system/gonk/nsVolumeStat.cpp new file mode 100644 index 000000000000..95cf8d7761a1 --- /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 diff --git a/dom/system/gonk/nsVolumeStat.h b/dom/system/gonk/nsVolumeStat.h new file mode 100644 index 000000000000..4cef60bc9806 --- /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 + +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__ diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 69f5ab1ddab9..481af2359811 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -106,6 +106,8 @@ using mozilla::dom::gonk::SystemWorkerManager; #ifdef MOZ_WIDGET_GONK #include "AudioManager.h" using mozilla::dom::gonk::AudioManager; +#include "nsVolumeService.h" +using mozilla::system::nsVolumeService; #endif #include "nsDOMMutationObserver.h" @@ -265,6 +267,7 @@ 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) @@ -742,6 +745,7 @@ 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); @@ -1012,6 +1016,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { #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 }, @@ -1147,6 +1152,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { #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 },