gecko/dom/system/gonk/MozMtpDatabase.cpp

971 lines
27 KiB
C++

/* -*- 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_FILE_NAME: return "MTP_PROPERTY_OBJECT_FILE_NAME";
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()
{
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.
RefPtr<DbEntry> dummy;
mDb.AppendElement(dummy);
}
//virtual
MozMtpDatabase::~MozMtpDatabase()
{
MTP_LOG("destructed");
}
void
MozMtpDatabase::AddEntry(DbEntry *entry)
{
entry->mHandle = GetNextHandle();
MOZ_ASSERT(mDb.Length() == entry->mHandle);
mDb.AppendElement(entry);
MTP_LOG("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;
}
static nsCString
GetPathWithoutFileName(const nsCString& aFullPath)
{
nsCString path;
int32_t offset = aFullPath.RFindChar('/');
if (offset != kNotFound) {
// The trailing slash will be as part of 'path'
path = StringHead(aFullPath, offset + 1);
}
MTP_LOG("returning '%s'", path.get());
return path;
}
void
MozMtpDatabase::AddDirectory(MtpStorageID aStorageID,
const char* aPath,
MtpObjectHandle aParent)
{
ScopedCloseDir dir;
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", aPath, 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 = aStorageID;
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);
AddDirectory(aStorageID, filename.get(), entry->mHandle);
}
}
}
void
MozMtpDatabase::AddStorage(MtpStorageID aStorageID,
const char* aPath,
const char* aName)
{
//TODO: Add an assert re thread being run on
PRFileInfo fileInfo;
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", aPath);
return;
}
#if 0
RefPtr<DbEntry> entry = new DbEntry;
entry->mStorageID = aStorageID;
entry->mParent = MTP_PARENT_ROOT;
entry->mObjectName = aName;
entry->mDisplayName = aName;
entry->mPath = aPath;
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
entry->mObjectSize = 0;
AddEntry(entry);
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
//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 aSucceeded)
{
MTP_LOG("Handle: 0x%08x Path: '%s'", aHandle, aPath);
if (!aSucceeded) {
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();
// 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->mStorageID == aStorageID && entry->mParent == aParent) {
list->push(entry->mHandle);
}
}
return list.forget();
}
//virtual
int
MozMtpDatabase::getNumObjects(MtpStorageID aStorageID,
MtpObjectFormat aFormat,
MtpObjectHandle aParent)
{
MTP_LOG("");
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
MtpObjectFormatList*
MozMtpDatabase::getSupportedPlaybackFormats()
{
static const uint16_t init_data[] = {MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION, MTP_FORMAT_PNG};
MtpObjectFormatList *list = new MtpObjectFormatList();
list->appendArray(init_data, MOZ_ARRAY_LENGTH(init_data));
MTP_LOG("returning MTP_FORMAT_UNDEFINED, MTP_FORMAT_ASSOCIATION, 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_ASSOCIATION, MTP_FORMAT_PNG");
return list;
}
static const MtpObjectProperty sSupportedObjectProperties[] =
{
MTP_PROPERTY_STORAGE_ID,
MTP_PROPERTY_OBJECT_FORMAT,
MTP_PROPERTY_PROTECTION_STATUS, // UINT16 - always 0
MTP_PROPERTY_OBJECT_SIZE,
MTP_PROPERTY_OBJECT_FILE_NAME, // just the filename - no directory
MTP_PROPERTY_NAME,
MTP_PROPERTY_DATE_MODIFIED,
MTP_PROPERTY_PARENT_OBJECT,
MTP_PROPERTY_PERSISTENT_UID,
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.putUInt16(entry->mObjectFormat); break;
case MTP_PROPERTY_OBJECT_SIZE: aPacket.putUInt64(entry->mObjectSize); break;
case MTP_PROPERTY_DISPLAY_NAME: aPacket.putString(entry->mDisplayName.get()); break;
case MTP_PROPERTY_PERSISTENT_UID:
// the same as aPacket.putUInt128
aPacket.putUInt64(entry->mHandle);
aPacket.putUInt64(entry->mStorageID);
break;
case MTP_PROPERTY_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;
}
static int
GetTypeOfObjectProp(MtpObjectProperty aProperty)
{
struct PropertyTableEntry {
MtpObjectProperty property;
int type;
};
static const PropertyTableEntry kObjectPropertyTable[] = {
{MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
{MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 },
{MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 },
{MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
{MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
{MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
{MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
{MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR },
{MTP_PROPERTY_NAME, MTP_TYPE_STR },
{MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 },
{MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR },
};
int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
const PropertyTableEntry* entryProp = kObjectPropertyTable;
int type = 0;
for (int i = 0; i < count; ++i, ++entryProp) {
if (entryProp->property == aProperty) {
type = entryProp->type;
break;
}
}
return type;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x Property: 0x%08x", aHandle, aProperty);
// Only support file name change
if (aProperty != MTP_PROPERTY_OBJECT_FILE_NAME) {
MTP_ERR("property 0x%x not supported", aProperty);
return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
}
if (GetTypeOfObjectProp(aProperty) != MTP_TYPE_STR) {
MTP_ERR("property type 0x%x not supported", GetTypeOfObjectProp(aProperty));
return MTP_RESPONSE_GENERAL_ERROR;
}
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Invalid Handle: 0x%08x", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MtpStringBuffer buf;
aPacket.getString(buf);
nsDependentCString newFileName(buf);
nsCString newFileFullPath(GetPathWithoutFileName(entry->mPath) + newFileName);
if (PR_Rename(entry->mPath.get(), newFileFullPath.get()) != PR_SUCCESS) {
MTP_ERR("Failed to rename '%s' to '%s'",
entry->mPath.get(), newFileFullPath.get());
return MTP_RESPONSE_GENERAL_ERROR;
}
MTP_LOG("renamed '%s' to '%s'", entry->mPath.get(), newFileFullPath.get());
entry->mPath = newFileFullPath;
entry->mObjectName = BaseName(entry->mPath);
entry->mDisplayName = entry->mObjectName;
return MTP_RESPONSE_OK;
}
//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",
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(numObjectProperties * 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_PERSISTENT_UID:
aPacket.putUInt16(MTP_TYPE_UINT128);
// the same as aPacket.putUInt128
aPacket.putUInt64(entry->mHandle);
aPacket.putUInt64(entry->mStorageID);
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:
case MTP_PROPERTY_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;
if (entry->mObjectSize > 0xFFFFFFFFuLL) {
aInfo.mCompressedSize = 0xFFFFFFFFuLL;
} else {
aInfo.mCompressedSize = entry->mObjectSize;
}
aInfo.mThumbFormat = MTP_FORMAT_UNDEFINED;
aInfo.mThumbCompressedSize = 0;
aInfo.mThumbPixWidth = 0;
aInfo.mThumbPixHeight = 0;
aInfo.mImagePixWidth = 0;
aInfo.mImagePixHeight = 0;
aInfo.mImagePixDepth = 0;
aInfo.mParent = entry->mParent;
aInfo.mAssociationType = 0;
aInfo.mAssociationDesc = 0;
aInfo.mSequenceNumber = 0;
aInfo.mName = ::strdup(entry->mObjectName.get());
aInfo.mDateCreated = entry->mDateCreated;
aInfo.mDateModified = entry->mDateModified;
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)
{
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'", 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);
return MTP_RESPONSE_OK;
}
#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);
MtpProperty* result = nullptr;
switch (aProperty)
{
case MTP_PROPERTY_PROTECTION_STATUS:
result = new MtpProperty(aProperty, MTP_TYPE_UINT16);
break;
case MTP_PROPERTY_OBJECT_FORMAT:
result = new MtpProperty(aProperty, MTP_TYPE_UINT16, false, aFormat);
break;
case MTP_PROPERTY_STORAGE_ID:
case MTP_PROPERTY_PARENT_OBJECT:
case MTP_PROPERTY_WIDTH:
case MTP_PROPERTY_HEIGHT:
case MTP_PROPERTY_IMAGE_BIT_DEPTH:
result = new MtpProperty(aProperty, MTP_TYPE_UINT32);
break;
case MTP_PROPERTY_OBJECT_SIZE:
result = new MtpProperty(aProperty, MTP_TYPE_UINT64);
break;
case MTP_PROPERTY_DISPLAY_NAME:
case MTP_PROPERTY_NAME:
result = new MtpProperty(aProperty, MTP_TYPE_STR);
break;
case MTP_PROPERTY_OBJECT_FILE_NAME:
result = new MtpProperty(aProperty, MTP_TYPE_STR, true);
break;
case MTP_PROPERTY_DATE_MODIFIED:
case MTP_PROPERTY_DATE_ADDED:
result = new MtpProperty(aProperty, MTP_TYPE_STR);
result->setFormDateTime();
break;
case MTP_PROPERTY_PERSISTENT_UID:
result = new MtpProperty(aProperty, MTP_TYPE_UINT128);
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