Bug 878310 - Improve DeviceStorage volume status reporting. r=qDot

This changeset causes tranistory states when changing from mounted to shared,
and back to mounted to be reported as shared instead of unavailable.

We also don't send the same status twice in a row.
This commit is contained in:
Dave Hylands 2013-08-22 16:12:25 -07:00
parent 6c7632cac4
commit 0ffd2e95b8
13 changed files with 153 additions and 27 deletions

View File

@ -300,6 +300,7 @@ private:
static mozilla::StaticRefPtr<VolumeNameCache> sVolumeNameCache; static mozilla::StaticRefPtr<VolumeNameCache> sVolumeNameCache;
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
nsString mLastStatus;
void DispatchMountChangeEvent(nsAString& aVolumeStatus); void DispatchMountChangeEvent(nsAString& aVolumeStatus);
#endif #endif

View File

@ -1261,14 +1261,27 @@ DeviceStorageFile::GetStatus(nsAString& aStatus)
nsCOMPtr<nsIVolume> vol; nsCOMPtr<nsIVolume> vol;
nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
NS_ENSURE_SUCCESS_VOID(rv); NS_ENSURE_SUCCESS_VOID(rv);
if (!vol) {
return;
}
bool isMediaPresent;
rv = vol->GetIsMediaPresent(&isMediaPresent);
NS_ENSURE_SUCCESS_VOID(rv);
if (!isMediaPresent) {
return;
}
bool isSharing;
rv = vol->GetIsSharing(&isSharing);
NS_ENSURE_SUCCESS_VOID(rv);
if (isSharing) {
aStatus.AssignLiteral("shared");
return;
}
int32_t volState; int32_t volState;
rv = vol->GetState(&volState); rv = vol->GetState(&volState);
NS_ENSURE_SUCCESS_VOID(rv); NS_ENSURE_SUCCESS_VOID(rv);
if (volState == nsIVolume::STATE_MOUNTED) { if (volState == nsIVolume::STATE_MOUNTED) {
aStatus.AssignLiteral("available"); aStatus.AssignLiteral("available");
} else if (volState == nsIVolume::STATE_SHARED ||
volState == nsIVolume::STATE_SHAREDMNT) {
aStatus.AssignLiteral("shared");
} }
#endif #endif
} }
@ -3159,6 +3172,12 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
void void
nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeStatus) nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeStatus)
{ {
if (aVolumeStatus == mLastStatus) {
// We've already sent this status, don't bother sending it again.
return;
}
mLastStatus = aVolumeStatus;
nsCOMPtr<nsIDOMEvent> event; nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this, NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this,
nullptr, nullptr); nullptr, nullptr);

View File

@ -1248,11 +1248,14 @@ bool
ContentChild::RecvFileSystemUpdate(const nsString& aFsName, ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
const nsString& aVolumeName, const nsString& aVolumeName,
const int32_t& aState, const int32_t& aState,
const int32_t& aMountGeneration) const int32_t& aMountGeneration,
const bool& aIsMediaPresent,
const bool& aIsSharing)
{ {
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState, nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState,
aMountGeneration); aMountGeneration, aIsMediaPresent,
aIsSharing);
nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
if (vs) { if (vs) {
@ -1264,6 +1267,8 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
unused << aVolumeName; unused << aVolumeName;
unused << aState; unused << aState;
unused << aMountGeneration; unused << aMountGeneration;
unused << aIsMediaPresent;
unused << aIsSharing;
#endif #endif
return true; return true;
} }

View File

@ -208,7 +208,9 @@ public:
virtual bool RecvFileSystemUpdate(const nsString& aFsName, virtual bool RecvFileSystemUpdate(const nsString& aFsName,
const nsString& aVolumeName, const nsString& aVolumeName,
const int32_t& aState, const int32_t& aState,
const int32_t& aMountGeneration); const int32_t& aMountGeneration,
const bool& aIsMediaPresent,
const bool& aIsSharing);
virtual bool RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority); virtual bool RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority);
virtual bool RecvMinimizeMemoryUsage(); virtual bool RecvMinimizeMemoryUsage();

View File

@ -1676,14 +1676,19 @@ ContentParent::Observe(nsISupports* aSubject,
nsString mountPoint; nsString mountPoint;
int32_t state; int32_t state;
int32_t mountGeneration; int32_t mountGeneration;
bool isMediaPresent;
bool isSharing;
vol->GetName(volName); vol->GetName(volName);
vol->GetMountPoint(mountPoint); vol->GetMountPoint(mountPoint);
vol->GetState(&state); vol->GetState(&state);
vol->GetMountGeneration(&mountGeneration); vol->GetMountGeneration(&mountGeneration);
vol->GetIsMediaPresent(&isMediaPresent);
vol->GetIsSharing(&isSharing);
unused << SendFileSystemUpdate(volName, mountPoint, state, unused << SendFileSystemUpdate(volName, mountPoint, state,
mountGeneration); mountGeneration, isMediaPresent,
isSharing);
} }
#endif #endif
#ifdef ACCESSIBILITY #ifdef ACCESSIBILITY

View File

@ -269,7 +269,8 @@ child:
nsCString reasons); nsCString reasons);
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState, FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
int32_t mountGeneration); int32_t mountGeneration, bool isMediaPresent,
bool isSharing);
NotifyProcessPriorityChanged(ProcessPriority priority); NotifyProcessPriorityChanged(ProcessPriority priority);
MinimizeMemoryUsage(); MinimizeMemoryUsage();

View File

@ -443,6 +443,7 @@ AutoMounter::UpdateState()
// Volume is mounted, we need to unmount before // Volume is mounted, we need to unmount before
// we can share. // we can share.
LOG("UpdateState: Unmounting %s", vol->NameStr()); LOG("UpdateState: Unmounting %s", vol->NameStr());
vol->SetIsSharing(true);
vol->StartUnmount(mResponseCallback); vol->StartUnmount(mResponseCallback);
return; // UpdateState will be called again when the Unmount command completes return; // UpdateState will be called again when the Unmount command completes
} }

View File

@ -59,11 +59,18 @@ Volume::Volume(const nsCSubstring& aName)
mMountGeneration(-1), mMountGeneration(-1),
mMountLocked(true), // Needs to agree with nsVolume::nsVolume mMountLocked(true), // Needs to agree with nsVolume::nsVolume
mSharingEnabled(false), mSharingEnabled(false),
mCanBeShared(true) mCanBeShared(true),
mIsSharing(false)
{ {
DBG("Volume %s: created", NameStr()); DBG("Volume %s: created", NameStr());
} }
void
Volume::SetIsSharing(bool aIsSharing)
{
mIsSharing = aIsSharing;
}
void void
Volume::SetMediaPresent(bool aMediaPresent) Volume::SetMediaPresent(bool aMediaPresent)
{ {
@ -133,9 +140,29 @@ Volume::SetState(Volume::STATE aNewState)
StateStr(aNewState), mEventObserverList.Length()); StateStr(aNewState), mEventObserverList.Length());
} }
if (aNewState == nsIVolume::STATE_NOMEDIA) { switch (aNewState) {
// Cover the startup case where we don't get insertion/removal events case nsIVolume::STATE_NOMEDIA:
mMediaPresent = false; // Cover the startup case where we don't get insertion/removal events
mMediaPresent = false;
mIsSharing = false;
break;
case nsIVolume::STATE_MOUNTED:
case nsIVolume::STATE_FORMATTING:
mIsSharing = false;
break;
case nsIVolume::STATE_SHARED:
case nsIVolume::STATE_SHAREDMNT:
// Covers startup cases. Normally, mIsSharing would be set to true
// when we issue the command to initiate the sharing process, but
// it's conceivable that a volume could already be in a shared state
// when b2g starts.
mIsSharing = true;
break;
default:
break;
} }
mState = aNewState; mState = aNewState;
mEventObserverList.Broadcast(this); mEventObserverList.Broadcast(this);

View File

@ -47,6 +47,7 @@ public:
bool MediaPresent() const { return mMediaPresent; } bool MediaPresent() const { return mMediaPresent; }
bool CanBeShared() const { return mCanBeShared; } bool CanBeShared() const { return mCanBeShared; }
bool IsSharingEnabled() const { return mCanBeShared && mSharingEnabled; } bool IsSharingEnabled() const { return mCanBeShared && mSharingEnabled; }
bool IsSharing() const { return mIsSharing; }
void SetSharingEnabled(bool aSharingEnabled); void SetSharingEnabled(bool aSharingEnabled);
@ -71,6 +72,7 @@ private:
void StartShare(VolumeResponseCallback* aCallback); void StartShare(VolumeResponseCallback* aCallback);
void StartUnshare(VolumeResponseCallback* aCallback); void StartUnshare(VolumeResponseCallback* aCallback);
void SetIsSharing(bool aIsSharing);
void SetState(STATE aNewState); void SetState(STATE aNewState);
void SetMediaPresent(bool aMediaPresent); void SetMediaPresent(bool aMediaPresent);
void SetMountPoint(const nsCSubstring& aMountPoint); void SetMountPoint(const nsCSubstring& aMountPoint);
@ -90,6 +92,7 @@ private:
bool mMountLocked; bool mMountLocked;
bool mSharingEnabled; bool mSharingEnabled;
bool mCanBeShared; bool mCanBeShared;
bool mIsSharing;
static EventObserverList mEventObserverList; static EventObserverList mEventObserverList;
}; };

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl" #include "nsISupports.idl"
#include "nsIVolumeStat.idl" #include "nsIVolumeStat.idl"
[scriptable, uuid(4b5bd562-bd05-4658-ab0f-f668a9e25fb5)] [scriptable, uuid(e476e7ea-5cde-4d5a-b00d-d60daad76398)]
interface nsIVolume : nsISupports interface nsIVolume : nsISupports
{ {
// These MUST match the states from android's system/vold/Volume.h header // These MUST match the states from android's system/vold/Volume.h header
@ -48,6 +48,20 @@ interface nsIVolume : nsISupports
// Determines if a mountlock is currently being held against this volume. // Determines if a mountlock is currently being held against this volume.
readonly attribute boolean isMountLocked; readonly attribute boolean isMountLocked;
// Determines if media is actually present or not. Note, that when an sdcard
// is ejected, it may go through several tranistory states before finally
// arriving at STATE_NOMEDIA. So isMediaPresent may be false even when the
// current state isn't STATE_NOMEDIA.
readonly attribute boolean isMediaPresent;
// Determines if the volume is currently being shared. This covers off
// more than just state == STATE_SHARED. isSharing will return true from the
// time that the volume leaves the mounted state, until it gets back to
// mounted, nomedia, or formatting states. This attribute is to allow
// device storage to suppress unwanted 'unavailable' status when
// transitioning from mounted to sharing and back again.
readonly attribute boolean isSharing;
nsIVolumeStat getStats(); nsIVolumeStat getStats();
// Whether this is a fake volume. // Whether this is a fake volume.

View File

@ -51,7 +51,9 @@ nsVolume::nsVolume(const Volume* aVolume)
mState(aVolume->State()), mState(aVolume->State()),
mMountGeneration(aVolume->MountGeneration()), mMountGeneration(aVolume->MountGeneration()),
mMountLocked(aVolume->IsMountLocked()), mMountLocked(aVolume->IsMountLocked()),
mIsFake(false) mIsFake(false),
mIsMediaPresent(aVolume->MediaPresent()),
mIsSharing(aVolume->IsSharing())
{ {
} }
@ -96,12 +98,24 @@ bool nsVolume::Equals(nsIVolume* aVolume)
return true; return true;
} }
NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent)
{
*aIsMediaPresent = mIsMediaPresent;
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked) NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked)
{ {
*aIsMountLocked = mMountLocked; *aIsMountLocked = mMountLocked;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing)
{
*aIsSharing = mIsSharing;
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetName(nsAString& aName) NS_IMETHODIMP nsVolume::GetName(nsAString& aName)
{ {
aName = mName; aName = mName;
@ -154,9 +168,11 @@ void
nsVolume::LogState() const nsVolume::LogState() const
{ {
if (mState == nsIVolume::STATE_MOUNTED) { if (mState == nsIVolume::STATE_MOUNTED) {
LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d", LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d "
"media %d sharing %d",
NameStr().get(), StateStr(), MountPointStr().get(), NameStr().get(), StateStr(), MountPointStr().get(),
MountGeneration(), (int)IsMountLocked(), (int)IsFake()); MountGeneration(), (int)IsMountLocked(), (int)IsFake(),
(int)IsMediaPresent(), (int)IsSharing());
return; return;
} }
@ -171,6 +187,8 @@ void nsVolume::Set(nsIVolume* aVolume)
aVolume->GetMountPoint(mMountPoint); aVolume->GetMountPoint(mMountPoint);
aVolume->GetState(&mState); aVolume->GetState(&mState);
aVolume->GetIsFake(&mIsFake); aVolume->GetIsFake(&mIsFake);
aVolume->GetIsMediaPresent(&mIsMediaPresent);
aVolume->GetIsSharing(&mIsSharing);
int32_t volMountGeneration; int32_t volMountGeneration;
aVolume->GetMountGeneration(&volMountGeneration); aVolume->GetMountGeneration(&volMountGeneration);
@ -237,6 +255,17 @@ nsVolume::UpdateMountLock(bool aMountLocked)
MountGeneration(), aMountLocked)); MountGeneration(), aMountLocked));
} }
void
nsVolume::SetIsFake(bool aIsFake)
{
mIsFake = aIsFake;
if (mIsFake) {
// The media is always present for fake volumes.
mIsMediaPresent = true;
MOZ_ASSERT(!mIsSharing);
}
}
void void
nsVolume::SetState(int32_t aState) nsVolume::SetState(int32_t aState)
{ {

View File

@ -25,15 +25,19 @@ public:
// This constructor is used by the UpdateVolumeRunnable constructor // This constructor is used by the UpdateVolumeRunnable constructor
nsVolume(const Volume* aVolume); nsVolume(const Volume* aVolume);
// This constructor is used by ContentChild::RecvFileSystemUpdate // This constructor is used by ContentChild::RecvFileSystemUpdate which is
// used to update the volume cache maintained in the child process.
nsVolume(const nsAString& aName, const nsAString& aMountPoint, nsVolume(const nsAString& aName, const nsAString& aMountPoint,
const int32_t& aState, const int32_t& aMountGeneration) const int32_t& aState, const int32_t& aMountGeneration,
const bool& aIsMediaPresent, const bool& aIsSharing)
: mName(aName), : mName(aName),
mMountPoint(aMountPoint), mMountPoint(aMountPoint),
mState(aState), mState(aState),
mMountGeneration(aMountGeneration), mMountGeneration(aMountGeneration),
mMountLocked(false), mMountLocked(false),
mIsFake(false) mIsFake(false),
mIsMediaPresent(aIsMediaPresent),
mIsSharing(aIsSharing)
{ {
} }
@ -44,7 +48,9 @@ public:
mState(STATE_INIT), mState(STATE_INIT),
mMountGeneration(-1), mMountGeneration(-1),
mMountLocked(true), // Needs to agree with Volume::Volume mMountLocked(true), // Needs to agree with Volume::Volume
mIsFake(false) mIsFake(false),
mIsMediaPresent(false),
mIsSharing(false)
{ {
} }
@ -75,7 +81,9 @@ private:
void UpdateMountLock(bool aMountLocked); void UpdateMountLock(bool aMountLocked);
bool IsFake() const { return mIsFake; } bool IsFake() const { return mIsFake; }
void SetIsFake(bool aIsFake) { mIsFake = aIsFake; } bool IsMediaPresent() const { return mIsMediaPresent; }
bool IsSharing() const { return mIsSharing; }
void SetIsFake(bool aIsFake);
void SetState(int32_t aState); void SetState(int32_t aState);
nsString mName; nsString mName;
@ -84,6 +92,8 @@ private:
int32_t mMountGeneration; int32_t mMountGeneration;
bool mMountLocked; bool mMountLocked;
bool mIsFake; bool mIsFake;
bool mIsMediaPresent;
bool mIsSharing;
}; };
} // system } // system

View File

@ -246,7 +246,9 @@ nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aRe
// from the pathname, so that the caller can determine the volume size. // from the pathname, so that the caller can determine the volume size.
nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"), nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
aPath, nsIVolume::STATE_MOUNTED, aPath, nsIVolume::STATE_MOUNTED,
-1 /*generation*/); -1 /* generation */,
true /* isMediaPresent*/,
false /* isSharing */);
vol.forget(aResult); vol.forget(aResult);
return NS_OK; return NS_OK;
} }
@ -375,7 +377,10 @@ NS_IMETHODIMP
nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path) nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
{ {
if (XRE_GetProcessType() == GeckoProcessType_Default) { if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT, -1); nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT,
-1 /* mountGeneration */,
true /* isMediaPresent */,
false /* isSharing */);
vol->SetIsFake(true); vol->SetIsFake(true);
vol->LogState(); vol->LogState();
UpdateVolume(vol.get()); UpdateVolume(vol.get());
@ -425,9 +430,11 @@ public:
NS_IMETHOD Run() NS_IMETHOD Run()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d", DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
"media %d sharing %d",
mVolume->NameStr().get(), mVolume->StateStr(), mVolume->NameStr().get(), mVolume->StateStr(),
mVolume->MountGeneration(), (int)mVolume->IsMountLocked()); mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
(int)mVolume->IsMediaPresent(), mVolume->IsSharing());
mVolumeService->UpdateVolume(mVolume); mVolumeService->UpdateVolume(mVolume);
mVolumeService = nullptr; mVolumeService = nullptr;
@ -443,9 +450,11 @@ private:
void void
nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume) nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
{ {
DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d", DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
"media %d sharing %d",
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(), aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
aVolume->MountGeneration(), (int)aVolume->IsMountLocked()); aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
(int)aVolume->IsMediaPresent(), (int)aVolume->IsSharing());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume)); NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
} }