Bug 1029533 - Initial standup of MTP server. r=echou

Note: This will be off by default. Currently, you need to do:

adb shell setprop sys.usb.config mtp,adb

in order to activate the MTP server.
This commit is contained in:
Dave Hylands 2014-07-11 14:42:15 -07:00
parent 7f4e99ce44
commit 7324f2a626
8 changed files with 1171 additions and 14 deletions

View File

@ -25,6 +25,7 @@
#include "mozilla/FileUtils.h"
#include "mozilla/Hal.h"
#include "mozilla/StaticPtr.h"
#include "MozMtpServer.h"
#include "nsAutoPtr.h"
#include "nsMemory.h"
#include "nsString.h"
@ -35,6 +36,7 @@
#include "VolumeManager.h"
using namespace mozilla::hal;
USING_MTP_NAMESPACE
/**************************************************************************
*
@ -69,6 +71,7 @@ using namespace mozilla::hal;
#define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions"
#define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage"
#define ICS_SYS_MTP_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mtp"
#define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state"
#define USE_DEBUG 0
@ -229,6 +232,9 @@ public:
}
}
void StartMtpServer();
void StopMtpServer();
void UpdateState();
const char* ModeStr(int32_t aMode)
@ -347,6 +353,7 @@ private:
};
static StaticRefPtr<AutoMounter> sAutoMounter;
static StaticRefPtr<MozMtpServer> sMozMtpServer;
/***************************************************************************/
@ -398,6 +405,25 @@ AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand)
}
}
void
AutoMounter::StartMtpServer()
{
if (sMozMtpServer) {
// Mtp Server is already running - nothing to do
return;
}
LOG("Starting MtpServer");
sMozMtpServer = new MozMtpServer();
sMozMtpServer->Run();
}
void
AutoMounter::StopMtpServer()
{
LOG("Stopping MtpServer");
sMozMtpServer = nullptr;
}
/***************************************************************************/
void
@ -441,22 +467,23 @@ AutoMounter::UpdateState()
bool umsAvail = false;
bool umsEnabled = false;
bool mtpAvail = false;
bool mtpEnabled = false;
if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
char functionsStr[60];
if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
functionsStr[0] = '\0';
}
umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
if (umsAvail) {
char functionsStr[60];
if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
} else {
ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
umsEnabled = false;
}
} else {
umsEnabled = false;
umsEnabled = strstr(functionsStr, "mass_storage") != nullptr;
}
mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
if (mtpAvail) {
mtpEnabled = strstr(functionsStr, "mtp") != nullptr;
}
} else {
umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled);
}
bool usbCablePluggedIn = IsUsbCablePluggedIn();
@ -469,9 +496,19 @@ AutoMounter::UpdateState()
}
}
bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn);
LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare);
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);
if (mtpAvail && mtpEnabled) {
if (enabled && usbCablePluggedIn) {
StartMtpServer();
} else {
StopMtpServer();
}
return;
}
bool filesOpen = false;
static unsigned filesOpenDelayCount = 0;
@ -902,6 +939,9 @@ AutoMounterUnmountVolume(const nsCString& aVolumeName)
void
ShutdownAutoMounter()
{
if (sAutoMounter) {
sAutoMounter->StopMtpServer();
}
sAutoMounterSetting = nullptr;
sUsbCableObserver = nullptr;

View File

@ -0,0 +1,44 @@
/* -*- 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_mozmtpcommon_h__
#define mozilla_system_mozmtpcommon_h__
#include "mozilla/Types.h"
#include <android/log.h>
#define MTP_LOG(msg, ...) \
__android_log_print(ANDROID_LOG_INFO, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
#define MTP_ERR(msg, ...) \
__android_log_print(ANDROID_LOG_ERROR, "MozMtp", \
"%s: " msg, __FUNCTION__, ##__VA_ARGS__) \
#define BEGIN_MTP_NAMESPACE \
namespace mozilla { namespace system { namespace mtp {
#define END_MTP_NAMESPACE \
} /* namespace mtp */ } /* namespace system */ } /* namespace mozilla */
#define USING_MTP_NAMESPACE \
using namespace mozilla::system::mtp;
namespace android {
class MOZ_EXPORT MtpServer;
class MOZ_EXPORT MtpStorage;
class MOZ_EXPORT MtpDatabase;
class MOZ_EXPORT MtpDataPacket;
class MOZ_EXPORT MtpProperty;
}
#include <mtp.h>
#include <MtpDatabase.h>
#include <MtpObjectInfo.h>
#include <MtpProperty.h>
#include <MtpServer.h>
#include <MtpStorage.h>
#include <MtpTypes.h>
#endif // mozilla_system_mtpcommon_h__

View File

@ -0,0 +1,792 @@
/* -*- 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 "MozMtpDatabase.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Scoped.h"
#include "nsIFile.h"
#include "nsPrintfCString.h"
#include "prio.h"
#include <dirent.h>
#include <libgen.h>
using namespace android;
using namespace mozilla;
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCloseDir, PRDir, PR_CloseDir)
}
BEGIN_MTP_NAMESPACE
static const char *
ObjectPropertyAsStr(MtpObjectProperty aProperty)
{
switch (aProperty) {
case MTP_PROPERTY_STORAGE_ID: return "MTP_PROPERTY_STORAGE_ID";
case MTP_PROPERTY_OBJECT_FORMAT: return "MTP_PROPERTY_OBJECT_FORMAT";
case MTP_PROPERTY_OBJECT_SIZE: return "MTP_PROPERTY_OBJECT_SIZE";
case MTP_PROPERTY_WIDTH: return "MTP_PROPERTY_WIDTH";
case MTP_PROPERTY_HEIGHT: return "MTP_PROPERTY_HEIGHT";
case MTP_PROPERTY_IMAGE_BIT_DEPTH: return "MTP_PROPERTY_IMAGE_BIT_DEPTH";
case MTP_PROPERTY_DISPLAY_NAME: return "MTP_PROPERTY_DISPLAY_NAME";
}
return "MTP_PROPERTY_???";
}
MozMtpDatabase::MozMtpDatabase(const char *aDir)
{
MTP_LOG("");
// We use the index into the array as the handle. Since zero isn't a valid
// index, we stick a dummy entry there.
RefPtr<DbEntry> dummy;
mDb.AppendElement(dummy);
ReadVolume("sdcard", aDir);
}
//virtual
MozMtpDatabase::~MozMtpDatabase()
{
MTP_LOG("");
}
void
MozMtpDatabase::AddEntry(DbEntry *entry)
{
entry->mHandle = GetNextHandle();
MOZ_ASSERT(mDb.Length() == entry->mHandle);
mDb.AppendElement(entry);
MTP_LOG("AddEntry: Handle: 0x%08x Parent: 0x%08x Path:'%s'",
entry->mHandle, entry->mParent, entry->mPath.get());
}
TemporaryRef<MozMtpDatabase::DbEntry>
MozMtpDatabase::GetEntry(MtpObjectHandle aHandle)
{
RefPtr<DbEntry> entry;
if (aHandle > 0 && aHandle < mDb.Length()) {
entry = mDb[aHandle];
}
return entry;
}
void
MozMtpDatabase::RemoveEntry(MtpObjectHandle aHandle)
{
if (aHandle > 0 && aHandle < mDb.Length()) {
mDb[aHandle] = nullptr;
}
}
nsCString
MozMtpDatabase::BaseName(const nsCString& path)
{
nsCOMPtr<nsIFile> file;
NS_NewNativeLocalFile(path, false, getter_AddRefs(file));
if (file) {
nsCString leafName;
file->GetNativeLeafName(leafName);
return leafName;
}
return path;
}
void
MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
{
ScopedCloseDir dir;
if (!(dir = PR_OpenDir(aDir))) {
MTP_ERR("Unable to open directory '%s'", aDir);
return;
}
PRDirEntry* dirEntry;
while ((dirEntry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
nsPrintfCString filename("%s/%s", aDir, dirEntry->name);
PRFileInfo64 fileInfo;
if (PR_GetFileInfo64(filename.get(), &fileInfo) != PR_SUCCESS) {
MTP_ERR("Unable to retrieve file information for '%s'", filename.get());
continue;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mParent = aParent;
entry->mObjectName = dirEntry->name;
entry->mDisplayName = dirEntry->name;
entry->mPath = filename;
entry->mDateCreated = fileInfo.creationTime;
entry->mDateModified = fileInfo.modifyTime;
if (fileInfo.type == PR_FILE_FILE) {
entry->mObjectFormat = MTP_FORMAT_DEFINED;
//TODO: Check how 64-bit filesize are dealt with
entry->mObjectSize = fileInfo.size;
AddEntry(entry);
} else if (fileInfo.type == PR_FILE_DIRECTORY) {
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(filename.get(), entry->mHandle);
}
}
}
void
MozMtpDatabase::ReadVolume(const char *volumeName, const char *aDir)
{
//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);
return;
}
if (fileInfo.type != PR_FILE_DIRECTORY) {
MTP_ERR("'%s' isn't a directory", aDir);
return;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = MTP_STORAGE_FIXED_RAM;
entry->mParent = MTP_PARENT_ROOT;
entry->mObjectName = volumeName;
entry->mDisplayName = volumeName;
entry->mPath = aDir;
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
ParseDirectory(aDir, entry->mHandle);
}
// called from SendObjectInfo to reserve a database entry for the incoming file
//virtual
MtpObjectHandle
MozMtpDatabase::beginSendObject(const char* aPath,
MtpObjectFormat aFormat,
MtpObjectHandle aParent,
MtpStorageID aStorageID,
uint64_t aSize,
time_t aModified)
{
if (!aParent) {
MTP_LOG("aParent is NULL");
return kInvalidObjectHandle;
}
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = aStorageID;
entry->mParent = aParent;
entry->mPath = aPath;
entry->mObjectName = BaseName(entry->mPath);
entry->mDisplayName = entry->mObjectName;
entry->mObjectFormat = aFormat;
entry->mObjectSize = aSize;
AddEntry(entry);
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s'", entry->mHandle, aParent, aPath);
return entry->mHandle;
}
// called to report success or failure of the SendObject file transfer
// success should signal a notification of the new object's creation,
// failure should remove the database entry created in beginSendObject
//virtual
void
MozMtpDatabase::endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool succeeded)
{
MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
if (!succeeded) {
RemoveEntry(aHandle);
}
}
//virtual
MtpObjectHandleList*
MozMtpDatabase::getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("StorageID: 0x%08x Format: 0x%04x Parent: 0x%08x",
aStorageID, aFormat, aParent);
//TODO: Optimize
ScopedDeletePtr<MtpObjectHandleList> list;
list = new MtpObjectHandleList();
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIndex;
for (entryIndex = 1; entryIndex < numEntries; entryIndex++) {
RefPtr<DbEntry> entry = mDb[entryIndex];
if (entry->mParent == aParent) {
list->push(entry->mHandle);
}
}
return list.forget();
}
//virtual
int
MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("");
return mDb.Length() - 1;
}
//virtual
MtpObjectFormatList*
MozMtpDatabase::getSupportedPlaybackFormats()
{
static const uint16_t init_data[] = {MTP_FORMAT_PNG};
MtpObjectFormatList *list = new MtpObjectFormatList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
MTP_LOG("returning MTP_FORMAT_PNG");
return list;
}
//virtual
MtpObjectFormatList*
MozMtpDatabase::getSupportedCaptureFormats()
{
static const uint16_t init_data[] = {MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
MtpObjectFormatList *list = new MtpObjectFormatList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
MTP_LOG("returning MTP_FORMAT_PNG");
return list;
}
static const MtpObjectProperty sSupportedObjectProperties[] =
{
MTP_PROPERTY_STORAGE_ID,
MTP_PROPERTY_PARENT_OBJECT,
MTP_PROPERTY_OBJECT_FORMAT,
MTP_PROPERTY_OBJECT_SIZE,
MTP_PROPERTY_OBJECT_FILE_NAME, // just the filename - no directory
MTP_PROPERTY_PROTECTION_STATUS, // UINT16 - always 0
MTP_PROPERTY_DATE_MODIFIED,
MTP_PROPERTY_DATE_ADDED,
};
//virtual
MtpObjectPropertyList*
MozMtpDatabase::getSupportedObjectProperties(MtpObjectFormat aFormat)
{
MTP_LOG("");
MtpObjectPropertyList *list = new MtpObjectPropertyList();
list->appendArray(sSupportedObjectProperties,
MOZ_ARRAY_LENGTH(sSupportedObjectProperties));
return list;
}
//virtual
MtpDevicePropertyList*
MozMtpDatabase::getSupportedDeviceProperties()
{
MTP_LOG("");
static const uint16_t init_data[] = { MTP_DEVICE_PROPERTY_UNDEFINED };
MtpDevicePropertyList *list = new MtpDevicePropertyList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
return list;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Invalid Handle: 0x%08x", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x '%s' Property: %s 0x%08x",
aHandle, entry->mDisplayName.get(), ObjectPropertyAsStr(aProperty), aProperty);
switch (aProperty)
{
case MTP_PROPERTY_STORAGE_ID: aPacket.putUInt32(entry->mStorageID); break;
case MTP_PROPERTY_PARENT_OBJECT: aPacket.putUInt32(entry->mParent); break;
case MTP_PROPERTY_OBJECT_FORMAT: aPacket.putUInt32(entry->mObjectFormat); break;
case MTP_PROPERTY_OBJECT_SIZE: aPacket.putUInt32(entry->mObjectSize); break;
case MTP_PROPERTY_DISPLAY_NAME: aPacket.putString(entry->mDisplayName.get()); break;
default:
MTP_LOG("Invalid Property: 0x%08x", aProperty);
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
}
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpResponseCode
MozMtpDatabase::getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("(GENERAL ERROR)");
return MTP_RESPONSE_GENERAL_ERROR;
}
//virtual
MtpResponseCode
MozMtpDatabase::setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("(NOT SUPPORTED)");
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpResponseCode
MozMtpDatabase::resetDeviceProperty(MtpDeviceProperty aProperty)
{
MTP_LOG("(NOT SUPPORTED)");
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
void
MozMtpDatabase::QueryEntries(MozMtpDatabase::MatchType aMatchType,
uint32_t aMatchField1,
uint32_t aMatchField2,
DbArray &result)
{
DbArray::size_type numEntries = mDb.Length();
DbArray::index_type entryIdx;
RefPtr<DbEntry> entry;
result.Clear();
switch (aMatchType) {
case MatchAll:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
if (mDb[entryIdx]) {
result.AppendElement(mDb[entryIdx]);
}
}
break;
case MatchHandle:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mHandle == aMatchField1) {
result.AppendElement(entry);
// Handles are unique - return the one that we found.
return;
}
}
break;
case MatchParent:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mParent == aMatchField1) {
result.AppendElement(entry);
}
}
break;
case MatchFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mObjectFormat == aMatchField1) {
result.AppendElement(entry);
}
}
break;
case MatchHandleFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mHandle == aMatchField1) {
if (entry->mObjectFormat == aMatchField2) {
result.AppendElement(entry);
}
// Only 1 entry can match my aHandle. So we can return early.
return;
}
}
break;
case MatchParentFormat:
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
entry = mDb[entryIdx];
if (entry && entry->mParent == aMatchField1 && entry->mObjectFormat == aMatchField2) {
result.AppendElement(entry);
}
}
break;
default:
MOZ_ASSERT(!"Invalid MatchType");
}
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
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)",
aHandle, aFormat, aProperty, aGroupCode, aDepth);
if (aDepth > 1) {
return MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED;
}
if (aGroupCode != 0) {
return MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED;
}
MatchType matchType = MatchAll;
uint32_t matchField1 = 0;
uint32_t matchField2 = 0;
// aHandle == 0 implies all objects at the root level
// further specificed by aFormat and/or aDepth
if (aFormat == 0) {
if (aHandle == 0xffffffff) {
// select all objects
matchType = MatchAll;
} else {
if (aDepth == 1) {
// select objects whose Parent matches aHandle
matchType = MatchParent;
matchField1 = aHandle;
} else {
// select object whose handle matches aHandle
matchType = MatchHandle;
matchField1 = aHandle;
}
}
} else {
if (aHandle == 0xffffffff) {
// select all objects whose format matches aFormat
matchType = MatchFormat;
matchField1 = aFormat;
} else {
if (aDepth == 1) {
// select objects whose Parent is aHandle and format matches aFormat
matchType = MatchParentFormat;
matchField1 = aHandle;
matchField2 = aFormat;
} else {
// select objects whose handle is aHandle and format matches aFormat
matchType = MatchHandleFormat;
matchField1 = aHandle;
matchField2 = aFormat;
}
}
}
DbArray result;
QueryEntries(matchType, matchField1, matchField2, result);
const MtpObjectProperty *objectPropertyList;
size_t numObjectProperties = 0;
MtpObjectProperty objectProperty;
if (aProperty == 0xffffffff) {
// return all supported properties
numObjectProperties = MOZ_ARRAY_LENGTH(sSupportedObjectProperties);
objectPropertyList = sSupportedObjectProperties;
} else {
// return property indicated by aProperty
numObjectProperties = 1;
objectProperty = aProperty;
objectPropertyList = &objectProperty;
}
DbArray::size_type numEntries = result.Length();
DbArray::index_type entryIdx;
aPacket.putUInt32(numEntries);
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
RefPtr<DbEntry> entry = result[entryIdx];
for (size_t propertyIdx = 0; propertyIdx < numObjectProperties; propertyIdx++) {
aPacket.putUInt32(entry->mHandle);
MtpObjectProperty prop = objectPropertyList[propertyIdx];
aPacket.putUInt16(prop);
switch (prop) {
case MTP_PROPERTY_STORAGE_ID:
aPacket.putUInt16(MTP_TYPE_UINT32);
aPacket.putUInt32(entry->mStorageID);
break;
case MTP_PROPERTY_PARENT_OBJECT:
aPacket.putUInt16(MTP_TYPE_UINT32);
aPacket.putUInt32(entry->mParent);
break;
case MTP_PROPERTY_OBJECT_FORMAT:
aPacket.putUInt16(MTP_TYPE_UINT16);
aPacket.putUInt16(entry->mObjectFormat);
break;
case MTP_PROPERTY_OBJECT_SIZE:
aPacket.putUInt16(MTP_TYPE_UINT64);
aPacket.putUInt64(entry->mObjectSize);
break;
case MTP_PROPERTY_OBJECT_FILE_NAME:
aPacket.putUInt16(MTP_TYPE_STR);
aPacket.putString(entry->mObjectName.get());
break;
case MTP_PROPERTY_PROTECTION_STATUS:
aPacket.putUInt16(MTP_TYPE_UINT16);
aPacket.putUInt16(0); // 0 = No Protection
break;
case MTP_PROPERTY_DATE_MODIFIED: {
aPacket.putUInt16(MTP_TYPE_STR);
PRExplodedTime explodedTime;
PR_ExplodeTime(entry->mDateModified, PR_LocalTimeParameters, &explodedTime);
char dateStr[20];
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
aPacket.putString(dateStr);
break;
}
case MTP_PROPERTY_DATE_ADDED: {
aPacket.putUInt16(MTP_TYPE_STR);
PRExplodedTime explodedTime;
PR_ExplodeTime(entry->mDateCreated, PR_LocalTimeParameters, &explodedTime);
char dateStr[20];
PR_FormatTime(dateStr, sizeof(dateStr), "%Y%m%dT%H%M%S", &explodedTime);
aPacket.putString(dateStr);
break;
}
default:
MTP_ERR("Unrecognized property code: %u", prop);
return MTP_RESPONSE_GENERAL_ERROR;
}
}
}
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Handle 0x%08x is invalid", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x Display:'%s' Object:'%s'", aHandle, entry->mDisplayName.get(), entry->mObjectName.get());
aInfo.mHandle = aHandle;
aInfo.mStorageID = entry->mStorageID;
aInfo.mFormat = entry->mObjectFormat;
aInfo.mProtectionStatus = 0x0;
aInfo.mCompressedSize = 0;
aInfo.mThumbFormat = entry->mObjectFormat;
aInfo.mThumbCompressedSize = 20*20*4;
aInfo.mThumbPixWidth = 20;
aInfo.mThumbPixHeight =20;
aInfo.mImagePixWidth = 20;
aInfo.mImagePixHeight = 20;
aInfo.mImagePixDepth = 4;
aInfo.mParent = entry->mParent;
aInfo.mAssociationType = 0;
aInfo.mAssociationDesc = 0;
aInfo.mSequenceNumber = 0;
aInfo.mName = ::strdup(entry->mObjectName.get());
aInfo.mDateCreated = 0;
aInfo.mDateModified = 0;
aInfo.mKeywords = ::strdup("fxos,touch");
return MTP_RESPONSE_OK;
}
//virtual
void*
MozMtpDatabase::getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize)
{
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
aOutThumbSize = 0;
return nullptr;
}
//virtual
MtpResponseCode
MozMtpDatabase::getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat)
{
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Handle 0x%08x is invalid", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MTP_LOG("Handle: 0x%08x FilePath: '%s'", aHandle, entry->mPath.get());
aOutFilePath = entry->mPath.get();
aOutFileLength = entry->mObjectSize;
aOutFormat = entry->mObjectFormat;
return MTP_RESPONSE_OK;
}
//virtual
MtpResponseCode
MozMtpDatabase::deleteFile(MtpObjectHandle aHandle)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
//TODO
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
#if 0
//virtual
MtpResponseCode
MozMtpDatabase::moveFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
{
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
// change parent
return MTP_RESPONSE_OK
}
//virtual
MtpResponseCode
MozMtpDatabase::copyFile(MtpObjectHandle aHandle, MtpObjectHandle aNewParent)
{
MTP_LOG("Handle: 0x%08x NewParent: 0x%08x", aHandle, aNewParent);
// duplicate DbEntry
// change parent
return MTP_RESPONSE_OK
}
#endif
//virtual
MtpObjectHandleList*
MozMtpDatabase::getObjectReferences(MtpObjectHandle aHandle)
{
MTP_LOG("Handle: 0x%08x (returning nullptr)", aHandle);
return nullptr;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
}
//virtual
MtpProperty*
MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
MtpObjectFormat aFormat)
{
MTP_LOG("Property: %s 0x%08x", ObjectPropertyAsStr(aProperty), aProperty);
// TODO: Perhaps Filesize should be 64-bit?
MtpProperty* result = nullptr;
switch (aProperty)
{
case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_WIDTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_HEIGHT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_DISPLAY_NAME: result = new MtpProperty(aProperty, MTP_TYPE_STR); break;
default:
break;
}
return result;
}
//virtual
MtpProperty*
MozMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty aProperty)
{
MTP_LOG("(returning MTP_DEVICE_PROPERTY_UNDEFINED)");
return new MtpProperty(MTP_DEVICE_PROPERTY_UNDEFINED, MTP_TYPE_UNDEFINED);
}
//virtual
void
MozMtpDatabase::sessionStarted()
{
MTP_LOG("");
}
//virtual
void
MozMtpDatabase::sessionEnded()
{
MTP_LOG("");
}
END_MTP_NAMESPACE

View File

@ -0,0 +1,161 @@
/* -*- 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_mozmtpdatabase_h__
#define mozilla_system_mozmtpdatabase_h__
#include "MozMtpCommon.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
BEGIN_MTP_NAMESPACE // mozilla::system::mtp
using namespace android;
class MozMtpDatabase : public MtpDatabase
{
public:
MozMtpDatabase(const char *aDir);
virtual ~MozMtpDatabase();
// called from SendObjectInfo to reserve a database entry for the incoming file
virtual MtpObjectHandle beginSendObject(const char* aPath,
MtpObjectFormat aFormat,
MtpObjectHandle aParent,
MtpStorageID aStorageID,
uint64_t aSize,
time_t aModified);
// called to report success or failure of the SendObject file transfer
// success should signal a notification of the new object's creation,
// failure should remove the database entry created in beginSendObject
virtual void endSendObject(const char* aPath,
MtpObjectHandle aHandle,
MtpObjectFormat aFormat,
bool aSucceeded);
virtual MtpObjectHandleList* getObjectList(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual int getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent);
virtual MtpObjectFormatList* getSupportedPlaybackFormats();
virtual MtpObjectFormatList* getSupportedCaptureFormats();
virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat aFormat);
virtual MtpDevicePropertyList* getSupportedDeviceProperties();
virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty aProperty,
MtpDataPacket& aPacket);
virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty aProperty);
virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle aHandle,
uint32_t aFormat,
uint32_t aProperty,
int aGroupCode,
int aDepth,
MtpDataPacket& aPacket);
virtual MtpResponseCode getObjectInfo(MtpObjectHandle aHandle,
MtpObjectInfo& aInfo);
virtual void* getThumbnail(MtpObjectHandle aHandle, size_t& aOutThumbSize);
virtual MtpResponseCode getObjectFilePath(MtpObjectHandle aHandle,
MtpString& aOutFilePath,
int64_t& aOutFileLength,
MtpObjectFormat& aOutFormat);
virtual MtpResponseCode deleteFile(MtpObjectHandle aHandle);
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle aHandle);
virtual MtpResponseCode setObjectReferences(MtpObjectHandle aHandle,
MtpObjectHandleList* aReferences);
virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty aProperty,
MtpObjectFormat aFormat);
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty aProperty);
virtual void sessionStarted();
virtual void sessionEnded();
private:
struct DbEntry
{
NS_INLINE_DECL_REFCOUNTING(DbEntry)
MtpObjectHandle mHandle; // uint32_t
MtpStorageID mStorageID; // uint32_t
nsCString mObjectName;
MtpObjectFormat mObjectFormat; // uint16_t
MtpObjectHandle mParent; // uint32_t
uint64_t mObjectSize;
nsCString mDisplayName;
nsCString mPath;
PRTime mDateCreated;
PRTime mDateModified;
};
typedef nsTArray<mozilla::RefPtr<DbEntry> > DbArray;
DbArray mDb;
enum MatchType
{
MatchAll,
MatchHandle,
MatchParent,
MatchFormat,
MatchHandleFormat,
MatchParentFormat,
};
void AddEntry(DbEntry *aEntry);
mozilla::TemporaryRef<DbEntry> GetEntry(MtpObjectHandle aHandle);
void RemoveEntry(MtpObjectHandle aHandle);
void QueryEntries(MatchType aMatchType, uint32_t aMatchField1,
uint32_t aMatchField2, DbArray& aResult);
nsCString BaseName(const nsCString& aPath);
MtpObjectHandle GetNextHandle()
{
return mDb.Length();
}
void ParseDirectory(const char *aDir, MtpObjectHandle aParent);
void ReadVolume(const char *aVolumeName, const char *aDir);
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpdatabase_h__

View File

@ -0,0 +1,80 @@
/* -*- 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 "MozMtpServer.h"
#include "MozMtpDatabase.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <cutils/properties.h>
#include "mozilla/FileUtils.h"
#include "mozilla/Scoped.h"
#include "nsThreadUtils.h"
using namespace android;
using namespace mozilla;
USING_MTP_NAMESPACE
class MtpServerRunnable : public nsRunnable
{
public:
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);
MTP_LOG("MozMtpServer started");
server->run();
MTP_LOG("MozMtpServer finished");
return NS_OK;
}
private:
ScopedClose mFd;
};
void
MozMtpServer::Run()
{
nsresult rv = NS_NewNamedThread("MtpServer", getter_AddRefs(mServerThread));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
MOZ_ASSERT(mServerThread);
mServerThread->Dispatch(new MtpServerRunnable(), 0);
}

View File

@ -0,0 +1,32 @@
/* -*- 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_mozmtpserver_h__
#define mozilla_system_mozmtpserver_h__
#include "MozMtpCommon.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
BEGIN_MTP_NAMESPACE
class MozMtpServer
{
public:
NS_INLINE_DECL_REFCOUNTING(MozMtpServer)
void Run();
private:
nsCOMPtr<nsIThread> mServerThread;
};
END_MTP_NAMESPACE
#endif // mozilla_system_mozmtpserver_h__

View File

@ -41,6 +41,8 @@ SOURCES += [
'AutoMounter.cpp',
'AutoMounterSetting.cpp',
'GonkGPSGeolocationProvider.cpp',
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',
'nsVolume.cpp',
@ -57,6 +59,11 @@ SOURCES += [
'VolumeServiceTest.cpp',
]
if CONFIG['ANDROID_VERSION'] >= '17':
CXXFLAGS += ['-I%s/frameworks/av/media/mtp' % CONFIG['ANDROID_SOURCE']]
else:
CXXFLAGS += ['-I%s/frameworks/base/media/mtp' % CONFIG['ANDROID_SOURCE']]
if CONFIG['ENABLE_TESTS']:
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']

View File

@ -134,6 +134,7 @@ OS_LIBS += \
-lstagefright_omx \
-lbinder \
-lgui \
-lmtp \
$(NULL)
endif