Bug 1049240 - Integrate MTP with the AutoMounter. r=echou

This commit is contained in:
Dave Hylands 2014-08-19 13:41:35 -07:00
parent ba38123468
commit 431ebf4702
11 changed files with 700 additions and 115 deletions

View File

@ -16,6 +16,7 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <android/log.h>
#include <cutils/properties.h>
#include "AutoMounter.h"
#include "nsVolumeService.h"
@ -26,7 +27,9 @@
#include "mozilla/Hal.h"
#include "mozilla/StaticPtr.h"
#include "MozMtpServer.h"
#include "MozMtpStorage.h"
#include "nsAutoPtr.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsMemory.h"
#include "nsString.h"
#include "nsThreadUtils.h"
@ -90,6 +93,13 @@ USING_MTP_NAMESPACE
namespace mozilla {
namespace system {
#define SYS_USB_CONFIG "sys.usb.config"
#define PERSIST_SYS_USB_CONFIG "persist.sys.usb.config"
#define USB_FUNC_ADB "adb"
#define USB_FUNC_MTP "mtp"
#define USB_FUNC_UMS "mass_storage"
class AutoMounter;
static void SetAutoMounterStatus(int32_t aStatus);
@ -115,7 +125,9 @@ IsUsbCablePluggedIn()
if (access(ICS_SYS_USB_STATE, F_OK) == 0) {
char usbState[20];
if (ReadSysFile(ICS_SYS_USB_STATE, usbState, sizeof(usbState))) {
return strcmp(usbState, "CONFIGURED") == 0;
DBG("IsUsbCablePluggedIn: state = '%s'", usbState);
return strcmp(usbState, "CONFIGURED") == 0 ||
strcmp(usbState, "CONNECTED") == 0;
}
ERR("Error reading file '%s': %s", ICS_SYS_USB_STATE, strerror(errno));
return false;
@ -129,6 +141,26 @@ IsUsbCablePluggedIn()
#endif
}
static bool
IsUsbConfigured()
{
if (access(ICS_SYS_USB_STATE, F_OK) == 0) {
char usbState[20];
if (ReadSysFile(ICS_SYS_USB_STATE, usbState, sizeof(usbState))) {
DBG("IsUsbConfigured: state = '%s'", usbState);
return strcmp(usbState, "CONFIGURED") == 0;
}
ERR("Error reading file '%s': %s", ICS_SYS_USB_STATE, strerror(errno));
return false;
}
bool configured;
if (ReadSysFile(GB_SYS_USB_CONFIGURED, &configured)) {
return configured;
}
ERR("Error reading file '%s': %s", GB_SYS_USB_CONFIGURED, strerror(errno));
return false;
}
/***************************************************************************/
// The AutoVolumeManagerStateObserver allows the AutoMounter to know when
@ -144,7 +176,7 @@ public:
class AutoVolumeEventObserver : public Volume::EventObserver
{
public:
virtual void Notify(Volume * const & aEvent);
virtual void Notify(Volume* const& aEvent);
};
class AutoMounterResponseCallback : public VolumeResponseCallback
@ -174,7 +206,8 @@ public:
typedef nsTArray<RefPtr<Volume>> VolumeArray;
AutoMounter()
: mResponseCallback(new AutoMounterResponseCallback),
: mState(STATE_IDLE),
mResponseCallback(new AutoMounterResponseCallback),
mMode(AUTOMOUNTER_DISABLE)
{
VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver);
@ -232,21 +265,34 @@ public:
}
}
void UpdateState();
void ConfigureUsbFunction(const char* aUsbFunc);
void StartMtpServer();
void StopMtpServer();
void UpdateState();
void StartUmsSharing();
void StopUmsSharing();
const char* ModeStr(int32_t aMode)
{
switch (aMode) {
case AUTOMOUNTER_DISABLE: return "Disable";
case AUTOMOUNTER_ENABLE: return "Enable";
case AUTOMOUNTER_ENABLE_UMS: return "Enable-UMS";
case AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED: return "DisableWhenUnplugged";
case AUTOMOUNTER_ENABLE_MTP: return "Enable-MTP";
}
return "??? Unknown ???";
}
bool IsModeEnabled(int32_t aMode)
{
return aMode == AUTOMOUNTER_ENABLE_MTP ||
aMode == AUTOMOUNTER_ENABLE_UMS;
}
void SetMode(int32_t aMode)
{
if ((aMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) &&
@ -256,8 +302,8 @@ public:
aMode = AUTOMOUNTER_DISABLE;
}
if ((aMode == AUTOMOUNTER_DISABLE) &&
(mMode == AUTOMOUNTER_ENABLE) && IsUsbCablePluggedIn()) {
if (aMode == AUTOMOUNTER_DISABLE &&
mMode == AUTOMOUNTER_ENABLE_UMS && IsUsbCablePluggedIn()) {
// On many devices (esp non-Samsung), we can't force the disable, so we
// need to defer until the USB cable is actually unplugged.
// See bug 777043.
@ -346,10 +392,64 @@ public:
private:
enum STATE
{
// IDLE - Nothing is being shared
STATE_IDLE,
// We've detected that conditions are right to enable mtp. So we've
// set sys.usb.config to include mtp, and we're waiting for the USB
// subsystem to be "configured". Once mtp shows up in
// then we know
// that its been configured and we can open /dev/mtp_usb
STATE_MTP_CONFIGURING,
// mtp has been configured (i.e. mtp now shows up in
// /sys/devices/virtual/android_usb/android0/functions so we can start
// the mtp server.
STATE_MTP_STARTED,
// The mtp server has reported sessionStarted. We'll leave this state
// when we receive sessionEnded.
STATE_MTP_CONNECTED,
// We've added mass_storage (aka UMS) to sys.usb.config and we're waiting for
// mass_storage to appear in /sys/devices/virtual/android_usb/android0/functions
STATE_UMS_CONFIGURING,
// mass_storage has been configured and we can start sharing once the user
// enables it.
STATE_UMS_CONFIGURED,
};
const char *StateStr(STATE aState)
{
switch (aState) {
case STATE_IDLE: return "IDLE";
case STATE_MTP_CONFIGURING: return "MTP_CONFIGURING";
case STATE_MTP_CONNECTED: return "MTP_CONNECTED";
case STATE_MTP_STARTED: return "MTP_STARTED";
case STATE_UMS_CONFIGURING: return "UMS_CONFIGURING";
case STATE_UMS_CONFIGURED: return "UMS_CONFIGURED";
}
return "STATE_???";
}
void SetState(STATE aState)
{
const char *oldStateStr = StateStr(mState);
mState = aState;
const char *newStateStr = StateStr(mState);
LOG("AutoMounter state changed from %s to %s", oldStateStr, newStateStr);
}
STATE mState;
AutoVolumeEventObserver mVolumeEventObserver;
AutoVolumeManagerStateObserver mVolumeManagerStateObserver;
RefPtr<VolumeResponseCallback> mResponseCallback;
int32_t mMode;
MozMtpStorage::Array mMozMtpStorage;
};
static StaticRefPtr<AutoMounter> sAutoMounter;
@ -405,6 +505,78 @@ AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand)
}
}
static bool
IsUsbFunctionEnabled(const char* aConfig, const char* aUsbFunc)
{
nsAutoCString config(aConfig);
nsCCharSeparatedTokenizer tokenizer(config, ',');
while (tokenizer.hasMoreTokens()) {
nsAutoCString token(tokenizer.nextToken());
if (token.Equals(aUsbFunc)) {
DBG("IsUsbFunctionEnabled('%s', '%s'): returning true", aConfig, aUsbFunc);
return true;
}
}
DBG("IsUsbFunctionEnabled('%s', '%s'): returning false", aConfig, aUsbFunc);
return false;
}
static void
SetUsbFunction(const char* aUsbFunc)
{
char oldSysUsbConfig[PROPERTY_VALUE_MAX];
property_get(SYS_USB_CONFIG, oldSysUsbConfig, "");
if (IsUsbFunctionEnabled(oldSysUsbConfig, aUsbFunc)) {
// The function is already configured. Nothing else to do.
DBG("SetUsbFunction('%s') - already set - nothing to do", aUsbFunc);
return;
}
char newSysUsbConfig[PROPERTY_VALUE_MAX];
if (strcmp(aUsbFunc, USB_FUNC_MTP) == 0) {
// We're enabling MTP. For this we'll wind up using mtp, or mtp,adb
strlcpy(newSysUsbConfig, USB_FUNC_MTP, sizeof(newSysUsbConfig));
} else if (strcmp(aUsbFunc, USB_FUNC_UMS) == 0) {
// We're enabling UMS. For this we make the assumption that the persisted
// property has mass_storage enabled.
property_get(PERSIST_SYS_USB_CONFIG, newSysUsbConfig, "");
} else {
printf_stderr("AutoMounter::SetUsbFunction Unrecognized aUsbFunc '%s'\n", aUsbFunc);
MOZ_ASSERT(0);
return;
}
// Make sure the new value that we write into sys.usb.config keeps the adb
// (or non-adb) of the current string.
if (IsUsbFunctionEnabled(oldSysUsbConfig, USB_FUNC_ADB)) {
// ADB was turned on - keep it on.
if (!IsUsbFunctionEnabled(newSysUsbConfig, USB_FUNC_ADB)) {
// Add adb to the new string
strlcat(newSysUsbConfig, ",", sizeof(newSysUsbConfig));
strlcat(newSysUsbConfig, USB_FUNC_ADB, sizeof(newSysUsbConfig));
}
} else {
// ADB was turned off - keep it off
if (IsUsbFunctionEnabled(newSysUsbConfig, USB_FUNC_ADB)) {
// Remove ADB from the new string.
if (strcmp(newSysUsbConfig, USB_FUNC_ADB) == 0) {
newSysUsbConfig[0] = '\0';
} else {
nsAutoCString withoutAdb(newSysUsbConfig);
withoutAdb.ReplaceSubstring( "," USB_FUNC_ADB, "");
strlcpy(newSysUsbConfig, withoutAdb.get(), sizeof(newSysUsbConfig));
}
}
}
LOG("SetUsbFunction(%s) %s to '%s'", aUsbFunc, SYS_USB_CONFIG, newSysUsbConfig);
property_set(SYS_USB_CONFIG, newSysUsbConfig);
}
void
AutoMounter::StartMtpServer()
{
@ -415,12 +587,22 @@ AutoMounter::StartMtpServer()
LOG("Starting MtpServer");
sMozMtpServer = new MozMtpServer();
sMozMtpServer->Run();
VolumeArray::index_type volIndex;
VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
nsRefPtr<MozMtpStorage> storage = new MozMtpStorage(vol, sMozMtpServer);
mMozMtpStorage.AppendElement(storage);
}
}
void
AutoMounter::StopMtpServer()
{
LOG("Stopping MtpServer");
mMozMtpStorage.Clear();
sMozMtpServer = nullptr;
}
@ -465,10 +647,20 @@ AutoMounter::UpdateState()
return;
}
bool umsAvail = false;
bool umsEnabled = false;
bool mtpAvail = false;
bool mtpEnabled = false;
// Calling setprop sys.usb.config mtp,adb (or adding mass_storage) will
// cause /sys/devices/virtual/android_usb/android0/state to go:
// CONFIGURED -> DISCONNECTED -> CONNECTED -> CONFIGURED
//
// Since IsUsbCablePluggedIn returns state == CONFIGURED, it will look
// like a cable pull and replugin.
bool umsAvail = false;
bool umsConfigured = false;
bool umsEnabled = false;
bool mtpAvail = false;
bool mtpConfigured = false;
bool mtpEnabled = false;
bool usbCablePluggedIn = IsUsbCablePluggedIn();
if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
char functionsStr[60];
@ -476,40 +668,149 @@ AutoMounter::UpdateState()
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
functionsStr[0] = '\0';
}
DBG("UpdateState: USB functions: '%s'", functionsStr);
bool usbConfigured = IsUsbConfigured();
umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
if (umsAvail) {
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
umsConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_UMS) != nullptr;
umsEnabled = (mMode == AUTOMOUNTER_ENABLE_UMS) ||
(mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && umsConfigured;
} else {
umsConfigured = false;
umsEnabled = false;
}
mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
if (mtpAvail) {
mtpEnabled = strstr(functionsStr, "mtp") != nullptr;
mtpConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_MTP) != nullptr;
mtpEnabled = (mMode == AUTOMOUNTER_ENABLE_MTP) ||
(mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && mtpConfigured;
} else {
mtpConfigured = false;
mtpEnabled = false;
}
}
bool usbCablePluggedIn = IsUsbCablePluggedIn();
bool enabled = (mMode == AUTOMOUNTER_ENABLE);
bool enabled = mtpEnabled || umsEnabled;
if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) {
// DISABLE_WHEN_UNPLUGGED implies already enabled.
enabled = usbCablePluggedIn;
if (!usbCablePluggedIn) {
mMode = AUTOMOUNTER_DISABLE;
mtpEnabled = false;
umsEnabled = false;
}
}
bool tryToShare = (((umsAvail && umsEnabled) || (mtpAvail && mtpEnabled))
&& enabled && usbCablePluggedIn);
LOG("UpdateState: ums:%d%d mtp:%d%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
umsAvail, umsEnabled, mtpAvail, mtpEnabled, mMode, usbCablePluggedIn, tryToShare);
DBG("UpdateState: ums:A%dC%dE%d mtp:A%dC%dE%d mode:%d usb:%d mState:%s",
umsAvail, umsConfigured, umsEnabled,
mtpAvail, mtpConfigured, mtpEnabled,
mMode, usbCablePluggedIn, StateStr(mState));
if (mtpAvail && mtpEnabled) {
if (enabled && usbCablePluggedIn) {
StartMtpServer();
} else {
StopMtpServer();
}
return;
switch (mState) {
case STATE_IDLE:
if (!usbCablePluggedIn) {
// Stay in the IDLE state. We'll get a CONNECTED or CONFIGURED
// UEvent when the usb cable is plugged in.
break;
}
if (mtpEnabled) {
if (mtpConfigured) {
// The USB layer has already been configured. Now we can go ahead
// and start the MTP server. This particular codepath will not
// normally be taken, but it could happen if you stop and restart
// b2g while sys.usb.config is set to enable mtp.
StartMtpServer();
SetState(STATE_MTP_STARTED);
} else {
// The MTP USB layer is configuring. Wait for it to finish
// before we start the MTP server.
SetUsbFunction(USB_FUNC_MTP);
SetState(STATE_MTP_CONFIGURING);
}
} else if (umsConfigured) {
// UMS is already configured.
SetState(STATE_UMS_CONFIGURED);
} else if (umsAvail) {
// We do this whether or not UMS is enabled. With UMS, it's the
// sharing of the volume which is significant. What is important
// is that we don't leave it in MTP mode when MTP isn't enabled.
SetUsbFunction(USB_FUNC_UMS);
SetState(STATE_UMS_CONFIGURING);
}
break;
case STATE_MTP_CONFIGURING:
// While configuring, the USB configuration state will change from
// CONFIGURED -> CONNECTED -> DISCONNECTED -> CONNECTED -> CONFIGURED
// so we don't check for cable unplugged here.
if (mtpEnabled && mtpConfigured) {
// The USB layer has been configured. Now we can go ahead and start
// the MTP server.
StartMtpServer();
SetState(STATE_MTP_STARTED);
}
break;
case STATE_MTP_STARTED:
if (usbCablePluggedIn) {
if (mtpConfigured && mtpEnabled) {
// Everything is still good. Leave the MTP server running
break;
}
DBG("STATE_MTP_STARTED: About to StopMtpServer "
"mtpConfigured = %d mtpEnabled = %d usbCablePluggedIn: %d",
mtpConfigured, mtpEnabled, usbCablePluggedIn);
StopMtpServer();
if (umsAvail) {
// Switch back to UMS
SetUsbFunction(USB_FUNC_UMS);
SetState(STATE_UMS_CONFIGURING);
break;
}
}
SetState(STATE_IDLE);
break;
case STATE_UMS_CONFIGURING:
// While configuring, the USB configuration state will change from
// CONFIGURED -> CONNECTED -> DISCONNECTED -> CONNECTED -> CONFIGURED
// so we don't check for cable unplugged here.
if (umsConfigured) {
SetState(STATE_UMS_CONFIGURED);
}
break;
case STATE_UMS_CONFIGURED:
if (usbCablePluggedIn) {
if (mtpEnabled) {
// MTP was enabled. Start reconfiguring.
SetState(STATE_MTP_CONFIGURING);
SetUsbFunction(USB_FUNC_MTP);
break;
}
if (umsConfigured && umsEnabled) {
// This is the normal state when UMS is enabled.
break;
}
}
SetState(STATE_IDLE);
break;
default:
SetState(STATE_IDLE);
break;
}
bool tryToShare = umsEnabled && usbCablePluggedIn;
LOG("UpdateState: ums:A%dC%dE%d mtp:A%dC%dE%d mode:%d usb:%d tryToShare:%d state:%s",
umsAvail, umsConfigured, umsEnabled,
mtpAvail, mtpConfigured, mtpEnabled,
mMode, usbCablePluggedIn, tryToShare, StateStr(mState));
bool filesOpen = false;
static unsigned filesOpenDelayCount = 0;
VolumeArray::index_type volIndex;
@ -524,7 +825,8 @@ AutoMounter::UpdateState()
vol->MediaPresent() ? "inserted" : "missing",
vol->MountPoint().get(), vol->MountGeneration(),
(int)vol->IsMountLocked(),
vol->CanBeShared() ? (vol->IsSharingEnabled() ? (vol->IsSharing() ? "en-y" : "en-n") : "dis") : "x");
vol->CanBeShared() ? (vol->IsSharingEnabled() ?
(vol->IsSharing() ? "en-y" : "en-n") : "dis") : "x");
if (vol->IsSharing() && !usbCablePluggedIn) {
// We call SetIsSharing(true) below to indicate intent to share. This
// causes a state change which notifys apps, and they'll close any
@ -940,6 +1242,7 @@ void
ShutdownAutoMounter()
{
if (sAutoMounter) {
DBG("ShutdownAutoMounter: About to StopMtpServer");
sAutoMounter->StopMtpServer();
}
sAutoMounterSetting = nullptr;

View File

@ -14,8 +14,9 @@ namespace system {
// AutoMounter modes
#define AUTOMOUNTER_DISABLE 0
#define AUTOMOUNTER_ENABLE 1
#define AUTOMOUNTER_ENABLE_UMS 1
#define AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED 2
#define AUTOMOUNTER_ENABLE_MTP 3
// Automounter statuses
#define AUTOMOUNTER_STATUS_DISABLED 0

View File

@ -40,9 +40,9 @@ ObjectPropertyAsStr(MtpObjectProperty aProperty)
return "MTP_PROPERTY_???";
}
MozMtpDatabase::MozMtpDatabase(const char *aDir)
MozMtpDatabase::MozMtpDatabase()
{
MTP_LOG("");
MTP_LOG("constructed");
// We use the index into the array as the handle. Since zero isn't a valid
// index, we stick a dummy entry there.
@ -50,14 +50,12 @@ MozMtpDatabase::MozMtpDatabase(const char *aDir)
RefPtr<DbEntry> dummy;
mDb.AppendElement(dummy);
ReadVolume("sdcard", aDir);
}
//virtual
MozMtpDatabase::~MozMtpDatabase()
{
MTP_LOG("");
MTP_LOG("destructed");
}
void
@ -67,7 +65,7 @@ MozMtpDatabase::AddEntry(DbEntry *entry)
MOZ_ASSERT(mDb.Length() == entry->mHandle);
mDb.AppendElement(entry);
MTP_LOG("AddEntry: Handle: 0x%08x Parent: 0x%08x Path:'%s'",
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path:'%s'",
entry->mHandle, entry->mParent, entry->mPath.get());
}
@ -120,18 +118,20 @@ GetPathWithoutFileName(const nsCString& aFullPath)
}
void
MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
MozMtpDatabase::AddDirectory(MtpStorageID aStorageID,
const char* aPath,
MtpObjectHandle aParent)
{
ScopedCloseDir dir;
if (!(dir = PR_OpenDir(aDir))) {
MTP_ERR("Unable to open directory '%s'", aDir);
if (!(dir = PR_OpenDir(aPath))) {
MTP_ERR("Unable to open directory '%s'", aPath);
return;
}
PRDirEntry* dirEntry;
while ((dirEntry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
nsPrintfCString filename("%s/%s", aDir, dirEntry->name);
nsPrintfCString filename("%s/%s", aPath, dirEntry->name);
PRFileInfo64 fileInfo;
if (PR_GetFileInfo64(filename.get(), &fileInfo) != PR_SUCCESS) {
MTP_ERR("Unable to retrieve file information for '%s'", filename.get());
@ -140,7 +140,7 @@ MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mStorageID = aStorageID;
entry->mParent = aParent;
entry->mObjectName = dirEntry->name;
entry->mDisplayName = dirEntry->name;
@ -157,40 +157,59 @@ MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(filename.get(), entry->mHandle);
AddDirectory(aStorageID, filename.get(), entry->mHandle);
}
}
}
void
MozMtpDatabase::ReadVolume(const char *volumeName, const char *aDir)
MozMtpDatabase::AddStorage(MtpStorageID aStorageID,
const char* aPath,
const char* aName)
{
//TODO: Add an assert re thread being run on
PRFileInfo fileInfo;
if (PR_GetFileInfo(aDir, &fileInfo) != PR_SUCCESS) {
MTP_ERR("'%s' doesn't exist", aDir);
if (PR_GetFileInfo(aPath, &fileInfo) != PR_SUCCESS) {
MTP_ERR("'%s' doesn't exist", aPath);
return;
}
if (fileInfo.type != PR_FILE_DIRECTORY) {
MTP_ERR("'%s' isn't a directory", aDir);
MTP_ERR("'%s' isn't a directory", aPath);
return;
}
#if 0
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mStorageID = aStorageID;
entry->mParent = MTP_PARENT_ROOT;
entry->mObjectName = volumeName;
entry->mDisplayName = volumeName;
entry->mPath = aDir;
entry->mObjectName = aName;
entry->mDisplayName = aName;
entry->mPath = aPath;
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(aDir, entry->mHandle);
AddDirectory(aStorageID, aPath, entry->mHandle);
#else
AddDirectory(aStorageID, aPath, MTP_PARENT_ROOT);
#endif
}
void
MozMtpDatabase::RemoveStorage(MtpStorageID aStorageID)
{
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIndex;
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
RefPtr<DbEntry> entry = mDb[entryIndex];
if (entry && entry->mStorageID == aStorageID) {
mDb[entryIndex] = nullptr;
}
}
}
// called from SendObjectInfo to reserve a database entry for the incoming file
@ -232,12 +251,12 @@ MozMtpDatabase::beginSendObject(const char* aPath,
//virtual
void
MozMtpDatabase::endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool succeeded)
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool aSucceeded)
{
MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
if (!succeeded) {
if (!aSucceeded) {
RemoveEntry(aHandle);
}
}
@ -245,8 +264,8 @@ MozMtpDatabase::endSendObject(const char* aPath,
//virtual
MtpObjectHandleList*
MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("StorageID: 0x%08x Format: 0x%04x Parent: 0x%08x",
aStorageID, aFormat, aParent);
@ -257,11 +276,14 @@ MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
list = new MtpObjectHandleList();
// Note: objects in the topmost directory of each storage area have a parent
// of MTP_PARENT_ROOT. So we need to filter on storage ID as well.
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIndex;
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
RefPtr<DbEntry> entry = mDb[entryIndex];
if (entry && entry->mParent == aParent) {
if (entry && entry->mStorageID == aStorageID && entry->mParent == aParent) {
list->push(entry->mHandle);
}
}
@ -271,12 +293,23 @@ MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
//virtual
int
MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("");
return mDb.Length() - 1;
int count = 0;
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIndex;
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
RefPtr<DbEntry> entry = mDb[entryIndex];
if (entry && entry->mStorageID == aStorageID) {
count++;
}
}
return count;
}
//virtual
@ -344,8 +377,8 @@ MozMtpDatabase::getSupportedDeviceProperties()
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
@ -417,8 +450,8 @@ GetTypeOfObjectProp(MtpObjectProperty aProperty)
//virtual
MtpResponseCode
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x Property: 0x%08x", aHandle, aProperty);
@ -463,7 +496,7 @@ MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
//virtual
MtpResponseCode
MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
MtpDataPacket& aPacket)
{
MTP_LOG("(GENERAL ERROR)");
return MTP_RESPONSE_GENERAL_ERROR;
@ -472,7 +505,7 @@ MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
//virtual
MtpResponseCode
MozMtpDatabase::setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
MtpDataPacket& aPacket)
{
MTP_LOG("(NOT SUPPORTED)");
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
@ -567,13 +600,13 @@ MozMtpDatabase::QueryEntries(MozMtpDatabase::MatchType aMatchType,
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket)
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x Format: 0x%08x aProperty: 0x%08x aGroupCode: %d aDepth %d (NOT SUPPORTED)",
MTP_LOG("Handle: 0x%08x Format: 0x%08x aProperty: 0x%08x aGroupCode: %d aDepth %d",
aHandle, aFormat, aProperty, aGroupCode, aDepth);
if (aDepth > 1) {
@ -726,7 +759,7 @@ MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
//virtual
MtpResponseCode
MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo)
MtpObjectInfo& aInfo)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
@ -780,9 +813,9 @@ MozMtpDatabase::getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize)
//virtual
MtpResponseCode
MozMtpDatabase::getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat)
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
@ -811,6 +844,10 @@ MozMtpDatabase::deleteFile(MtpObjectHandle aHandle)
MTP_LOG("Handle: 0x%08x '%s'", aHandle, entry->mPath.get());
//TODO: MtpServer::doDeleteObject calls us, and then calls a private
// method (deletePath) which recursively deletes the path.
// We need to tell device storage that these files are gone
// File deletion will happen in lower level implementation.
// The only thing we need to do is removing the entry from the db.
RemoveEntry(aHandle);
@ -854,7 +891,7 @@ MozMtpDatabase::getObjectReferences(MtpObjectHandle aHandle)
//virtual
MtpResponseCode
MozMtpDatabase::setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences)
MtpObjectHandleList* aReferences)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;

View File

@ -22,7 +22,9 @@ using namespace android;
class MozMtpDatabase : public MtpDatabase
{
public:
MozMtpDatabase(const char *aDir);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpDatabase)
MozMtpDatabase();
virtual ~MozMtpDatabase();
// called from SendObjectInfo to reserve a database entry for the incoming file
@ -106,11 +108,14 @@ public:
virtual void sessionEnded();
void AddStorage(MtpStorageID aStorageID, const char* aPath, const char *aName);
void RemoveStorage(MtpStorageID aStorageID);
private:
struct DbEntry
{
NS_INLINE_DECL_REFCOUNTING(DbEntry)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DbEntry)
MtpObjectHandle mHandle; // uint32_t
MtpStorageID mStorageID; // uint32_t
@ -152,8 +157,7 @@ private:
return mDb.Length();
}
void ParseDirectory(const char *aDir, MtpObjectHandle aParent);
void ReadVolume(const char *aVolumeName, const char *aDir);
void AddDirectory(MtpStorageID aStorageID, const char *aPath, MtpObjectHandle aParent);
};
END_MTP_NAMESPACE

View File

@ -16,46 +16,34 @@
#include <unistd.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
#include "base/message_loop.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Scoped.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "Volume.h"
using namespace android;
using namespace mozilla;
USING_MTP_NAMESPACE
BEGIN_MTP_NAMESPACE
class MtpServerRunnable : public nsRunnable
{
public:
MtpServerRunnable(int aMtpUsbFd, MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer),
mMtpUsbFd(aMtpUsbFd)
{
}
nsresult Run()
{
const char *mtpUsbFilename = "/dev/mtp_usb";
const char *productName = "FirefoxOS";
const char *storageDir = "/storage/sdcard";
mFd = open(mtpUsbFilename, O_RDWR);
if (mFd.get() < 0) {
MTP_LOG("open of '%s' failed", mtpUsbFilename);
return NS_OK;
}
MTP_LOG("MozMtpServer open done, fd: %d. Start reading.", mFd.get());
ScopedDeletePtr<MozMtpDatabase> database;
ScopedDeletePtr<MtpServer> server;
ScopedDeletePtr<MtpStorage> storage;
database = new MozMtpDatabase(storageDir);
server = new MtpServer(mFd.get(), database, false, 1023, 0664, 0775);
storage = new MtpStorage(MTP_STORAGE_FIXED_RAM, // id
storageDir, // filePath
productName, // description
100uLL * 1024uLL * 1024uLL, // reserveSpace
false, // removable
2uLL * 1024uLL * 1024uLL * 1024uLL); // maxFileSize
server->addStorage(storage);
nsRefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
MTP_LOG("MozMtpServer started");
server->run();
@ -65,16 +53,51 @@ public:
}
private:
ScopedClose mFd;
nsRefPtr<MozMtpServer> mMozMtpServer;
ScopedClose mMtpUsbFd; // We want to hold this open while the server runs
};
already_AddRefed<RefCountedMtpServer>
MozMtpServer::GetMtpServer()
{
nsRefPtr<RefCountedMtpServer> server = mMtpServer;
return server.forget();
}
already_AddRefed<MozMtpDatabase>
MozMtpServer::GetMozMtpDatabase()
{
nsRefPtr<MozMtpDatabase> db = mMozMtpDatabase;
return db.forget();
}
void
MozMtpServer::Run()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
const char *mtpUsbFilename = "/dev/mtp_usb";
ScopedClose mtpUsbFd(open(mtpUsbFilename, O_RDWR));
if (mtpUsbFd.get() < 0) {
MTP_ERR("open of '%s' failed", mtpUsbFilename);
return;
}
MTP_LOG("Opened '%s' fd %d", mtpUsbFilename, mtpUsbFd.get());
mMozMtpDatabase = new MozMtpDatabase();
mMtpServer = new RefCountedMtpServer(mtpUsbFd.get(), // fd
mMozMtpDatabase.get(), // MtpDatabase
false, // ptp?
AID_MEDIA_RW, // file group
0664, // file permissions
0775); // dir permissions
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
MOZ_ASSERT(mServerThread);
mServerThread->Dispatch(new MtpServerRunnable(), 0);
mServerThread->Dispatch(new MtpServerRunnable(mtpUsbFd.forget(), this), NS_DISPATCH_NORMAL);
}
END_MTP_NAMESPACE

View File

@ -8,20 +8,48 @@
#define mozilla_system_mozmtpserver_h__
#include "MozMtpCommon.h"
#include "MozMtpDatabase.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
namespace mozilla {
namespace system {
class Volume;
}
}
BEGIN_MTP_NAMESPACE
using namespace android;
class RefCountedMtpServer : public MtpServer
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMtpServer)
RefCountedMtpServer(int aFd, MtpDatabase* aDatabase, bool aPtp,
int aFileGroup, int aFilePerm, int aDirectoryPerm)
: MtpServer(aFd, aDatabase, aPtp, aFileGroup, aFilePerm, aDirectoryPerm)
{
}
};
class MozMtpServer
{
public:
NS_INLINE_DECL_REFCOUNTING(MozMtpServer)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpServer)
void Run();
// void UpdateStorage(android::MtpStorageID id, Volume *vol);
already_AddRefed<RefCountedMtpServer> GetMtpServer();
already_AddRefed<MozMtpDatabase> GetMozMtpDatabase();
private:
nsRefPtr<RefCountedMtpServer> mMtpServer;
nsRefPtr<MozMtpDatabase> mMozMtpDatabase;
nsCOMPtr<nsIThread> mServerThread;
};

View File

@ -0,0 +1,133 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "MozMtpStorage.h"
#include "MozMtpDatabase.h"
#include "MozMtpServer.h"
#include "base/message_loop.h"
#include "nsXULAppAPI.h"
BEGIN_MTP_NAMESPACE
using namespace android;
MozMtpStorage::MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer)
: mMozMtpServer(aMozMtpServer),
mVolume(aVolume)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
// The MtpStorageID has the physical volume in the top 16 bits, and the
// logical volumein the lower 16 bits. We treat each volume as a separate
// phsyical storage;
mStorageID = mVolume->Id() << 16 | 1;
MTP_LOG("Storage constructed for Volume %s mStorageID 0x%08x",
aVolume->NameStr(), mStorageID);
Volume::RegisterObserver(this);
// Get things in sync
Notify(mVolume);
}
MozMtpStorage::~MozMtpStorage()
{
MTP_LOG("Storage destructed for Volume %s mStorageID 0x%08x",
mVolume->NameStr(), mStorageID);
Volume::UnregisterObserver(this);
if (mMtpStorage) {
StorageUnavailable();
}
}
// virtual
void
MozMtpStorage::Notify(Volume* const& aVolume)
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
if (aVolume != mVolume) {
// Not our volume
return;
}
Volume::STATE volState = aVolume->State();
MTP_LOG("Volume %s mStorageID 0x%08x state changed to %s SharingEnabled: %d",
aVolume->NameStr(), mStorageID, aVolume->StateStr(),
aVolume->IsSharingEnabled());
if (mMtpStorage) {
if (volState != nsIVolume::STATE_MOUNTED || !aVolume->IsSharingEnabled()) {
// The volume is no longer accessible. We need to remove this storage
// from the MTP server
StorageUnavailable();
}
} else {
if (volState == nsIVolume::STATE_MOUNTED && aVolume->IsSharingEnabled()) {
// The volume is accessible. Tell the MTP server.
StorageAvailable();
}
}
}
void
MozMtpStorage::StorageAvailable()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
nsCString mountPoint = mVolume->MountPoint();
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MozMtpDatabase",
mVolume->NameStr(), mStorageID, mountPoint.get());
nsRefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
db->AddStorage(mStorageID, mountPoint.get(), mVolume->NameStr());
MOZ_ASSERT(!mMtpStorage);
//TODO: For now we assume that the storage removable unless we're sure it's
// not. Bug 1033952 will add an isRemovable attribute to the Volume
// and then we'll know properly.
//TODO: Figure out what to do about maxFileSize.
mMtpStorage.reset(new MtpStorage(mStorageID, // id
mountPoint.get(), // filePath
mVolume->NameStr(), // description
1024uLL * 1024uLL, // reserveSpace
true, // removable
2uLL * 1024uLL * 1024uLL * 1024uLL)); // maxFileSize
nsRefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
MTP_LOG("Adding Volume %s mStorageID 0x%08x mountPoint %s to MtpServer",
mVolume->NameStr(), mStorageID, mountPoint.get());
server->addStorage(mMtpStorage.get());
}
void
MozMtpStorage::StorageUnavailable()
{
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
MOZ_ASSERT(mMtpStorage);
MTP_LOG("Removing mStorageID 0x%08x from MtpServer", mStorageID);
nsRefPtr<RefCountedMtpServer> server = mMozMtpServer->GetMtpServer();
server->removeStorage(mMtpStorage.get());
MTP_LOG("Removing mStorageID 0x%08x from MozMtpDatabse", mStorageID);
nsRefPtr<MozMtpDatabase> db = mMozMtpServer->GetMozMtpDatabase();
db->RemoveStorage(mStorageID);
mMtpStorage = nullptr;
}
END_MTP_NAMESPACE

View File

@ -0,0 +1,48 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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_mozmtpstorage_h__
#define mozilla_system_mozmtpstorage_h__
#include "MozMtpCommon.h"
#include "nsAutoPtr.h"
#include "mozilla/UniquePtr.h"
#include "Volume.h"
BEGIN_MTP_NAMESPACE
using namespace android;
class MozMtpServer;
class MozMtpStorage : public Volume::EventObserver
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozMtpStorage)
MozMtpStorage(Volume* aVolume, MozMtpServer* aMozMtpServer);
typedef nsTArray<nsRefPtr<MozMtpStorage> > Array;
private:
virtual ~MozMtpStorage();
virtual void Notify(Volume* const& aEvent);
void StorageAvailable();
void StorageUnavailable();
nsRefPtr<MozMtpServer> mMozMtpServer;
UniquePtr<MtpStorage> mMtpStorage;
RefPtr<Volume> mVolume;
MtpStorageID mStorageID;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpstorage_h__

View File

@ -50,6 +50,8 @@ Volume::EventObserverList Volume::mEventObserverList;
// be locked until we get our first update from nsVolume (MainThread).
static int32_t sMountGeneration = 0;
static uint32_t sNextId = 1;
// We don't get media inserted/removed events at startup. So we
// assume it's present, and we'll be told that it's missing.
Volume::Volume(const nsCSubstring& aName)
@ -64,7 +66,8 @@ Volume::Volume(const nsCSubstring& aName)
mUnmountRequested(false),
mCanBeShared(true),
mIsSharing(false),
mIsFormatting(false)
mIsFormatting(false),
mId(sNextId++)
{
DBG("Volume %s: created", NameStr());
}
@ -140,6 +143,7 @@ Volume::SetSharingEnabled(bool aSharingEnabled)
LOG("SetSharingMode for volume %s to %d canBeShared = %d",
NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
mEventObserverList.Broadcast(this);
}
void
@ -179,14 +183,14 @@ Volume::SetState(Volume::STATE aNewState)
}
if (aNewState == nsIVolume::STATE_MOUNTED) {
mMountGeneration = ++sMountGeneration;
LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) "
LOG("Volume %s (%u): changing state from %s to %s @ '%s' (%d observers) "
"mountGeneration = %d, locked = %d",
NameStr(), StateStr(mState),
NameStr(), mId, StateStr(mState),
StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length(),
mMountGeneration, (int)mMountLocked);
} else {
LOG("Volume %s: changing state from %s to %s (%d observers)",
NameStr(), StateStr(mState),
LOG("Volume %s (%u): changing state from %s to %s (%d observers)",
NameStr(), mId, StateStr(mState),
StateStr(aNewState), mEventObserverList.Length());
}

View File

@ -44,6 +44,8 @@ public:
// (i.e. path that leads to the files stored on the volume).
const nsCString& MountPoint() const { return mMountPoint; }
uint32_t Id() const { return mId; }
int32_t MountGeneration() const { return mMountGeneration; }
bool IsMountLocked() const { return mMountLocked; }
bool MediaPresent() const { return mMediaPresent; }
@ -110,6 +112,7 @@ private:
bool mCanBeShared;
bool mIsSharing;
bool mIsFormatting;
uint32_t mId; // Unique ID (used by MTP)
static EventObserverList mEventObserverList;
};

View File

@ -43,6 +43,7 @@ SOURCES += [
'GonkGPSGeolocationProvider.cpp',
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'MozMtpStorage.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',
'nsVolume.cpp',