Bug 785124 - Pt 1 - Add VolumeMountLock which allows SDCard to be locked. r=dougt

From 8e39b8e5f3ab7e6344b0a8a5eeabdcf672de8fb4 Mon Sep 17 00:00:00 2001
---
 dom/ipc/ContentChild.cpp                  |   18 +++-
 dom/ipc/ContentChild.h                    |    5 +-
 dom/ipc/ContentParent.cpp                 |   22 +++-
 dom/ipc/ContentParent.h                   |    2 +
 dom/ipc/PContent.ipdl                     |    6 +-
 dom/system/gonk/AutoMounter.cpp           |   19 +++-
 dom/system/gonk/Makefile.in               |    4 +-
 dom/system/gonk/Volume.cpp                |   62 ++++++++++-
 dom/system/gonk/Volume.h                  |   11 +-
 dom/system/gonk/VolumeServiceIOThread.cpp |   11 +-
 dom/system/gonk/VolumeServiceIOThread.h   |    7 +-
 dom/system/gonk/nsIVolume.idl             |   21 +++-
 dom/system/gonk/nsIVolumeMountLock.idl    |   12 +++
 dom/system/gonk/nsIVolumeService.idl      |    9 +-
 dom/system/gonk/nsVolume.cpp              |   96 ++++++++++++++++-
 dom/system/gonk/nsVolume.h                |   43 ++++++--
 dom/system/gonk/nsVolumeMountLock.cpp     |  157 +++++++++++++++++++++++++++
 dom/system/gonk/nsVolumeMountLock.h       |   55 ++++++++++
 dom/system/gonk/nsVolumeService.cpp       |  168 +++++++++++++++++++++++------
 dom/system/gonk/nsVolumeService.h         |   20 +++-
 layout/build/nsLayoutModule.cpp           |    5 +-
 layout/build/nsLayoutStatics.cpp          |    9 ++
 22 files changed, 684 insertions(+), 78 deletions(-)
 create mode 100644 dom/system/gonk/nsIVolumeMountLock.idl
 create mode 100644 dom/system/gonk/nsVolumeMountLock.cpp
 create mode 100644 dom/system/gonk/nsVolumeMountLock.h
This commit is contained in:
Dave Hylands 2012-12-14 16:01:34 -08:00
parent bb9ec533eb
commit 78d09607c8
22 changed files with 695 additions and 81 deletions

View File

@ -30,6 +30,7 @@
#include "mozilla/layers/PCompositorChild.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "nsIMemoryReporter.h"
#include "nsIMemoryInfoDumper.h"
@ -101,6 +102,7 @@
#include "AudioChannelService.h"
using namespace base;
using namespace mozilla;
using namespace mozilla::docshell;
using namespace mozilla::dom::bluetooth;
using namespace mozilla::dom::devicestorage;
@ -1083,14 +1085,24 @@ ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, con
}
bool
ContentChild::RecvFileSystemUpdate(const nsString& aFsName, const nsString& aName, const int32_t &aState)
ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
const nsString& aName,
const int32_t& aState,
const int32_t& aMountGeneration)
{
#ifdef MOZ_WIDGET_GONK
nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aName, aState);
nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aName, aState,
aMountGeneration);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
nsString stateStr(NS_ConvertUTF8toUTF16(volume->StateStr()));
obs->NotifyObservers(volume, NS_VOLUME_STATE_CHANGED, stateStr.get());
#else
// Remove warnings about unused arguments
unused << aFsName;
unused << aName;
unused << aState;
unused << aMountGeneration;
#endif
return true;
}

View File

@ -183,7 +183,10 @@ public:
virtual bool RecvLastPrivateDocShellDestroyed();
virtual bool RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& reason);
virtual bool RecvFileSystemUpdate(const nsString& aFsName, const nsString& aName, const int32_t& aState);
virtual bool RecvFileSystemUpdate(const nsString& aFsName,
const nsString& aName,
const int32_t& aState,
const int32_t& aMountGeneration);
#ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; }

View File

@ -102,6 +102,7 @@
#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#include "nsIVolumeService.h"
using namespace mozilla::system;
#endif
#ifdef MOZ_B2G_BT
@ -235,9 +236,9 @@ ContentParent::MaybeTakePreallocatedAppProcess()
/*static*/ void
ContentParent::FirstIdle(void)
{
// The parent has gone idle for the first time. This would be a good
// time to preallocate an app process.
ScheduleDelayedPreallocateAppProcess();
// The parent has gone idle for the first time. This would be a good
// time to preallocate an app process.
ScheduleDelayedPreallocateAppProcess();
}
/*static*/ void
@ -1133,6 +1134,22 @@ ContentParent::RecvAudioChannelUnregisterType(const AudioChannelType& aType)
return true;
}
bool
ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
{
#ifdef MOZ_WIDGET_GONK
nsresult rv;
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
if (vs) {
vs->BroadcastVolume(aVolumeName);
}
return true;
#else
NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
return false;
#endif
}
NS_IMPL_THREADSAFE_ISUPPORTS3(ContentParent,
nsIObserver,
nsIThreadObserver,
@ -1212,12 +1229,15 @@ ContentParent::Observe(nsISupports* aSubject,
nsString volName;
nsString mountPoint;
int32_t state;
int32_t mountGeneration;
vol->GetName(volName);
vol->GetMountPoint(mountPoint);
vol->GetState(&state);
vol->GetMountGeneration(&mountGeneration);
unused << SendFileSystemUpdate(volName, mountPoint, state);
unused << SendFileSystemUpdate(volName, mountPoint, state,
mountGeneration);
}
#endif
#ifdef ACCESSIBILITY

View File

@ -326,6 +326,8 @@ private:
virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType);
virtual bool RecvAudioChannelUnregisterType(const AudioChannelType& aType);
virtual bool RecvBroadcastVolume(const nsString& aVolumeName);
virtual void ProcessingError(Result what) MOZ_OVERRIDE;
GeckoChildProcessHost* mSubprocess;

View File

@ -327,7 +327,8 @@ child:
FilePathUpdate(nsString type, nsString filepath, nsCString reasons);
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState);
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
int32_t mountGeneration);
parent:
/**
@ -439,6 +440,9 @@ parent:
async AudioChannelRegisterType(AudioChannelType aType);
async AudioChannelUnregisterType(AudioChannelType aType);
// get nsIVolumeService to broadcast volume information
async BroadcastVolume(nsString volumeName);
both:
AsyncMessage(nsString aMessage, ClonedMessageData aData);
};

View File

@ -398,8 +398,16 @@ AutoMounter::UpdateState()
RefPtr<Volume> vol = mAutoVolume[volIndex];
Volume::STATE volState = vol->State();
LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(),
vol->MediaPresent() ? "inserted" : "missing");
if (vol->State() == nsIVolume::STATE_MOUNTED) {
LOG("UpdateState: Volume %s is %s and %s @ %s gen %d locked %d",
vol->NameStr(), vol->StateStr(),
vol->MediaPresent() ? "inserted" : "missing",
vol->MountPoint().get(), vol->MountGeneration(),
(int)vol->IsMountLocked());
} else {
LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(),
vol->MediaPresent() ? "inserted" : "missing");
}
if (!vol->MediaPresent()) {
// No media - nothing we can do
continue;
@ -409,17 +417,24 @@ AutoMounter::UpdateState()
// We're going to try to unmount and share the volumes
switch (volState) {
case nsIVolume::STATE_MOUNTED: {
if (vol->IsMountLocked()) {
// The volume is currently locked, so leave it in the mounted
// state.
DBG("UpdateState: Mounted volume %s is locked, leaving",
vol->NameStr());
break;
}
// Volume is mounted, we need to unmount before
// we can share.
DBG("UpdateState: Unmounting %s", vol->NameStr());
vol->StartUnmount(mResponseCallback);
return;
return; // UpdateState will be called again when the Unmount command completes
}
case nsIVolume::STATE_IDLE: {
// Volume is unmounted. We can go ahead and share.
DBG("UpdateState: Sharing %s", vol->NameStr());
vol->StartShare(mResponseCallback);
return;
return; // UpdateState will be called again when the Share command completes
}
default: {
// Not in a state that we can do anything about.
@ -433,14 +448,14 @@ AutoMounter::UpdateState()
// Volume is shared. We can go ahead and unshare.
DBG("UpdateState: Unsharing %s", vol->NameStr());
vol->StartUnshare(mResponseCallback);
return;
return; // UpdateState will be called again when the Unshare command completes
}
case nsIVolume::STATE_IDLE: {
// Volume is unmounted, try to mount.
DBG("UpdateState: Mounting %s", vol->NameStr());
vol->StartMount(mResponseCallback);
return;
return; // UpdateState will be called again when Mount command completes
}
default: {
// Not in a state that we can do anything about.

View File

@ -39,6 +39,7 @@ XPIDLSRCS = \
nsINetworkManager.idl \
nsIRadioInterfaceLayer.idl \
nsIVolume.idl \
nsIVolumeMountLock.idl \
nsIVolumeService.idl \
nsIVolumeStat.idl \
nsIWorkerHolder.idl \
@ -63,6 +64,7 @@ CPPSRCS += \
GonkGPSGeolocationProvider.cpp \
AudioChannelManager.cpp \
nsVolume.cpp \
nsVolumeMountLock.cpp \
nsVolumeService.cpp \
nsVolumeStat.cpp \
TimeZoneSettingObserver.cpp \
@ -75,8 +77,8 @@ CPPSRCS += \
# for our local copy of AudioSystem.h
LOCAL_INCLUDES += -I$(topsrcdir)/media/libsydneyaudio/src
EXPORTS = \
nsVolume.h \
GonkGPSGeolocationProvider.h \
nsVolume.h \
$(NULL)
endif

View File

@ -16,12 +16,48 @@ namespace system {
Volume::EventObserverList Volume::mEventObserverList;
// We have a feature where volumes can be locked when mounted. This
// is used to prevent a volume from being shared with the PC while
// it is actively being used (say for storing an update image)
//
// We use WakeLocks (a poor choice of name, but it does what we want)
// from the PowerManagerService to determine when we're locked.
// In particular we'll create a wakelock called volume-NAME-GENERATION
// (where NAME is the volume name, and GENERATION is its generation
// number), and if this wakelock is locked, then we'll prevent a volume
// from being shared.
//
// Implementation Details:
//
// Since the AutoMounter can only control when something gets mounted
// and not when it gets unmounted (for example: a user pulls the SDCard)
// and because Volume and nsVolume data structures are maintained on
// separate threads, we have the potential for some race conditions.
// We eliminate the race conditions by introducing the concept of a
// generation number. Every time a volume transitions to the Mounted
// state, it gets assigned a new generation number. Whenever the state
// of a Volume changes, we send the updated state and current generation
// number to the main thread where it gets updated in the nsVolume.
//
// Since WakeLocks can only be queried from the main-thread, the
// nsVolumeService looks for WakeLock status changes, and forwards
// the results to the IOThread.
//
// If the Volume (IOThread) recieves a volume update where the generation
// number mismatches, then the update is simply ignored.
//
// When a Volume (IOThread) initially becomes mounted, we assume it to
// be locked until we get our first update from nsVolume (MainThread).
static int32_t sMountGeneration = 0;
// 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(nsIVolume::STATE_INIT),
mName(aName)
mName(aName),
mMountGeneration(-1),
mMountLocked(true) // Needs to agree with nsVolume::nsVolume
{
DBG("Volume %s: created", NameStr());
}
@ -68,9 +104,12 @@ Volume::SetState(Volume::STATE aNewState)
return;
}
if (aNewState == nsIVolume::STATE_MOUNTED) {
LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers)",
mMountGeneration = ++sMountGeneration;
LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) "
"mountGeneration = %d, locked = %d",
NameStr(), StateStr(mState),
StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length());
StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length(),
mMountGeneration, (int)mMountLocked);
} else {
LOG("Volume %s: changing state from %s to %s (%d observers)",
NameStr(), StateStr(mState),
@ -149,6 +188,23 @@ Volume::UnregisterObserver(Volume::EventObserver *aObserver)
mEventObserverList.RemoveObserver(aObserver);
}
//static
void
Volume::UpdateMountLock(const nsACString &aVolumeName,
const int32_t &aMountGeneration,
const bool &aMountLocked)
{
RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
if (!vol || (vol->mMountGeneration != aMountGeneration)) {
return;
}
if (vol->mMountLocked != aMountLocked) {
vol->mMountLocked = aMountLocked;
DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
mEventObserverList.Broadcast(vol);
}
}
void
Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer &aTokenizer)
{

View File

@ -42,7 +42,9 @@ public:
// (i.e. path that leads to the files stored on the volume).
const nsCString &MountPoint() const { return mMountPoint; }
bool MediaPresent() const { return mMediaPresent; }
int32_t MountGeneration() const { return mMountGeneration; }
bool IsMountLocked() const { return mMountLocked; }
bool MediaPresent() const { return mMediaPresent; }
typedef mozilla::Observer<Volume *> EventObserver;
typedef mozilla::ObserverList<Volume *> EventObserverList;
@ -53,6 +55,7 @@ public:
private:
friend class AutoMounter; // Calls StartXxx
friend class nsVolume; // Calls UpdateMountLock
friend class VolumeManager; // Calls HandleVoldResponse
friend class VolumeListCallback; // Calls SetMountPoint, SetState
@ -71,10 +74,16 @@ private:
void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer &aTokenizer);
static void UpdateMountLock(const nsACString &aVolumeName,
const int32_t &aMountGeneration,
const bool &aMountLocked);
bool mMediaPresent;
STATE mState;
const nsCString mName;
nsCString mMountPoint;
int32_t mMountGeneration;
bool mMountLocked;
static EventObserverList mEventObserverList;
};

View File

@ -12,7 +12,8 @@
namespace mozilla {
namespace system {
VolumeServiceIOThread::VolumeServiceIOThread()
VolumeServiceIOThread::VolumeServiceIOThread(nsVolumeService *aVolumeService)
: mVolumeService(aVolumeService)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
@ -35,7 +36,7 @@ VolumeServiceIOThread::Notify(Volume * const &aVolume)
if (VolumeManager::State() != VolumeManager::VOLUMES_READY) {
return;
}
nsVolumeService::UpdateVolumeIOThread(aVolume);
mVolumeService->UpdateVolumeIOThread(aVolume);
}
void
@ -57,17 +58,17 @@ VolumeServiceIOThread::UpdateAllVolumes()
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
nsVolumeService::UpdateVolumeIOThread(vol);
mVolumeService->UpdateVolumeIOThread(vol);
}
}
static RefPtr<VolumeServiceIOThread> sVolumeServiceIOThread;
static StaticRefPtr<VolumeServiceIOThread> sVolumeServiceIOThread;
void
InitVolumeServiceIOThread()
InitVolumeServiceIOThread(nsVolumeService * const &aVolumeService)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sVolumeServiceIOThread = new VolumeServiceIOThread();
sVolumeServiceIOThread = new VolumeServiceIOThread(aVolumeService);
}
void

View File

@ -12,6 +12,8 @@
namespace mozilla {
namespace system {
class nsVolumeService;
/***************************************************************************
* The nsVolumeServiceIOThread is a companion class to the nsVolumeService
* class, but whose methods are called from IOThread.
@ -21,7 +23,7 @@ class VolumeServiceIOThread : public VolumeManager::StateObserver,
public RefCounted<VolumeServiceIOThread>
{
public:
VolumeServiceIOThread();
VolumeServiceIOThread(nsVolumeService *aVolumeService);
~VolumeServiceIOThread();
private:
@ -30,9 +32,10 @@ private:
virtual void Notify(const VolumeManager::StateChangedEvent &aEvent);
virtual void Notify(Volume * const &aVolume);
RefPtr<nsVolumeService> mVolumeService;
};
void InitVolumeServiceIOThread();
void InitVolumeServiceIOThread(nsVolumeService * const &aVolumeService);
void ShutdownVolumeServiceIOThread();
} // system

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
#include "nsIVolumeStat.idl"
[scriptable, uuid(3c9cae8d-9da2-4aa1-b8bf-4db8c8620808)]
[scriptable, uuid(1134f267-7b81-42f2-b64a-6edb91286576)]
interface nsIVolume : nsISupports
{
// These MUST match the states from android's system/vold/Volume.h header
@ -20,12 +20,31 @@ interface nsIVolume : nsISupports
const long STATE_SHARED = 7;
const long STATE_SHAREDMNT = 8;
// The name of the volume. Often there is only one volume, called sdcard.
// But some phones support multiple volumes.
readonly attribute DOMString name;
// The mount point is the path on the system where the volume is mounted
// and is only valid when state == STATE_MOUNTED.
readonly attribute DOMString mountPoint;
// Reflects the current state of the volume, using STATE_xxx constants
// from above.
readonly attribute long state;
// mountGeneration is a unique number which is used distinguish between
// periods of time that a volume is in the mounted state. Each time a
// volume transitions to the mounted state, the mountGeneration will
// be different from the last time it transitioned to the mounted state.
readonly attribute long mountGeneration;
// While a volume is mounted, it can be locked, preventing it from being
// shared with the PC. To lock a volume, acquire an nsIDOMMozWakeLock
// using the name of this attribute. Note that mountLockName changes
// every time the mountGeneration changes, so you'll need to reacquire
// the wakelock every time the volume becomes mounted.
readonly attribute DOMString mountLockName;
nsIVolumeStat getStats();
};

View File

@ -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(44449f34-5ca1-4aff-bce6-22c79263de24)]
interface nsIVolumeMountLock : nsISupports
{
void unlock();
};

View File

@ -4,16 +4,21 @@
#include "nsISupports.idl"
#include "nsIVolume.idl"
#include "nsIVolumeMountLock.idl"
[scriptable, uuid(b31bd379-4bd2-4189-a85b-c0927a266a85)]
[scriptable, uuid(597403c6-5ba4-4e7b-b3f4-ed3f05f775d8)]
interface nsIVolumeService : nsISupports
{
nsIVolume getVolumeByName(in DOMString volName);
nsIVolume getVolumeByPath(in DOMString path);
void BroadcastVolume(in DOMString volName);
nsIVolumeMountLock createMountLock(in DOMString volName);
};
%{C++
#define NS_VOLUMESERVICE_CID \
{0xb31bd379, 0x4bd2, 0x4189, {0xa8, 0x5b, 0xc0, 0x92, 0x7a, 0x26, 0x6a, 0x85}}
{0x597403c6, 0x5ba4, 0x4e7b, {0xb3, 0xf4, 0xed, 0x3f, 0x05, 0xf7, 0x75, 0xd8}}
#define NS_VOLUMESERVICE_CONTRACTID "@mozilla.org/telephony/volume-service;1"
%}

View File

@ -3,11 +3,20 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsVolume.h"
#include "base/message_loop.h"
#include "nsIPowerManagerService.h"
#include "nsISupportsUtils.h"
#include "nsIVolume.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsVolumeStat.h"
#include "nsXULAppAPI.h"
#include "Volume.h"
#define VOLUME_MANAGER_LOG_TAG "nsVolume"
#include "VolumeManagerLog.h"
namespace mozilla {
namespace system {
@ -34,7 +43,9 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsVolume, nsIVolume)
nsVolume::nsVolume(const Volume *aVolume)
: mName(NS_ConvertUTF8toUTF16(aVolume->Name())),
mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())),
mState(aVolume->State())
mState(aVolume->State()),
mMountGeneration(aVolume->MountGeneration()),
mMountLocked(aVolume->IsMountLocked())
{
}
@ -44,6 +55,20 @@ NS_IMETHODIMP nsVolume::GetName(nsAString &aName)
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t *aMountGeneration)
{
*aMountGeneration = mMountGeneration;
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetMountLockName(nsAString &aMountLockName)
{
aMountLockName = NS_LITERAL_STRING("volume-") + Name();
aMountLockName.AppendPrintf("-%d", mMountGeneration);
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetMountPoint(nsAString &aMountPoint)
{
aMountPoint = mMountPoint;
@ -66,5 +91,74 @@ NS_IMETHODIMP nsVolume::GetStats(nsIVolumeStat **aResult)
return NS_OK;
}
void
nsVolume::LogState() const
{
if (mState == nsIVolume::STATE_MOUNTED) {
LOG("nsVolume: %s state %s @ '%s' gen %d locked %d",
NameStr(), StateStr(), MountPointStr(),
MountGeneration(), (int)IsMountLocked());
return;
}
LOG("nsVolume: %s state %s", NameStr(), StateStr());
}
void nsVolume::Set(const nsVolume *aVolume)
{
mName = aVolume->mName;
mMountPoint = aVolume->mMountPoint;
mState = aVolume->mState;
if (mState != nsIVolume::STATE_MOUNTED) {
// Since we're not in the mounted state, we need to
// forgot whatever mount generation we may have had.
mMountGeneration = -1;
return;
}
if (mMountGeneration == aVolume->mMountGeneration) {
// No change in mount generation, nothing else to do
return;
}
mMountGeneration = aVolume->mMountGeneration;
// Notify the Volume on IOThread whether the volume is locked or not.
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
if (!pmService) {
return;
}
nsString mountLockName;
GetMountLockName(mountLockName);
nsString mountLockState;
pmService->GetWakeLockState(mountLockName, mountLockState);
UpdateMountLock(mountLockState);
}
void
nsVolume::UpdateMountLock(const nsAString &aMountLockState)
{
// There are 3 states, unlocked, locked-background, and locked-foreground
// I figured it was easier to use negtive logic and compare for unlocked.
UpdateMountLock(!aMountLockState.EqualsLiteral("unlocked"));
}
void
nsVolume::UpdateMountLock(bool aMountLocked)
{
if (aMountLocked == mMountLocked) {
return;
}
// The locked/unlocked state changed. Tell IOThread about it.
mMountLocked = aMountLocked;
LogState();
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(Volume::UpdateMountLock,
NS_LossyConvertUTF16toASCII(Name()),
MountGeneration(), aMountLocked));
}
} // system
} // mozilla

View File

@ -5,13 +5,16 @@
#ifndef mozilla_system_nsvolume_h__
#define mozilla_system_nsvolume_h__
#include "nsCOMPtr.h"
#include "nsIVolume.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace system {
class Volume;
class VolumeMountLock;
class nsVolume : public nsIVolume
{
@ -19,16 +22,27 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIVOLUME
// This constructor is used by the UpdateVolumeRunnable constructor
nsVolume(const Volume *aVolume);
nsVolume(const nsAString &aName, const nsAString &aMountPoint, const int32_t &aState)
: mName(aName), mMountPoint(aMountPoint), mState(aState)
// This constructor is used by ContentChild::RecvFileSystemUpdate
nsVolume(const nsAString &aName, const nsAString &aMountPoint,
const int32_t &aState, const int32_t &aMountGeneration)
: mName(aName),
mMountPoint(aMountPoint),
mState(aState),
mMountGeneration(aMountGeneration),
mMountLocked(false)
{
}
// This constructor is used by nsVolumeService::FindAddVolumeByName, and
// will be followed shortly by a Set call.
nsVolume(const nsAString &aName)
: mName(aName),
mState(STATE_INIT)
mState(STATE_INIT),
mMountGeneration(-1),
mMountLocked(true) // Needs to agree with Volume::Volume
{
}
@ -36,19 +50,21 @@ public:
{
return mName.Equals(aVolume->mName)
&& mMountPoint.Equals(aVolume->mMountPoint)
&& (mState == aVolume->mState);
&& (mState == aVolume->mState)
&& (mMountGeneration == aVolume->mMountGeneration)
&& (mMountLocked == aVolume->mMountLocked);
}
void Set(const nsVolume *aVolume)
{
mName = aVolume->mName;
mMountPoint = aVolume->mMountPoint;
mState = aVolume->mState;
}
void Set(const nsVolume *aVolume);
void LogState() const;
const nsString &Name() const { return mName; }
const char *NameStr() const { return NS_LossyConvertUTF16toASCII(mName).get(); }
int32_t MountGeneration() const { return mMountGeneration; }
bool IsMountLocked() const { return mMountLocked; }
const nsString &MountPoint() const { return mMountPoint; }
const char *MountPointStr() const { return NS_LossyConvertUTF16toASCII(mMountPoint).get(); }
@ -60,10 +76,15 @@ public:
private:
~nsVolume() {}
protected:
friend class nsVolumeService; // Calls the following XxxMountLock functions
void UpdateMountLock(const nsAString &aMountLockState);
void UpdateMountLock(bool aMountLocked);
nsString mName;
nsString mMountPoint;
int32_t mState;
int32_t mMountGeneration;
bool mMountLocked;
};
} // system

View File

@ -0,0 +1,157 @@
/* 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 "nsVolumeMountLock.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsIPowerManagerService.h"
#include "nsIVolume.h"
#include "nsIVolumeService.h"
#include "nsString.h"
#define VOLUME_MANAGER_LOG_TAG "nsVolumeMountLock"
#include "VolumeManagerLog.h"
using namespace mozilla::dom;
using namespace mozilla::services;
namespace mozilla {
namespace system {
NS_IMPL_ISUPPORTS3(nsVolumeMountLock, nsIVolumeMountLock,
nsIObserver, nsISupportsWeakReference)
// static
already_AddRefed<nsVolumeMountLock>
nsVolumeMountLock::Create(const nsAString &aVolumeName)
{
DBG("nsVolumeMountLock::Create called");
nsRefPtr<nsVolumeMountLock> mountLock = new nsVolumeMountLock(aVolumeName);
nsresult rv = mountLock->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
return mountLock.forget();
}
nsVolumeMountLock::nsVolumeMountLock(const nsAString &aVolumeName)
: mVolumeName(aVolumeName),
mVolumeGeneration(-1),
mUnlocked(false)
{
}
//virtual
nsVolumeMountLock::~nsVolumeMountLock()
{
Unlock();
}
nsresult nsVolumeMountLock::Init()
{
LOG("nsVolumeMountLock created for '%s'",
NS_LossyConvertUTF16toASCII(mVolumeName).get());
// Add ourselves as an Observer. It's important that we use a weak
// reference here. If we used a strong reference, then that reference
// would prevent this object from being destructed.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, true /*weak*/);
// Request the sdcard info, so we know the state/generation without having
// to wait for a state change.
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild::GetSingleton()->SendBroadcastVolume(mVolumeName);
return NS_OK;
}
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE);
vs->BroadcastVolume(mVolumeName);
return NS_OK;
}
/* void unlock (); */
NS_IMETHODIMP nsVolumeMountLock::Unlock()
{
LOG("nsVolumeMountLock released for '%s'",
NS_LossyConvertUTF16toASCII(mVolumeName).get());
mUnlocked = true;
mWakeLock = nullptr;
// While we don't really need to remove weak observers, we do so anyways
// since it will reduce the number of times Observe gets called.
nsCOMPtr<nsIObserverService> obs = GetObserverService();
obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
return NS_OK;
}
NS_IMETHODIMP nsVolumeMountLock::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
{
if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
return NS_OK;
}
if (mUnlocked) {
// We're not locked anymore, so we don't need to look at the notifications.
return NS_OK;
}
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
if (!vol) {
return NS_OK;
}
nsString volName;
vol->GetName(volName);
if (!volName.Equals(mVolumeName)) {
return NS_OK;
}
int32_t state;
nsresult rv = vol->GetState(&state);
NS_ENSURE_SUCCESS(rv, rv);
if (state != nsIVolume::STATE_MOUNTED) {
mWakeLock = nullptr;
mVolumeGeneration = -1;
return NS_OK;
}
int32_t mountGeneration;
rv = vol->GetMountGeneration(&mountGeneration);
NS_ENSURE_SUCCESS(rv, rv);
DBG("nsVolumeMountLock::Observe mountGeneration = %d mVolumeGeneration = %d",
mountGeneration, mVolumeGeneration);
if (mVolumeGeneration == mountGeneration) {
return NS_OK;
}
// The generation changed, which means that any wakelock we may have
// been holding is now invalid. Grab a new wakelock for the new generation
// number.
mWakeLock = nullptr;
mVolumeGeneration = mountGeneration;
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
NS_ENSURE_TRUE(pmService, NS_ERROR_FAILURE);
nsString mountLockName;
vol->GetMountLockName(mountLockName);
rv = pmService->NewWakeLock(mountLockName, nullptr, getter_AddRefs(mWakeLock));
NS_ENSURE_SUCCESS(rv, rv);
LOG("nsVolumeMountLock acquired for '%s' gen %d",
NS_LossyConvertUTF16toASCII(mVolumeName).get(), mVolumeGeneration);
return NS_OK;
}
} // namespace system
} // namespace mozilla

View File

@ -0,0 +1,53 @@
/* 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_nsvolumemountlock_h__
#define mozilla_system_nsvolumemountlock_h__
#include "nsIVolumeMountLock.h"
#include "nsIDOMWakeLock.h"
#include "nsIObserver.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace system {
/* The VolumeMountLock is designed so that it can be used in the Child or
* Parent process. While the VolumeMountLock object exists, then the
* VolumeManager/AutoMounter will prevent a mounted volume from being
* shared with the PC.
*/
class nsVolumeMountLock MOZ_FINAL : public nsIVolumeMountLock,
public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIVOLUMEMOUNTLOCK
static already_AddRefed<nsVolumeMountLock> Create(const nsAString &volumeName);
const nsString &VolumeName() const { return mVolumeName; }
private:
nsVolumeMountLock(const nsAString &aVolumeName);
~nsVolumeMountLock();
nsresult Init();
nsString mVolumeName;
int32_t mVolumeGeneration;
nsCOMPtr<nsIDOMMozWakeLock> mWakeLock;
bool mUnlocked;
};
} // namespace system
} // namespace mozilla
#endif // mozilla_system_nsvolumemountlock_h__

View File

@ -11,8 +11,10 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsDependentSubstring.h"
#include "nsIDOMWakeLockListener.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIPowerManagerService.h"
#include "nsISupportsUtils.h"
#include "nsIVolume.h"
#include "nsIVolumeService.h"
@ -21,7 +23,9 @@
#include "nsString.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsVolumeMountLock.h"
#include "nsXULAppAPI.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Services.h"
#define VOLUME_MANAGER_LOG_TAG "nsVolumeService"
@ -29,30 +33,106 @@
#include <stdlib.h>
using namespace mozilla::dom;
using namespace mozilla::services;
namespace mozilla {
namespace system {
NS_IMPL_ISUPPORTS1(nsVolumeService, nsIVolumeService)
NS_IMPL_ISUPPORTS2(nsVolumeService, nsIVolumeService, nsIDOMMozWakeLockListener)
StaticRefPtr<nsVolumeService> nsVolumeService::sSingleton;
// static
already_AddRefed<nsVolumeService>
nsVolumeService::GetSingleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
sSingleton = new nsVolumeService();
}
NS_ADDREF(sSingleton.get());
return sSingleton.get();
}
// static
void
nsVolumeService::Shutdown()
{
if (!sSingleton || (XRE_GetProcessType() != GeckoProcessType_Default)) {
return;
}
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
if (pmService) {
pmService->RemoveWakeLockListener(sSingleton.get());
}
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(ShutdownVolumeServiceIOThread));
sSingleton = nullptr;
}
nsVolumeService::nsVolumeService()
{
sSingleton = this;
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// We don't support the nsIVolumeService in the child processes,
// but we get constructed due to the way we're registered with
// nsLayoutModule.cpp. So we exit early to reduce our memory
// impact, and so that we don't start unnecessary IOThread stuff.
return;
}
// 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));
NewRunnableFunction(InitVolumeServiceIOThread, this));
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
if (!pmService) {
return;
}
pmService->AddWakeLockListener(this);
}
nsVolumeService::~nsVolumeService()
{
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(ShutdownVolumeServiceIOThread));
}
/* nsIVolume getVolumeByName (in DOMString volName); */
// Callback for nsIDOMMozWakeLockListener
NS_IMETHODIMP nsVolumeService::Callback(const nsAString &aTopic, const nsAString &aState)
{
CheckMountLock(aTopic, aState);
return NS_OK;
}
NS_IMETHODIMP nsVolumeService::BroadcastVolume(const nsAString &aVolName)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
if (!vol) {
ERR("BroadcastVolume: Unable to locate volume '%s'",
NS_LossyConvertUTF16toASCII(aVolName).get());
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIObserverService> obs = GetObserverService();
NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr());
nsString stateStr(NS_ConvertUTF8toUTF16(vol->StateStr()));
obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
return NS_OK;
}
NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString &aVolName, nsIVolume **aResult)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -67,7 +147,6 @@ NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString &aVolName, nsIVol
return NS_OK;
}
/* nsIVolume getVolumeByPath (in DOMString path); */
NS_IMETHODIMP nsVolumeService::GetVolumeByPath(const nsAString &aPath, nsIVolume **aResult)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -105,11 +184,41 @@ NS_IMETHODIMP nsVolumeService::GetVolumeByPath(const nsAString &aPath, nsIVolume
// In order to support queries by DeviceStorage and the updater, we will fabricate
// a volume from the pathname, so that the caller can determine the volume size
nsRefPtr<nsVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
aPath, nsIVolume::STATE_MOUNTED);
aPath, nsIVolume::STATE_MOUNTED,
-1 /*generation*/);
NS_ADDREF(*aResult = vol);
return NS_OK;
}
NS_IMETHODIMP nsVolumeService::CreateMountLock(const nsAString &aVolumeName, nsIVolumeMountLock **aResult)
{
nsRefPtr<nsVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
if (!mountLock) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ADDREF(*aResult = mountLock);
return NS_OK;
}
void nsVolumeService::CheckMountLock(const nsAString &aMountLockName,
const nsAString &aMountLockState)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
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];
nsString mountLockName;
vol->GetMountLockName(mountLockName);
if (mountLockName.Equals(aMountLockName)) {
vol->UpdateMountLock(aMountLockState);
return;
}
}
}
already_AddRefed<nsVolume> nsVolumeService::FindVolumeByName(const nsAString &aName)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -126,6 +235,7 @@ already_AddRefed<nsVolume> nsVolumeService::FindVolumeByName(const nsAString &aN
return NULL;
}
//static
already_AddRefed<nsVolume> nsVolumeService::FindAddVolumeByName(const nsAString &aName)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -157,13 +267,6 @@ void nsVolumeService::UpdateVolume(const nsVolume *aVolume)
if (!obs) {
return;
}
if (aVolume->State() == nsIVolume::STATE_MOUNTED) {
LOG("UpdateVolume: '%s' state %s @ '%s'",
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPointStr());
} else {
LOG("UpdateVolume: '%s' state %s",
aVolume->NameStr(), aVolume->StateStr());
}
nsString stateStr(NS_ConvertUTF8toUTF16(vol->StateStr()));
obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
}
@ -175,8 +278,9 @@ void nsVolumeService::UpdateVolume(const nsVolume *aVolume)
class UpdateVolumeRunnable : public nsRunnable
{
public:
UpdateVolumeRunnable(const Volume *aVolume)
: mVolume(new nsVolume(aVolume))
UpdateVolumeRunnable(nsVolumeService *aVolumeService, const Volume *aVolume)
: mVolumeService(aVolumeService),
mVolume(new nsVolume(aVolume))
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
}
@ -184,34 +288,29 @@ public:
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
DBG("UpdateVolumeRunnable::Run '%s' state %s",
mVolume->NameStr(), mVolume->StateStr());
DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d",
mVolume->NameStr(), mVolume->StateStr(), mVolume->MountGeneration(),
(int)mVolume->IsMountLocked());
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);
mVolumeService->UpdateVolume(mVolume);
mVolumeService = NULL;
mVolume = NULL;
return NS_OK;
}
private:
nsRefPtr<nsVolume> mVolume;
nsRefPtr<nsVolumeService> mVolumeService;
nsRefPtr<nsVolume> mVolume;
};
//static
void nsVolumeService::UpdateVolumeIOThread(const Volume *aVolume)
{
DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s'",
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get());
DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d",
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
aVolume->MountGeneration(), (int)aVolume->IsMountLocked());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
NS_DispatchToMainThread(new UpdateVolumeRunnable(aVolume));
NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
}
} // system
} // mozilla
} // namespace system
} // namespace mozilla

View File

@ -5,7 +5,10 @@
#ifndef mozilla_system_nsvolumeservice_h__
#define mozilla_system_nsvolumeservice_h__
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsIDOMWakeLockListener.h"
#include "nsIVolume.h"
#include "nsIVolumeService.h"
#include "nsVolume.h"
@ -14,29 +17,41 @@
namespace mozilla {
namespace system {
class WakeLockCallback;
/***************************************************************************
* 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
class nsVolumeService MOZ_FINAL : public nsIVolumeService,
public nsIDOMMozWakeLockListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIVOLUMESERVICE
NS_DECL_NSIDOMMOZWAKELOCKLISTENER
nsVolumeService();
static already_AddRefed<nsVolumeService> GetSingleton();
//static nsVolumeService *GetSingleton();
static void Shutdown();
void CheckMountLock(const nsAString &aMountLockName,
const nsAString &aMountLockState);
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);
void UpdateVolumeIOThread(const Volume *aVolume);
private:
~nsVolumeService();
nsVolume::Array mVolumeArray;
nsVolume::Array mVolumeArray;
static StaticRefPtr<nsVolumeService> sSingleton;
};
} // system

View File

@ -296,7 +296,6 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(BluetoothService,
#ifdef MOZ_WIDGET_GONK
NS_GENERIC_FACTORY_CONSTRUCTOR(AudioManager)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsVolumeService)
#endif
#ifdef MOZ_B2G_FM
@ -325,6 +324,10 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITimeService,
#ifdef MOZ_WIDGET_GONK
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIGeolocationProvider,
GonkGPSGeolocationProvider::GetSingleton)
// Since the nsVolumeService constructor calls into nsIPowerManagerService,
// we need it to be constructed sometime after nsIPowerManagerService.
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsVolumeService,
nsVolumeService::GetSingleton)
#endif
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMediaManagerService,
MediaManager::GetInstance)

View File

@ -87,6 +87,11 @@
#include "AudioStream.h"
#endif
#ifdef MOZ_WIDGET_GONK
#include "nsVolumeService.h"
using namespace mozilla::system;
#endif
#include "nsError.h"
#include "nsCycleCollector.h"
@ -349,6 +354,10 @@ nsLayoutStatics::Shutdown()
WMFDecoder::UnloadDLLs();
#endif
#ifdef MOZ_WIDGET_GONK
nsVolumeService::Shutdown();
#endif
nsCORSListenerProxy::Shutdown();
nsIPresShell::ReleaseStatics();