mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 737153 - Enable mounting storage through USB from a host machine - Add the VolumeManager, sr=cjones r=qDot
This commit is contained in:
parent
05c5f8c8bc
commit
9acc65b631
71
dom/system/gonk/Volume.cpp
Normal file
71
dom/system/gonk/Volume.cpp
Normal 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
95
dom/system/gonk/Volume.h
Normal 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__
|
85
dom/system/gonk/VolumeCommand.cpp
Normal file
85
dom/system/gonk/VolumeCommand.cpp
Normal 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
|
||||
|
185
dom/system/gonk/VolumeCommand.h
Normal file
185
dom/system/gonk/VolumeCommand.h
Normal 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__
|
448
dom/system/gonk/VolumeManager.cpp
Normal file
448
dom/system/gonk/VolumeManager.cpp
Normal 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
|
||||
|
183
dom/system/gonk/VolumeManager.h
Normal file
183
dom/system/gonk/VolumeManager.h
Normal 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__
|
20
dom/system/gonk/VolumeManagerLog.h
Normal file
20
dom/system/gonk/VolumeManagerLog.h
Normal 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__
|
Loading…
Reference in New Issue
Block a user