mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
Bug 1188796 - Have MTP record modified timestamps when they're passed. r=alchen
Of the 3 popular host platforms, only Windows properly passes in the modification timestamp of the file. Under linux, it passes in an empty string, which then gets passed to beginSendObject as zero. This patch now treats that as the current date/time. Under OSX, the Android FIle Transfer program passes in the date modified and date created, but sets both of these fields to be the current date/time and not the times from the files being copied. Hl: Enter commit message. Lines beginning with 'HG:' are removed.
This commit is contained in:
parent
e54571a7f6
commit
59280e28fd
@ -22,6 +22,8 @@
|
||||
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <utime.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace mozilla;
|
||||
@ -63,6 +65,7 @@ ObjectPropertyAsStr(MtpObjectProperty aProperty)
|
||||
case MTP_PROPERTY_PROTECTION_STATUS: return "MTP_PROPERTY_PROTECTION_STATUS";
|
||||
case MTP_PROPERTY_OBJECT_SIZE: return "MTP_PROPERTY_OBJECT_SIZE";
|
||||
case MTP_PROPERTY_OBJECT_FILE_NAME: return "MTP_PROPERTY_OBJECT_FILE_NAME";
|
||||
case MTP_PROPERTY_DATE_CREATED: return "MTP_PROPERTY_DATE_CREATED";
|
||||
case MTP_PROPERTY_DATE_MODIFIED: return "MTP_PROPERTY_DATE_MODIFIED";
|
||||
case MTP_PROPERTY_PARENT_OBJECT: return "MTP_PROPERTY_PARENT_OBJECT";
|
||||
case MTP_PROPERTY_PERSISTENT_UID: return "MTP_PROPERTY_PERSISTENT_UID";
|
||||
@ -76,6 +79,16 @@ ObjectPropertyAsStr(MtpObjectProperty aProperty)
|
||||
return "MTP_PROPERTY_???";
|
||||
}
|
||||
|
||||
static char*
|
||||
FormatDate(time_t aTime, char *aDateStr, size_t aDateStrSize)
|
||||
{
|
||||
struct tm tm;
|
||||
localtime_r(&aTime, &tm);
|
||||
MTP_LOG("(%ld) tm_zone = %s off = %ld", aTime, tm.tm_zone, tm.tm_gmtoff);
|
||||
strftime(aDateStr, aDateStrSize, "%Y%m%dT%H%M%S", &tm);
|
||||
return aDateStr;
|
||||
}
|
||||
|
||||
MozMtpDatabase::MozMtpDatabase()
|
||||
: mMutex("MozMtpDatabase::mMutex"),
|
||||
mDb(mMutex),
|
||||
@ -222,9 +235,21 @@ MozMtpDatabase::UpdateEntry(MtpObjectHandle aHandle, DeviceStorageFile* aFile)
|
||||
int64_t fileSize = 0;
|
||||
aFile->mFile->GetFileSize(&fileSize);
|
||||
entry->mObjectSize = fileSize;
|
||||
aFile->mFile->GetLastModifiedTime(&entry->mDateCreated);
|
||||
entry->mDateModified = entry->mDateCreated;
|
||||
MTP_DBG("UpdateEntry (0x%08x file %s)", entry->mHandle, entry->mPath.get());
|
||||
|
||||
PRTime dateModifiedMsecs;
|
||||
// GetLastModifiedTime returns msecs
|
||||
aFile->mFile->GetLastModifiedTime(&dateModifiedMsecs);
|
||||
entry->mDateModified = dateModifiedMsecs / PR_MSEC_PER_SEC;
|
||||
entry->mDateCreated = entry->mDateModified;
|
||||
entry->mDateAdded = entry->mDateModified;
|
||||
|
||||
#if USE_DEBUG
|
||||
char dateStr[20];
|
||||
MTP_DBG("UpdateEntry (0x%08x file %s) modified (%ld) %s",
|
||||
entry->mHandle, entry->mPath.get(),
|
||||
entry->mDateModified,
|
||||
FormatDate(entry->mDateModified, dateStr, sizeof(dateStr)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -454,14 +479,19 @@ MozMtpDatabase::CreateEntryForFileAndNotify(const nsACString& aPath,
|
||||
aFile->mFile->GetFileSize(&fileSize);
|
||||
entry->mObjectSize = fileSize;
|
||||
|
||||
aFile->mFile->GetLastModifiedTime(&entry->mDateCreated);
|
||||
// Note: Even though PRTime records usec, GetLastModifiedTime returns
|
||||
// msecs.
|
||||
PRTime dateModifiedMsecs;
|
||||
aFile->mFile->GetLastModifiedTime(&dateModifiedMsecs);
|
||||
entry->mDateModified = dateModifiedMsecs / PR_MSEC_PER_SEC;
|
||||
} else {
|
||||
// Found a slash, this makes this a directory component
|
||||
entry->mObjectFormat = MTP_FORMAT_ASSOCIATION;
|
||||
entry->mObjectSize = 0;
|
||||
entry->mDateCreated = PR_Now();
|
||||
time(&entry->mDateModified);
|
||||
}
|
||||
entry->mDateModified = entry->mDateCreated;
|
||||
entry->mDateCreated = entry->mDateModified;
|
||||
entry->mDateAdded = entry->mDateModified;
|
||||
|
||||
AddEntryAndNotify(entry, aMtpServer);
|
||||
MTP_LOG("About to call sendObjectAdded Handle 0x%08x file %s", entry->mHandle, entry->mPath.get());
|
||||
@ -503,8 +533,11 @@ MozMtpDatabase::AddDirectory(MtpStorageID aStorageID,
|
||||
entry->mObjectName = dirEntry->name;
|
||||
entry->mDisplayName = dirEntry->name;
|
||||
entry->mPath = filename;
|
||||
entry->mDateCreated = fileInfo.creationTime;
|
||||
entry->mDateModified = fileInfo.modifyTime;
|
||||
|
||||
// PR_GetFileInfo64 returns timestamps in usecs
|
||||
entry->mDateModified = fileInfo.modifyTime / PR_USEC_PER_SEC;
|
||||
entry->mDateCreated = fileInfo.creationTime / PR_USEC_PER_SEC;
|
||||
time(&entry->mDateAdded);
|
||||
|
||||
if (fileInfo.type == PR_FILE_FILE) {
|
||||
entry->mObjectFormat = MTP_FORMAT_DEFINED;
|
||||
@ -653,9 +686,71 @@ MozMtpDatabase::beginSendObject(const char* aPath,
|
||||
entry->mObjectFormat = aFormat;
|
||||
entry->mObjectSize = aSize;
|
||||
|
||||
if (aModified != 0) {
|
||||
// Currently, due to the way that parseDateTime is coded in
|
||||
// frameworks/av/media/mtp/MtpUtils.cpp, aModified winds up being the number
|
||||
// of seconds from the epoch in local time, rather than UTC time. So we
|
||||
// need to convert it back to being relative to UTC since that's what linux
|
||||
// expects time_t to contain.
|
||||
//
|
||||
// In more concrete testable terms, if the host parses 2015-08-02 02:22:00
|
||||
// as a local time in the Pacific timezone, aModified will come to us as
|
||||
// 1438482120.
|
||||
//
|
||||
// What we want is what mktime would pass us with the same date. Using python
|
||||
// (because its simple) with the current timezone set to be America/Vancouver:
|
||||
//
|
||||
// >>> import time
|
||||
// >>> time.mktime((2015, 8, 2, 2, 22, 0, 0, 0, -1))
|
||||
// 1438507320.0
|
||||
// >>> time.localtime(1438507320)
|
||||
// time.struct_time(tm_year=2015, tm_mon=8, tm_mday=2, tm_hour=2, tm_min=22, tm_sec=0, tm_wday=6, tm_yday=214, tm_isdst=1)
|
||||
//
|
||||
// Currently, when a file has a modification time of 2015-08-22 02:22:00 PDT
|
||||
// then aModified will come in as 1438482120 which corresponds to
|
||||
// 2015-08-22 02:22:00 UTC
|
||||
|
||||
struct tm tm;
|
||||
if (gmtime_r(&aModified, &tm) != NULL) {
|
||||
// GMT always comes back with tm_isdst = 0, so we set it to -1 in order
|
||||
// to have mktime figure out dst based on the date.
|
||||
tm.tm_isdst = -1;
|
||||
aModified = mktime(&tm);
|
||||
if (aModified == (time_t)-1) {
|
||||
aModified = 0;
|
||||
}
|
||||
} else {
|
||||
aModified = 0;
|
||||
}
|
||||
}
|
||||
if (aModified == 0) {
|
||||
// The ubuntu host doesn't pass in the modified/created times in the
|
||||
// SENDOBJECT packet, so aModified winds up being zero. About the best
|
||||
// we can do with that is to use the current time.
|
||||
time(&aModified);
|
||||
}
|
||||
|
||||
// And just an FYI for anybody else looking at timestamps. Under OSX you
|
||||
// need to use the Android File Transfer program to copy files into the
|
||||
// phone. That utility passes in both date modified and date created
|
||||
// timestamps, but they're both equal to the time that the file was copied
|
||||
// and not the times that are associated with the files.
|
||||
|
||||
// Now we have aModified in a traditional time_t format, which is the number
|
||||
// of seconds from the UTC epoch.
|
||||
|
||||
entry->mDateModified = aModified;
|
||||
entry->mDateCreated = entry->mDateModified;
|
||||
entry->mDateAdded = entry->mDateModified;
|
||||
|
||||
AddEntry(entry);
|
||||
|
||||
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s'", entry->mHandle, aParent, aPath);
|
||||
#if USE_DEBUG
|
||||
char dateStr[20];
|
||||
MTP_LOG("Handle: 0x%08x Parent: 0x%08x Path: '%s' aModified %ld %s",
|
||||
entry->mHandle, aParent, aPath, aModified,
|
||||
FormatDate(entry->mDateModified, dateStr, sizeof(dateStr)));
|
||||
#endif
|
||||
|
||||
mBeginSendObjectCalled = true;
|
||||
return entry->mHandle;
|
||||
@ -677,6 +772,22 @@ MozMtpDatabase::endSendObject(const char* aPath,
|
||||
if (aSucceeded) {
|
||||
RefPtr<DbEntry> entry = GetEntry(aHandle);
|
||||
if (entry) {
|
||||
// The android MTP server only copies the data in, it doesn't set the
|
||||
// modified timestamp, so we do that here.
|
||||
|
||||
struct utimbuf new_times;
|
||||
struct stat sb;
|
||||
|
||||
char dateStr[20];
|
||||
MTP_LOG("Path: '%s' setting modified time to (%ld) %s",
|
||||
entry->mPath.get(), entry->mDateModified,
|
||||
FormatDate(entry->mDateModified, dateStr, sizeof(dateStr)));
|
||||
|
||||
stat(entry->mPath.get(), &sb);
|
||||
new_times.actime = sb.st_atime; // Preserve atime
|
||||
new_times.modtime = entry->mDateModified;
|
||||
utime(entry->mPath.get(), &new_times);
|
||||
|
||||
FileWatcherNotify(entry, "modified");
|
||||
}
|
||||
} else {
|
||||
@ -794,6 +905,7 @@ static const MtpObjectProperty sSupportedObjectProperties[] =
|
||||
MTP_PROPERTY_OBJECT_SIZE,
|
||||
MTP_PROPERTY_OBJECT_FILE_NAME, // just the filename - no directory
|
||||
MTP_PROPERTY_NAME,
|
||||
MTP_PROPERTY_DATE_CREATED,
|
||||
MTP_PROPERTY_DATE_MODIFIED,
|
||||
MTP_PROPERTY_PARENT_OBJECT,
|
||||
MTP_PROPERTY_PERSISTENT_UID,
|
||||
@ -874,6 +986,7 @@ GetTypeOfObjectProp(MtpObjectProperty aProperty)
|
||||
{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_CREATED, MTP_TYPE_STR },
|
||||
{MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
|
||||
{MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
|
||||
{MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR },
|
||||
@ -1130,6 +1243,8 @@ MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
|
||||
UnprotectedDbArray::size_type numEntries = result.Length();
|
||||
UnprotectedDbArray::index_type entryIdx;
|
||||
|
||||
char dateStr[20];
|
||||
|
||||
aPacket.putUInt32(numObjectProperties * numEntries);
|
||||
for (entryIdx = 0; entryIdx < numEntries; entryIdx++) {
|
||||
RefPtr<DbEntry> entry = result[entryIdx];
|
||||
@ -1178,23 +1293,24 @@ MozMtpDatabase::getObjectPropertyList(MtpObjectHandle aHandle,
|
||||
aPacket.putUInt16(0); // 0 = No Protection
|
||||
break;
|
||||
|
||||
case MTP_PROPERTY_DATE_CREATED: {
|
||||
aPacket.putUInt16(MTP_TYPE_STR);
|
||||
aPacket.putString(FormatDate(entry->mDateCreated, dateStr, sizeof(dateStr)));
|
||||
MTP_LOG("mDateCreated: (%ld) %s", entry->mDateCreated, dateStr);
|
||||
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);
|
||||
aPacket.putString(FormatDate(entry->mDateModified, dateStr, sizeof(dateStr)));
|
||||
MTP_LOG("mDateModified: (%ld) %s", entry->mDateModified, 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);
|
||||
aPacket.putString(FormatDate(entry->mDateAdded, dateStr, sizeof(dateStr)));
|
||||
MTP_LOG("mDateAdded: (%ld) %s", entry->mDateAdded, dateStr);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1243,11 +1359,14 @@ MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
|
||||
aInfo.mAssociationDesc = 0;
|
||||
aInfo.mSequenceNumber = 0;
|
||||
aInfo.mName = ::strdup(entry->mObjectName.get());
|
||||
aInfo.mDateCreated = entry->mDateCreated;
|
||||
aInfo.mDateModified = entry->mDateModified;
|
||||
|
||||
MTP_LOG("aInfo.mDateCreated = %ld entry->mDateCreated = %ld",
|
||||
aInfo.mDateCreated, entry->mDateCreated);
|
||||
MTP_LOG("aInfo.mDateModified = %ld entry->mDateModified = %ld",
|
||||
aInfo.mDateModified, entry->mDateModified);
|
||||
|
||||
// entry->mDateXxxx is a PRTime stores the time as microseconds from the epoch.
|
||||
// aInfo.mDateXxxx is time_t which stores the time as seconds from the epoch.
|
||||
aInfo.mDateCreated = entry->mDateCreated / PR_USEC_PER_SEC;
|
||||
aInfo.mDateModified = entry->mDateModified / PR_USEC_PER_SEC;
|
||||
aInfo.mKeywords = ::strdup("fxos,touch");
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
@ -1383,6 +1502,7 @@ MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
|
||||
case MTP_PROPERTY_OBJECT_FILE_NAME:
|
||||
result = new MtpProperty(aProperty, MTP_TYPE_STR, true);
|
||||
break;
|
||||
case MTP_PROPERTY_DATE_CREATED:
|
||||
case MTP_PROPERTY_DATE_MODIFIED:
|
||||
case MTP_PROPERTY_DATE_ADDED:
|
||||
result = new MtpProperty(aProperty, MTP_TYPE_STR);
|
||||
|
@ -133,7 +133,8 @@ private:
|
||||
mParent(0),
|
||||
mObjectSize(0),
|
||||
mDateCreated(0),
|
||||
mDateModified(0) {}
|
||||
mDateModified(0),
|
||||
mDateAdded(0) {}
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DbEntry)
|
||||
|
||||
@ -145,8 +146,9 @@ private:
|
||||
uint64_t mObjectSize;
|
||||
nsCString mDisplayName;
|
||||
nsCString mPath;
|
||||
PRTime mDateCreated;
|
||||
PRTime mDateModified;
|
||||
time_t mDateCreated;
|
||||
time_t mDateModified;
|
||||
time_t mDateAdded;
|
||||
|
||||
protected:
|
||||
~DbEntry() {}
|
||||
|
Loading…
Reference in New Issue
Block a user