Bug 737153 - Enable mounting storage through USB from a host machine - Add the VolumeManager, sr=cjones r=qDot

This commit is contained in:
Dave Hylands 2012-05-24 21:03:07 -07:00
parent 05c5f8c8bc
commit 9acc65b631
7 changed files with 1087 additions and 0 deletions

View File

@ -0,0 +1,71 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManager.h"
#include "VolumeManagerLog.h"
namespace mozilla {
namespace system {
Volume::Volume(const nsCSubstring &aName)
: mState(STATE_INIT),
mName(aName)
{
DBG("Volume %s: created", NameStr());
}
void
Volume::SetState(Volume::STATE aNewState)
{
if (aNewState != mState) {
LOG("Volume %s: changing state from %s to %s",
NameStr(), StateStr(mState), StateStr(aNewState));
mState = aNewState;
}
}
void
Volume::SetMountPoint(const nsCSubstring &aMountPoint)
{
if (!mMountPoint.Equals(aMountPoint)) {
mMountPoint = aMountPoint;
DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
}
}
void
Volume::StartMount(VolumeResponseCallback *aCallback)
{
StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
}
void
Volume::StartUnmount(VolumeResponseCallback *aCallback)
{
StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
}
void
Volume::StartShare(VolumeResponseCallback *aCallback)
{
StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
}
void
Volume::StartUnshare(VolumeResponseCallback *aCallback)
{
StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
}
void
Volume::StartCommand(VolumeCommand *aCommand)
{
VolumeManager::PostCommand(aCommand);
}
} // namespace system
} // namespace mozilla

95
dom/system/gonk/Volume.h Normal file
View File

@ -0,0 +1,95 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_system_volume_h__
#define mozilla_system_volume_h__
#include "mozilla/RefPtr.h"
#include "nsString.h"
#include "VolumeCommand.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* There is an instance of the Volume class for each volume reported
* from vold.
*
* Each volume originates from the /system/etv/vold.fstab file.
*
***************************************************************************/
class Volume : public RefCounted<Volume>
{
public:
// These MUST match the states from android's system/vold/Volume.h header
enum STATE
{
STATE_INIT = -1,
STATE_NOMEDIA = 0,
STATE_IDLE = 1,
STATE_PENDING = 2,
STATE_CHECKING = 3,
STATE_MOUNTED = 4,
STATE_UNMOUNTING = 5,
STATE_FORMATTING = 6,
STATE_SHARED = 7,
STATE_SHAREDMNT = 8
};
Volume(const nsCSubstring &aVolumeName);
const char *StateStr(STATE aState) const
{
switch (aState) {
case STATE_INIT: return "Init";
case STATE_NOMEDIA: return "NoMedia";
case STATE_IDLE: return "Idle";
case STATE_PENDING: return "Pending";
case STATE_CHECKING: return "Checking";
case STATE_MOUNTED: return "Mounted";
case STATE_UNMOUNTING: return "Unmounting";
case STATE_FORMATTING: return "Formatting";
case STATE_SHARED: return "Shared";
case STATE_SHAREDMNT: return "Shared-Mounted";
}
return "???";
}
const char *StateStr() const { return StateStr(mState); }
STATE State() const { return mState; }
const nsCString &Name() const { return mName; }
const char *NameStr() const { return mName.get(); }
// The mount point is the name of the directory where the volume is mounted.
// (i.e. path that leads to the files stored on the volume).
const nsCString &MountPoint() const { return mMountPoint; }
// The StartXxx functions will queue up a command to the VolumeManager.
// You can queue up as many commands as you like, and aCallback will
// be called as each one completes.
void StartMount(VolumeResponseCallback *aCallback);
void StartUnmount(VolumeResponseCallback *aCallback);
void StartShare(VolumeResponseCallback *aCallback);
void StartUnshare(VolumeResponseCallback *aCallback);
private:
friend class VolumeManager; // Calls SetState
friend class VolumeListCallback; // Calls SetMountPoint, SetState
void SetState(STATE aNewState);
void SetMountPoint(const nsCSubstring &aMountPoint);
void StartCommand(VolumeCommand *aCommand);
STATE mState;
const nsCString mName;
nsCString mMountPoint;
};
} // system
} // mozilla
#endif // mozilla_system_volumemanager_h__

View File

@ -0,0 +1,85 @@
/* 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 "nsString.h"
#include "nsWhitespaceTokenizer.h"
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManager.h"
#include "VolumeManagerLog.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* The VolumeActionCommand class is used to send commands which apply
* to a particular volume.
*
* The following commands would fit into this category:
*
* volume mount <volname>
* volume unmount <volname> [force]
* volume format <volname>
* volume share <volname> <method>
* volume unshare <volname> <method>
* volume shared <volname> <method>
*
* A typical response looks like:
*
* # vdc volume unshare sdcard ums
* 605 Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
* 200 volume operation succeeded
*
* Note that the 600 series of responses are considered unsolicited and
* are dealt with directly by the VolumeManager. This command will only
* see the terminating response code (200 in the example above).
*
***************************************************************************/
VolumeActionCommand::VolumeActionCommand(Volume *aVolume,
const char *aAction,
const char *aExtraArgs,
VolumeResponseCallback *aCallback)
: VolumeCommand(aCallback),
mVolume(aVolume)
{
nsCAutoString cmd;
cmd = "volume ";
cmd += aAction;
cmd += " ";
cmd += aVolume->Name().get();
// vold doesn't like trailing white space, so only add it if we really need to.
if (aExtraArgs && (*aExtraArgs != '\0')) {
cmd += " ";
cmd += aExtraArgs;
}
SetCmd(cmd);
}
/***************************************************************************
*
* The VolumeListCommand class is used to send the "volume list" command to
* vold.
*
* A typical response looks like:
*
* # vdc volume list
* 110 sdcard /mnt/sdcard 4
* 110 sdcard1 /mnt/sdcard/external_sd 4
* 200 Volumes listed.
*
***************************************************************************/
VolumeListCommand::VolumeListCommand(VolumeResponseCallback *aCallback)
: VolumeCommand(NS_LITERAL_CSTRING("volume list"), aCallback)
{
}
} // system
} // mozilla

View File

@ -0,0 +1,185 @@
/* 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_volumecommand_h__
#define mozilla_system_volumecommand_h__
#include "mozilla/RefPtr.h"
#include <algorithm>
#include <vold/ResponseCode.h>
namespace mozilla {
namespace system {
class Volume;
class VolumeCommand;
/***************************************************************************
*
* The VolumeResponseCallback class is an abstract base class. The ResponseReceived
* method will be called for each response received.
*
* Depending on the command, there may be multiple responses for the
* command. Done() will return true if this is the last response.
*
* The responses from vold are all of the form:
*
* <ResponseCode> <String>
*
* Valid Response codes can be found in the vold/ResponseCode.h header.
*
***************************************************************************/
class VolumeResponseCallback : public RefCounted<VolumeResponseCallback>
{
public:
VolumeResponseCallback()
: mResponseCode(0), mPending(false) {}
virtual ~VolumeResponseCallback() {}
bool Done() const
{
// Response codes from the 200, 400, and 500 series all indicated that
// the command has completed.
return (mResponseCode >= ResponseCode::CommandOkay)
&& (mResponseCode < ResponseCode::UnsolicitedInformational);
}
bool WasSuccessful() const
{
return mResponseCode == ResponseCode::CommandOkay;
}
bool IsPending() const { return mPending; }
int ResponseCode() const { return mResponseCode; }
const nsCString &ResponseStr() const { return mResponseStr; }
protected:
virtual void ResponseReceived(const VolumeCommand *aCommand) = 0;
private:
friend class VolumeCommand; // Calls HandleResponse and SetPending
void HandleResponse(const VolumeCommand *aCommand,
int aResponseCode,
nsACString &aResponseStr)
{
mResponseCode = aResponseCode;
mResponseStr = aResponseStr;
if (mResponseCode >= ResponseCode::CommandOkay) {
// This is a final response.
mPending = false;
}
ResponseReceived(aCommand);
}
void SetPending(bool aPending) { mPending = aPending; }
int mResponseCode; // The response code parsed from vold
nsCString mResponseStr; // The rest of the line.
bool mPending; // Waiting for response?
};
/***************************************************************************
*
* The VolumeCommand class is an abstract base class used to encapsulate
* volume commands send to vold.
*
* See VolumeManager.h for a list of the volume commands.
*
* Commands sent to vold need an explicit null character so we add one
* to the command to ensure that it's included in the length.
*
* All of these commands are asynchronous in nature, and the
* ResponseReceived callback will be called when a response is available.
*
***************************************************************************/
class VolumeCommand : public RefCounted<VolumeCommand>
{
public:
VolumeCommand(VolumeResponseCallback *aCallback)
: mBytesConsumed(0),
mCallback(aCallback)
{
SetCmd(NS_LITERAL_CSTRING(""));
}
VolumeCommand(const nsACString &aCommand, VolumeResponseCallback *aCallback)
: mBytesConsumed(0),
mCallback(aCallback)
{
SetCmd(aCommand);
}
virtual ~VolumeCommand() {}
void SetCmd(const nsACString &aCommand)
{
mCmd = aCommand;
// Add a null character. We want this to be included in the length since
// vold uses it to determine the end of the command.
mCmd.Append('\0');
}
const char *CmdStr() const { return mCmd.get(); }
const char *Data() const { return mCmd.Data() + mBytesConsumed; }
size_t BytesConsumed() const { return mBytesConsumed; }
size_t BytesRemaining() const
{
return mCmd.Length() - std::min(mCmd.Length(), mBytesConsumed);
}
void ConsumeBytes(size_t aNumBytes)
{
mBytesConsumed += std::min(BytesRemaining(), aNumBytes);
}
private:
friend class VolumeManager; // Calls SetPending & HandleResponse
void SetPending(bool aPending)
{
if (mCallback) {
mCallback->SetPending(aPending);
}
}
void HandleResponse(int aResponseCode, nsACString &aResponseStr)
{
if (mCallback) {
mCallback->HandleResponse(this, aResponseCode, aResponseStr);
}
}
nsCString mCmd; // Command being sent
size_t mBytesConsumed; // How many bytes have been sent
// Called when a response to the command is received.
RefPtr<VolumeResponseCallback> mCallback;
};
class VolumeActionCommand : public VolumeCommand
{
public:
VolumeActionCommand(Volume *aVolume, const char *aAction,
const char *aExtraArgs, VolumeResponseCallback *aCallback);
private:
RefPtr<Volume> mVolume;
};
class VolumeListCommand : public VolumeCommand
{
public:
VolumeListCommand(VolumeResponseCallback *aCallback);
};
} // system
} // mozilla
#endif // mozilla_system_volumecommand_h__

View File

@ -0,0 +1,448 @@
/* 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 <android/log.h>
#include <cutils/sockets.h>
#include <fcntl.h>
#include <sys/socket.h>
#include "base/message_loop.h"
#include "nsWhitespaceTokenizer.h"
#include "nsXULAppAPI.h"
#include "Volume.h"
#include "VolumeCommand.h"
#include "VolumeManager.h"
#include "VolumeManagerLog.h"
//using namespace mozilla::dom::gonk;
namespace mozilla {
namespace system {
static RefPtr<VolumeManager> sVolumeManager;
/***************************************************************************/
VolumeManager::VolumeManager()
: mState(VolumeManager::STARTING),
mSocket(-1),
mCommandPending(false),
mRcvIdx(0)
{
DBG("VolumeManager constructor called");
}
VolumeManager::~VolumeManager()
{
}
//static
VolumeManager::STATE
VolumeManager::State()
{
if (!sVolumeManager) {
return VolumeManager::UNINITIALIZED;
}
return sVolumeManager->mState;
}
//static
const char *
VolumeManager::StateStr()
{
switch (State()) {
case UNINITIALIZED: return "Uninitialized";
case STARTING: return "Starting";
case VOLUMES_READY: return "Volumes Ready";
}
return "???";
}
//static
void
VolumeManager::SetState(STATE aNewState)
{
if (!sVolumeManager) {
return;
}
if (sVolumeManager->mState != aNewState) {
sVolumeManager->mState = aNewState;
sVolumeManager->mStateObserverList.Broadcast(StateChangedEvent());
}
}
//static
void
VolumeManager::RegisterStateObserver(StateObserver *aObserver)
{
sVolumeManager->mStateObserverList.AddObserver(aObserver);
}
//static
void VolumeManager::UnregisterStateObserver(StateObserver *aObserver)
{
sVolumeManager->mStateObserverList.RemoveObserver(aObserver);
}
//static
TemporaryRef<Volume>
VolumeManager::FindVolumeByName(const nsCSubstring &aName)
{
if (!sVolumeManager) {
return NULL;
}
for (VolumeArray::iterator volIter = sVolumeManager->mVolumeArray.begin();
volIter != sVolumeManager->mVolumeArray.end();
volIter++) {
if ((*volIter)->Name().Equals(aName)) {
return *volIter;
}
}
return NULL;
}
//static
TemporaryRef<Volume>
VolumeManager::FindAddVolumeByName(const nsCSubstring &aName)
{
RefPtr<Volume> vol = FindVolumeByName(aName);
if (vol) {
return vol;
}
// No volume found, create and add a new one.
vol = new Volume(aName);
sVolumeManager->mVolumeArray.push_back(vol);
return vol;
}
class VolumeListCallback : public VolumeResponseCallback
{
virtual void ResponseReceived(const VolumeCommand *aCommand)
{
switch (ResponseCode()) {
case ResponseCode::VolumeListResult: {
// Each line will look something like:
//
// sdcard /mnt/sdcard 1
//
// So for each volume that we get back, we update any volumes that
// we have of the same name, or add new ones if they don't exist.
nsCWhitespaceTokenizer tokenizer(ResponseStr());
nsDependentCSubstring volName(tokenizer.nextToken());
nsDependentCSubstring mntPoint(tokenizer.nextToken());
nsCString state(tokenizer.nextToken());
RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName);
vol->SetMountPoint(mntPoint);
PRInt32 errCode;
vol->SetState((Volume::STATE)state.ToInteger(&errCode));
break;
}
case ResponseCode::CommandOkay: {
// We've received the list of volumes. Tell anybody who
// is listening that we're open for business.
VolumeManager::SetState(VolumeManager::VOLUMES_READY);
break;
}
}
}
};
bool
VolumeManager::OpenSocket()
{
SetState(STARTING);
if ((mSocket.rwget() = socket_local_client("vold",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM)) < 0) {
ERR("Error connecting to vold: (%s) - will retry", strerror(errno));
return false;
}
// add FD_CLOEXEC flag
int flags = fcntl(mSocket.get(), F_GETFD);
if (flags == -1) {
return false;
}
flags |= FD_CLOEXEC;
if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
return false;
}
// set non-blocking
if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
return false;
}
if (!MessageLoopForIO::current()->
WatchFileDescriptor(mSocket.get(),
true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher,
this)) {
return false;
}
LOG("Connected to vold");
PostCommand(new VolumeListCommand(new VolumeListCallback));
return true;
}
//static
void
VolumeManager::PostCommand(VolumeCommand *aCommand)
{
if (!sVolumeManager) {
ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data());
return;
}
aCommand->SetPending(true);
DBG("Sending command '%s'", aCommand->Data());
// vold can only process one command at a time, so add our command
// to the end of the command queue.
sVolumeManager->mCommands.push(aCommand);
if (!sVolumeManager->mCommandPending) {
// There aren't any commands currently being processed, so go
// ahead and kick this one off.
sVolumeManager->mCommandPending = true;
sVolumeManager->WriteCommandData();
}
}
/***************************************************************************
* The WriteCommandData initiates sending of a command to vold. Since
* we're running on the IOThread and not allowed to block, WriteCommandData
* will write as much data as it can, and if not all of the data can be
* written then it will setup a file descriptor watcher and
* OnFileCanWriteWithoutBlocking will call WriteCommandData to write out
* more of the command data.
*/
void
VolumeManager::WriteCommandData()
{
if (mCommands.size() == 0) {
return;
}
VolumeCommand *cmd = mCommands.front();
if (cmd->BytesRemaining() == 0) {
// All bytes have been written. We're waiting for a response.
return;
}
// There are more bytes left to write. Try to write them all.
ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining());
if (bytesWritten < 0) {
ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining());
Restart();
return;
}
DBG("Wrote %ld bytes (of %d)", bytesWritten, cmd->BytesRemaining());
cmd->ConsumeBytes(bytesWritten);
if (cmd->BytesRemaining() == 0) {
return;
}
// We were unable to write all of the command bytes. Setup a watcher
// so we'll get called again when we can write without blocking.
if (!MessageLoopForIO::current()->
WatchFileDescriptor(mSocket.get(),
false, // one-shot
MessageLoopForIO::WATCH_WRITE,
&mWriteWatcher,
this)) {
ERR("Failed to setup write watcher for vold socket");
Restart();
}
}
void
VolumeManager::OnFileCanReadWithoutBlocking(int aFd)
{
MOZ_ASSERT(aFd == mSocket.get());
while (true) {
ssize_t bytesRemaining = read(aFd, &mRcvBuf[mRcvIdx], sizeof(mRcvBuf) - mRcvIdx);
if (bytesRemaining < 0) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
return;
}
if (errno == EINTR) {
continue;
}
ERR("Unknown read error: %d (%s) - restarting", errno, strerror(errno));
Restart();
return;
}
if (bytesRemaining == 0) {
// This means that vold probably crashed
ERR("Vold appears to have crashed - restarting");
Restart();
return;
}
// We got some data. Each line is terminated by a null character
DBG("Read %ld bytes", bytesRemaining);
while (bytesRemaining > 0) {
bytesRemaining--;
if (mRcvBuf[mRcvIdx] == '\0') {
// We found a line terminator. Each line is formatted as an
// integer response code followed by the rest of the line.
// Fish out the response code.
char *endPtr;
int responseCode = strtol(mRcvBuf, &endPtr, 10);
if (*endPtr == ' ') {
endPtr++;
}
// Now fish out the rest of the line after the response code
nsDependentCString responseLine(endPtr, &mRcvBuf[mRcvIdx] - endPtr);
DBG("Rcvd: %d '%s'", responseCode, responseLine.Data());
if (responseCode >= ResponseCode::UnsolicitedInformational) {
// These are unsolicited broadcasts. We intercept these and process
// them ourselves
HandleBroadcast(responseCode, responseLine);
} else {
// Everything else is considered to be part of the command response.
if (mCommands.size() > 0) {
VolumeCommand *cmd = mCommands.front();
cmd->HandleResponse(responseCode, responseLine);
if (responseCode >= ResponseCode::CommandOkay) {
// That's a terminating response. We can remove the command.
mCommands.pop();
mCommandPending = false;
// Start the next command, if there is one.
WriteCommandData();
}
} else {
ERR("Response with no command");
}
}
if (bytesRemaining > 0) {
// There is data in the receive buffer beyond the current line.
// Shift it down to the beginning.
memmove(&mRcvBuf[0], &mRcvBuf[mRcvIdx + 1], bytesRemaining);
}
mRcvIdx = 0;
} else {
mRcvIdx++;
}
}
}
}
void
VolumeManager::OnFileCanWriteWithoutBlocking(int aFd)
{
MOZ_ASSERT(aFd == mSocket.get());
WriteCommandData();
}
void
VolumeManager::HandleBroadcast(int aResponseCode, nsCString &aResponseLine)
{
if (aResponseCode != ResponseCode::VolumeStateChange) {
return;
}
// Format of the line is something like:
//
// Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
//
// So we parse out the volume name and the state after the string " to "
nsCWhitespaceTokenizer tokenizer(aResponseLine);
tokenizer.nextToken(); // The word "Volume"
nsDependentCSubstring volName(tokenizer.nextToken());
RefPtr<Volume> vol = FindVolumeByName(volName);
if (!vol) {
return;
}
const char *s = strstr(aResponseLine.get(), " to ");
if (!s) {
return;
}
s += 4;
vol->SetState((Volume::STATE)atoi(s));
}
void
VolumeManager::Restart()
{
mReadWatcher.StopWatchingFileDescriptor();
mWriteWatcher.StopWatchingFileDescriptor();
while (!mCommands.empty()) {
mCommands.pop();
}
mCommandPending = false;
mSocket.dispose();
mRcvIdx = 0;
Start();
}
//static
void
VolumeManager::Start()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (!sVolumeManager) {
return;
}
if (!sVolumeManager->OpenSocket()) {
// Socket open failed, try again in a second.
MessageLoopForIO::current()->
PostDelayedTask(FROM_HERE,
NewRunnableFunction(VolumeManager::Start),
1000);
}
}
/***************************************************************************/
static void
InitVolumeManagerIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
MOZ_ASSERT(!sVolumeManager);
sVolumeManager = new VolumeManager();
VolumeManager::Start();
}
static void
ShutdownVolumeManagerIOThread()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
sVolumeManager = NULL;
}
/**************************************************************************
*
* Public API
*
* Since the VolumeManager runs in IO Thread context, we need to switch
* to IOThread context before we can do anything.
*
**************************************************************************/
void
InitVolumeManager()
{
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(InitVolumeManagerIOThread));
}
void
ShutdownVolumeManager()
{
XRE_GetIOMessageLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(ShutdownVolumeManagerIOThread));
}
} // system
} // mozilla

View File

@ -0,0 +1,183 @@
/* 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_volumemanager_h__
#define mozilla_system_volumemanager_h__
#include <vector>
#include <queue>
#include "base/message_loop.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Observer.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"
#include "Volume.h"
#include "VolumeCommand.h"
namespace mozilla {
namespace system {
/***************************************************************************
*
* All of the public API mentioned in this file (unless otherwise
* mentioned) must run from the IOThread.
*
***************************************************************************/
/***************************************************************************
*
* The VolumeManager class is a front-end for android's vold service.
*
* Vold uses a unix socket interface and accepts null-terminated string
* commands. The following commands were determined by examining the vold
* source code:
*
* volume list
* volume mount <volname>
* volume unmount <volname> [force]
* volume debug [on|off]
* volume format <volname>
* volume share <volname> <method>
* volume unshare <volname> <method>
* volume shared <volname> <method>
*
* <volname> is the name of the volume as used in /system/etc/vold.fstab
* <method> is ums
*
* dump
*
* share status <method> (Determines if a particular sharing method is available)
* (GB only - not available in ICS)
*
* storage users (??? always crashes vold ???)
*
* asec list
* asec ...lots more...
*
* obb list
* obb ...lots more...
*
* xwarp enable
* xwarp disable
* xwarp status
*
* There is also a command line tool called vdc, which can be used to send
* the above commands to vold.
*
* Currently, only the volume list, share/unshare, and mount/unmount
* commands are being used.
*
***************************************************************************/
class VolumeManager : public MessageLoopForIO::Watcher,
public RefCounted<VolumeManager>
{
public:
typedef std::vector<RefPtr<Volume> > VolumeArray;
VolumeManager();
virtual ~VolumeManager();
//-----------------------------------------------------------------------
//
// State related methods.
//
// The VolumeManager starts off in the STARTING state. Once a connection
// is established with vold, it asks for a list of volumes, and once the
// volume list has been received, then the VolumeManager enters the
// VOLUMES_READY state.
//
// If vold crashes, then the VolumeManager will once again enter the
// STARTING state and try to reestablish a connection with vold.
enum STATE
{
UNINITIALIZED,
STARTING,
VOLUMES_READY
};
static STATE State();
static const char *StateStr();
class StateChangedEvent
{
public:
StateChangedEvent() {}
};
typedef mozilla::Observer<StateChangedEvent> StateObserver;
typedef mozilla::ObserverList<StateChangedEvent> StateObserverList;
static void RegisterStateObserver(StateObserver *aObserver);
static void UnregisterStateObserver(StateObserver *aObserver);
//-----------------------------------------------------------------------
static void Start();
static TemporaryRef<Volume> FindVolumeByName(const nsCSubstring &aName);
static TemporaryRef<Volume> FindAddVolumeByName(const nsCSubstring &aName);
static void PostCommand(VolumeCommand *aCommand);
protected:
virtual void OnFileCanReadWithoutBlocking(int aFd);
virtual void OnFileCanWriteWithoutBlocking(int aFd);
private:
bool OpenSocket();
friend class VolumeListCallback; // Calls SetState
static void SetState(STATE aNewState);
void Restart();
void WriteCommandData();
void HandleBroadcast(int aResponseCode, nsCString &aResponseLine);
typedef std::queue<RefPtr<VolumeCommand> > CommandQueue;
static const int kRcvBufSize = 1024;
STATE mState;
ScopedClose mSocket;
VolumeArray mVolumeArray;
CommandQueue mCommands;
bool mCommandPending;
char mRcvBuf[kRcvBufSize];
size_t mRcvIdx;
StateObserverList mStateObserverList;
MessageLoopForIO *mIOLoop;
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
RefPtr<VolumeResponseCallback> mBroadcastCallback;
};
/***************************************************************************
*
* The initialization/shutdown functions do not need to be called from
* the IOThread context.
*
***************************************************************************/
/**
* Initialize the Volume Manager. On initialization, the VolumeManager will
* attempt to connect with vold and collect the list of volumes that vold
* knows about.
*/
void InitVolumeManager();
/**
* Shuts down the Volume Manager.
*/
void ShutdownVolumeManager();
} // system
} // mozilla
#endif // mozilla_system_volumemanager_h__

View File

@ -0,0 +1,20 @@
/* 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_volumemanagerlog_h__
#define mozilla_system_volumemanagerlog_h__
#define USE_DEBUG 0
#undef LOG
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "VolumeManager" , ## args)
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "VolumeManager" , ## args)
#if USE_DEBUG
#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "VolumeManager" , ## args)
#else
#define DBG(args...)
#endif
#endif // mozilla_system_volumemanagerlog_h__