Bug 1033952 - Implement "Removable" and "HotSwappable" APIs for device storage. r=dhylands, r=bzbarsky

This commit is contained in:
CHUANG CHENYU 2014-12-22 10:33:33 +08:00
parent 2c080f980c
commit 0aa1eafc0c
15 changed files with 282 additions and 30 deletions

View File

@ -272,6 +272,7 @@ public:
bool CanBeMounted();
bool CanBeFormatted();
bool CanBeShared();
bool IsRemovable();
bool Default();
// Uses XPCOM GetStorageName
@ -322,6 +323,7 @@ private:
nsCOMPtr<nsIFile> mRootDirectory;
nsString mStorageName;
bool mIsShareable;
bool mIsRemovable;
already_AddRefed<nsDOMDeviceStorage> GetStorage(const nsAString& aFullPath,
nsAString& aOutStoragePath);

View File

@ -3337,6 +3337,7 @@ NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
, mIsShareable(false)
, mIsRemovable(false)
, mIsWatchingFile(false)
, mAllowedToWatchFile(false)
{
@ -3383,6 +3384,12 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
return rv;
}
mIsShareable = !isFake;
bool isRemovable;
rv = vol->GetIsHotSwappable(&isRemovable);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mIsRemovable = isRemovable;
}
#endif
}
@ -4226,6 +4233,12 @@ nsDOMDeviceStorage::CanBeShared()
return mIsShareable;
}
bool
nsDOMDeviceStorage::IsRemovable()
{
return mIsRemovable;
}
already_AddRefed<Promise>
nsDOMDeviceStorage::GetRoot(ErrorResult& aRv)
{

View File

@ -2153,13 +2153,15 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
const bool& aIsSharing,
const bool& aIsFormatting,
const bool& aIsFake,
const bool& aIsUnmounting)
const bool& aIsUnmounting,
const bool& aIsRemovable,
const bool& aIsHotSwappable)
{
#ifdef MOZ_WIDGET_GONK
nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState,
aMountGeneration, aIsMediaPresent,
aIsSharing, aIsFormatting, aIsFake,
aIsUnmounting);
aIsUnmounting, aIsRemovable, aIsHotSwappable);
nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
if (vs) {
@ -2176,6 +2178,8 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
unused << aIsFormatting;
unused << aIsFake;
unused << aIsUnmounting;
unused << aIsRemovable;
unused << aIsHotSwappable;
#endif
return true;
}

View File

@ -336,7 +336,9 @@ public:
const bool& aIsSharing,
const bool& aIsFormatting,
const bool& aIsFake,
const bool& aIsUnmounting) MOZ_OVERRIDE;
const bool& aIsUnmounting,
const bool& aIsRemovable,
const bool& aIsHotSwappable) MOZ_OVERRIDE;
virtual bool RecvNuwaFork() MOZ_OVERRIDE;

View File

@ -2829,6 +2829,8 @@ ContentParent::Observe(nsISupports* aSubject,
bool isFormatting;
bool isFake;
bool isUnmounting;
bool isRemovable;
bool isHotSwappable;
vol->GetName(volName);
vol->GetMountPoint(mountPoint);
@ -2839,6 +2841,8 @@ ContentParent::Observe(nsISupports* aSubject,
vol->GetIsFormatting(&isFormatting);
vol->GetIsFake(&isFake);
vol->GetIsUnmounting(&isUnmounting);
vol->GetIsRemovable(&isRemovable);
vol->GetIsHotSwappable(&isHotSwappable);
#ifdef MOZ_NUWA_PROCESS
if (!(IsNuwaReady() && IsNuwaProcess()))
@ -2847,7 +2851,7 @@ ContentParent::Observe(nsISupports* aSubject,
unused << SendFileSystemUpdate(volName, mountPoint, state,
mountGeneration, isMediaPresent,
isSharing, isFormatting, isFake,
isUnmounting);
isUnmounting, isRemovable, isHotSwappable);
}
} else if (!strcmp(aTopic, "phone-state-changed")) {
nsString state(aData);

View File

@ -319,6 +319,8 @@ struct VolumeInfo {
bool isFormatting;
bool isFake;
bool isUnmounting;
bool isRemovable;
bool isHotSwappable;
};
struct ClipboardCapabilities {
@ -489,7 +491,8 @@ child:
// VolumeInfo above.
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
int32_t mountGeneration, bool isMediaPresent,
bool isSharing, bool isFormatting, bool isFake, bool isUnmounting);
bool isSharing, bool isFormatting, bool isFake,
bool isUnmounting, bool isRemovable, bool isHotSwappable);
// Ask the Nuwa process to create a new child process.
NuwaFork();

View File

@ -81,6 +81,8 @@ Volume::Volume(const nsCSubstring& aName)
mIsSharing(false),
mIsFormatting(false),
mIsUnmounting(false),
mIsRemovable(false),
mIsHotSwappable(false),
mId(sNextId++)
{
DBG("Volume %s: created", NameStr());
@ -156,6 +158,78 @@ Volume::SetIsUnmounting(bool aIsUnmounting)
sEventObserverList.Broadcast(this);
}
void
Volume::SetIsRemovable(bool aIsRemovable)
{
if (aIsRemovable == mIsRemovable) {
return;
}
mIsRemovable = aIsRemovable;
if (!mIsRemovable) {
mIsHotSwappable = false;
}
LOG("Volume %s: IsRemovable set to %d state %s",
NameStr(), (int)mIsRemovable, StateStr(mState));
sEventObserverList.Broadcast(this);
}
void
Volume::SetIsHotSwappable(bool aIsHotSwappable)
{
if (aIsHotSwappable == mIsHotSwappable) {
return;
}
mIsHotSwappable = aIsHotSwappable;
if (mIsHotSwappable) {
mIsRemovable = true;
}
LOG("Volume %s: IsHotSwappable set to %d state %s",
NameStr(), (int)mIsHotSwappable, StateStr(mState));
sEventObserverList.Broadcast(this);
}
bool
Volume::BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue)
{
if (aConfigValue.EqualsLiteral("1") ||
aConfigValue.LowerCaseEqualsLiteral("true")) {
aBoolValue = true;
return true;
}
if (aConfigValue.EqualsLiteral("0") ||
aConfigValue.LowerCaseEqualsLiteral("false")) {
aBoolValue = false;
return true;
}
return false;
}
void
Volume::SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue)
{
if (aConfigName.LowerCaseEqualsLiteral("removable")) {
bool value = false;
if (BoolConfigValue(aConfigValue, value)) {
SetIsRemovable(value);
} else {
ERR("Volume %s: invalid value '%s' for configuration '%s'",
NameStr(), aConfigValue.get(), aConfigName.get());
}
return;
}
if (aConfigName.LowerCaseEqualsLiteral("hotswappable")) {
bool value = false;
if (BoolConfigValue(aConfigValue, value)) {
SetIsHotSwappable(value);
} else {
ERR("Volume %s: invalid value '%s' for configuration '%s'",
NameStr(), aConfigValue.get(), aConfigName.get());
}
return;
}
ERR("Volume %s: invalid config '%s'", NameStr(), aConfigName.get());
}
void
Volume::SetMediaPresent(bool aMediaPresent)
{

View File

@ -75,6 +75,8 @@ public:
bool IsSharing() const { return mIsSharing; }
bool IsFormatting() const { return mIsFormatting; }
bool IsUnmounting() const { return mIsUnmounting; }
bool IsRemovable() const { return mIsRemovable; }
bool IsHotSwappable() const { return mIsHotSwappable; }
void SetFakeVolume(const nsACString& aMountPoint);
@ -107,11 +109,16 @@ private:
void SetIsSharing(bool aIsSharing);
void SetIsFormatting(bool aIsFormatting);
void SetIsUnmounting(bool aIsUnmounting);
void SetIsRemovable(bool aIsRemovable);
void SetIsHotSwappable(bool aIsHotSwappable);
void SetState(STATE aNewState);
void SetMediaPresent(bool aMediaPresent);
void SetMountPoint(const nsCSubstring& aMountPoint);
void StartCommand(VolumeCommand* aCommand);
bool BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue);
void SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue);
void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer);
static void UpdateMountLock(const nsACString& aVolumeName,
@ -132,6 +139,8 @@ private:
bool mIsSharing;
bool mIsFormatting;
bool mIsUnmounting;
bool mIsRemovable;
bool mIsHotSwappable;
uint32_t mId; // Unique ID (used by MTP)
static VolumeObserverList sEventObserverList;

View File

@ -168,12 +168,12 @@ void VolumeManager::InitConfig()
//
// The format of the volume.cfg file is as follows:
// create volume-name mount-point
// configure volume-name preference preference-value
// Blank lines and lines starting with the hash character "#" will be ignored.
ScopedCloseFile fp;
int n = 0;
char line[255];
char *command, *volNamePtr, *mountPointPtr, *save_ptr;
const char *filename = "/system/etc/volume.cfg";
if (!(fp = fopen(filename, "r"))) {
LOG("Unable to open volume configuration file '%s' - ignoring", filename);
@ -185,27 +185,87 @@ void VolumeManager::InitConfig()
if (line[0] == '#')
continue;
if (!(command = strtok_r(line, delim, &save_ptr))) {
nsCString commandline(line);
nsCWhitespaceTokenizer tokenizer(commandline);
if (!tokenizer.hasMoreTokens()) {
// Blank line - ignore
continue;
}
if (!strcmp(command, "create")) {
if (!(volNamePtr = strtok_r(nullptr, delim, &save_ptr))) {
nsCString command(tokenizer.nextToken());
if (command.EqualsLiteral("create")) {
if (!tokenizer.hasMoreTokens()) {
ERR("No vol_name in %s line %d", filename, n);
continue;
}
if (!(mountPointPtr = strtok_r(nullptr, delim, &save_ptr))) {
ERR("No mount point for volume '%s'. %s line %d", volNamePtr, filename, n);
nsCString volName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No mount point for volume '%s'. %s line %d",
volName.get(), filename, n);
continue;
}
nsCString mountPoint(mountPointPtr);
nsCString volName(volNamePtr);
nsCString mountPoint(tokenizer.nextToken());
RefPtr<Volume> vol = FindAddVolumeByName(volName);
vol->SetFakeVolume(mountPoint);
continue;
}
else {
ERR("Unrecognized command: '%s'", command);
if (command.EqualsLiteral("configure")) {
if (!tokenizer.hasMoreTokens()) {
ERR("No vol_name in %s line %d", filename, n);
continue;
}
nsCString volName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No configuration name specified for volume '%s'. %s line %d",
volName.get(), filename, n);
continue;
}
nsCString configName(tokenizer.nextToken());
if (!tokenizer.hasMoreTokens()) {
ERR("No value for configuration name '%s'. %s line %d",
configName.get(), filename, n);
continue;
}
nsCString configValue(tokenizer.nextToken());
RefPtr<Volume> vol = FindVolumeByName(volName);
if (vol) {
vol->SetConfig(configName, configValue);
} else {
ERR("Invalid volume name '%s'.", volName.get());
}
continue;
}
ERR("Unrecognized command: '%s'", command.get());
}
}
void
VolumeManager::DefaultConfig()
{
VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
if (numVolumes == 0) {
return;
}
if (numVolumes == 1) {
// This is to cover early shipping phones like the Buri,
// which had no internal storage, and only external sdcard.
//
// Phones line the nexus-4 which only have an internal
// storage area will need to have a volume.cfg file with
// removable set to false.
RefPtr<Volume> vol = VolumeManager::GetVolume(0);
vol->SetIsRemovable(true);
vol->SetIsHotSwappable(true);
return;
}
VolumeManager::VolumeArray::index_type volIndex;
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
if (!vol->Name().EqualsLiteral("sdcard")) {
vol->SetIsRemovable(true);
vol->SetIsHotSwappable(true);
}
}
}
@ -233,6 +293,7 @@ class VolumeListCallback : public VolumeResponseCallback
// We've received the list of volumes. Now read the Volume.cfg
// file to perform customizations, and then tell everybody
// that we're ready for business.
VolumeManager::DefaultConfig();
VolumeManager::InitConfig();
VolumeManager::Dump("READY");
VolumeManager::SetState(VolumeManager::VOLUMES_READY);

View File

@ -138,6 +138,8 @@ protected:
virtual void OnFileCanWriteWithoutBlocking(int aFd);
virtual void OnError();
static void DefaultConfig();
private:
bool OpenSocket();

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
#include "nsIVolumeStat.idl"
[scriptable, uuid(9B5E27F4-601C-11E4-B541-3E2D1D5D46B0)]
[scriptable, uuid(946B5334-6EC9-11E4-8689-F3061E5D46B0)]
interface nsIVolume : nsISupports
{
// These MUST match the states from android's system/vold/Volume.h header
@ -86,6 +86,13 @@ interface nsIVolume : nsISupports
// Whether this is a fake volume.
readonly attribute boolean isFake;
// Whether this is a removable volume
readonly attribute boolean isRemovable;
// Whether this is a hot-swappable volume
readonly attribute boolean isHotSwappable;
};
%{C++

View File

@ -59,7 +59,9 @@ nsVolume::nsVolume(const Volume* aVolume)
mIsMediaPresent(aVolume->MediaPresent()),
mIsSharing(aVolume->IsSharing()),
mIsFormatting(aVolume->IsFormatting()),
mIsUnmounting(aVolume->IsUnmounting())
mIsUnmounting(aVolume->IsUnmounting()),
mIsRemovable(aVolume->IsRemovable()),
mIsHotSwappable(aVolume->IsHotSwappable())
{
}
@ -119,6 +121,18 @@ bool nsVolume::Equals(nsIVolume* aVolume)
return false;
}
bool isRemovable;
aVolume->GetIsRemovable(&isRemovable);
if (mIsRemovable != isRemovable) {
return false;
}
bool isHotSwappable;
aVolume->GetIsHotSwappable(&isHotSwappable);
if (mIsHotSwappable != isHotSwappable) {
return false;
}
return true;
}
@ -200,6 +214,18 @@ NS_IMETHODIMP nsVolume::GetIsFake(bool *aIsFake)
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetIsRemovable(bool *aIsRemovable)
{
*aIsRemovable = mIsRemovable;
return NS_OK;
}
NS_IMETHODIMP nsVolume::GetIsHotSwappable(bool *aIsHotSwappable)
{
*aIsHotSwappable = mIsHotSwappable;
return NS_OK;
}
NS_IMETHODIMP nsVolume::Format()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -277,11 +303,12 @@ nsVolume::LogState() const
{
if (mState == nsIVolume::STATE_MOUNTED) {
LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d "
"media %d sharing %d formatting %d unmounting %d",
"media %d sharing %d formatting %d unmounting %d removable %d hotswappable %d",
NameStr().get(), StateStr(), MountPointStr().get(),
MountGeneration(), (int)IsMountLocked(), (int)IsFake(),
(int)IsMediaPresent(), (int)IsSharing(),
(int)IsFormatting(), (int)IsUnmounting());
(int)IsFormatting(), (int)IsUnmounting(),
(int)IsRemovable(), (int)IsHotSwappable());
return;
}
@ -300,6 +327,8 @@ void nsVolume::Set(nsIVolume* aVolume)
aVolume->GetIsSharing(&mIsSharing);
aVolume->GetIsFormatting(&mIsFormatting);
aVolume->GetIsUnmounting(&mIsUnmounting);
aVolume->GetIsRemovable(&mIsRemovable);
aVolume->GetIsHotSwappable(&mIsHotSwappable);
int32_t volMountGeneration;
aVolume->GetMountGeneration(&volMountGeneration);
@ -377,6 +406,24 @@ nsVolume::SetIsFake(bool aIsFake)
}
}
void
nsVolume::SetIsRemovable(bool aIsRemovable)
{
mIsRemovable = aIsRemovable;
if (!mIsRemovable) {
mIsHotSwappable = false;
}
}
void
nsVolume::SetIsHotSwappable(bool aIsHotSwappable)
{
mIsHotSwappable = aIsHotSwappable;
if (mIsHotSwappable) {
mIsRemovable = true;
}
}
void
nsVolume::SetState(int32_t aState)
{

View File

@ -31,7 +31,8 @@ public:
const int32_t& aState, const int32_t& aMountGeneration,
const bool& aIsMediaPresent, const bool& aIsSharing,
const bool& aIsFormatting, const bool& aIsFake,
const bool& aIsUnmounting)
const bool& aIsUnmounting, const bool& aIsRemovable,
const bool& aIsHotSwappable)
: mName(aName),
mMountPoint(aMountPoint),
mState(aState),
@ -41,7 +42,9 @@ public:
mIsMediaPresent(aIsMediaPresent),
mIsSharing(aIsSharing),
mIsFormatting(aIsFormatting),
mIsUnmounting(aIsUnmounting)
mIsUnmounting(aIsUnmounting),
mIsRemovable(aIsRemovable),
mIsHotSwappable(aIsHotSwappable)
{
}
@ -56,7 +59,9 @@ public:
mIsMediaPresent(false),
mIsSharing(false),
mIsFormatting(false),
mIsUnmounting(false)
mIsUnmounting(false),
mIsRemovable(false),
mIsHotSwappable(false)
{
}
@ -82,6 +87,8 @@ public:
bool IsSharing() const { return mIsSharing; }
bool IsFormatting() const { return mIsFormatting; }
bool IsUnmounting() const { return mIsUnmounting; }
bool IsRemovable() const { return mIsRemovable; }
bool IsHotSwappable() const { return mIsHotSwappable; }
typedef nsTArray<nsRefPtr<nsVolume> > Array;
@ -93,6 +100,8 @@ private:
void UpdateMountLock(bool aMountLocked);
void SetIsFake(bool aIsFake);
void SetIsRemovable(bool aIsRemovable);
void SetIsHotSwappable(bool aIsHotSwappble);
void SetState(int32_t aState);
static void FormatVolumeIOThread(const nsCString& aVolume);
static void MountVolumeIOThread(const nsCString& aVolume);
@ -108,6 +117,8 @@ private:
bool mIsSharing;
bool mIsFormatting;
bool mIsUnmounting;
bool mIsRemovable;
bool mIsHotSwappable;
};
} // system

View File

@ -210,7 +210,9 @@ nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aRe
false /* isSharing */,
false /* isFormatting */,
true /* isFake */,
false /* isUnmounting*/);
false /* isUnmounting */,
false /* isRemovable */,
false /* isHotSwappable*/);
vol.forget(aResult);
return NS_OK;
}
@ -272,6 +274,8 @@ nsVolumeService::GetVolumesForIPC(nsTArray<VolumeInfo>* aResult)
volInfo->isFormatting() = vol->mIsFormatting;
volInfo->isFake() = vol->mIsFake;
volInfo->isUnmounting() = vol->mIsUnmounting;
volInfo->isRemovable() = vol->mIsRemovable;
volInfo->isHotSwappable() = vol->mIsHotSwappable;
}
}
@ -300,7 +304,9 @@ nsVolumeService::GetVolumesFromParent()
volInfo.isSharing(),
volInfo.isFormatting(),
volInfo.isFake(),
volInfo.isUnmounting());
volInfo.isUnmounting(),
volInfo.isRemovable(),
volInfo.isHotSwappable());
UpdateVolume(vol, false);
}
}
@ -425,7 +431,9 @@ nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
false /* isSharing */,
false /* isFormatting */,
true /* isFake */,
false /* isUnmounting */);
false /* isUnmounting */,
false /* isRemovable */,
false /* isHotSwappable */);
vol->LogState();
UpdateVolume(vol.get());
return NS_OK;
@ -475,11 +483,12 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
"media %d sharing %d formatting %d unmounting %d",
"media %d sharing %d formatting %d unmounting %d removable %d hotswappable %d",
mVolume->NameStr().get(), mVolume->StateStr(),
mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
(int)mVolume->IsMediaPresent(), mVolume->IsSharing(),
mVolume->IsFormatting(), mVolume->IsUnmounting());
mVolume->IsFormatting(), mVolume->IsUnmounting(),
(int)mVolume->IsRemovable(), (int)mVolume->IsHotSwappable());
mVolumeService->UpdateVolume(mVolume);
mVolumeService = nullptr;
@ -496,11 +505,12 @@ void
nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
{
DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
"media %d sharing %d formatting %d unmounting %d",
"media %d sharing %d formatting %d unmounting %d removable %d hotswappable %d",
aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
(int)aVolume->MediaPresent(), (int)aVolume->IsSharing(),
(int)aVolume->IsFormatting(), (int)mVolume->IsUnmounting());
(int)aVolume->IsFormatting(), (int)mVolume->IsUnmounting(),
(int)aVolume->IsRemovable(), (int)mVolume->IsHotSwappable());
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
}

View File

@ -82,6 +82,9 @@ interface DeviceStorage : EventTarget {
// for storing new files.
readonly attribute boolean default;
// Indicates if the storage area denoted by storageName is removable
readonly attribute boolean isRemovable;
[NewObject, Throws]
// XXXbz what type does this really return?
Promise<any> getRoot();