解决win10上不能进行内部存储/SD卡之间不能拷贝的问题。
可以提升拷贝速度。
剪切拷贝可以去实现MoveObject.
主要实现:
Implement CopyObject(0x101A) operate on Android O
MtpResponseCode MtpServer::doCopyObject() {
if (!hasStorage())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
if (mRequest.getParameterCount() < 3)
return MTP_RESPONSE_INVALID_PARAMETER;
/*
* @param object_id the object to copy.
* @param storage_id the id of the destination storage.
* @param parent_id the id of the destination parent object (folder).
*/
MtpObjectHandle handle = mRequest.getParameter(1);
MtpStorageID storageID = mRequest.getParameter(2);
MtpObjectHandle parent = mRequest.getParameter(3);
ALOGD("handle: %08x parent: %08x storageID: %08X", handle, parent, storageID);
auto start = std::chrono::steady_clock::now();
MtpString path;
MtpString pathfr;
int64_t lenfr;
MtpObjectFormat fmtfr;
// get object infos
MtpResponseCode result = mDatabase->getObjectFilePath(handle, pathfr, lenfr, fmtfr);
if (result != MTP_RESPONSE_OK)
return result;
MtpObjectInfo info(handle);
result = mDatabase->getObjectInfo(handle, info);
if (result != MTP_RESPONSE_OK)
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
//prepare DB
MtpStorage* storage = getStorage(storageID);
if (!storage)
return MTP_RESPONSE_INVALID_STORAGE_ID;
// check space first
if (((uint64_t)lenfr) > storage->getFreeSpace())
return MTP_RESPONSE_STORAGE_FULL;
uint64_t maxFileSize = storage->getMaxFileSize();
// check storage max file size
if (maxFileSize != 0) {
// if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
// is >= 0xFFFFFFFF
if (((uint64_t)lenfr) > maxFileSize || ((uint64_t)lenfr) == 0xFFFFFFFF)
return MTP_RESPONSE_OBJECT_TOO_LARGE;
}
// special case the root
if (parent == MTP_PARENT_ROOT) {
path = storage->getPath();
parent = 0;
} else {
int64_t length;
MtpObjectFormat format;
int result = mDatabase->getObjectFilePath(parent, path, length, format);
if (result != MTP_RESPONSE_OK)
return result;
if (format != MTP_FORMAT_ASSOCIATION)
return MTP_RESPONSE_INVALID_PARENT_OBJECT;
ALOGD("pathfr: %s path: %s ", (const char*)pathfr, (const char*)path);
}
if (path[path.size() - 1] != '/')
path += "/";
path += info.mName;
handle = mDatabase->beginSendObject((const char*)path,
fmtfr, parent, storageID, lenfr, info.mDateModified);
ALOGD("path: %s handle: %d ", (const char*)path, handle);
if (handle == kInvalidObjectHandle) {
ALOGE("beginSendObject failed, kInvalidObjectHandle");
return MTP_RESPONSE_GENERAL_ERROR;
}
//open file to copy
const char* filePath = (const char *)pathfr;
mtp_file_range mfr;
mtp_file_range mto;
mode_t mask;
uint8_t buf[16384];
int count = 0;
int len = lenfr > 16384? 16384: lenfr;
uint64_t offset = 0;
mfr.fd = open(filePath, O_RDONLY);
if (mfr.fd < 0) {
result = MTP_RESPONSE_GENERAL_ERROR;
goto copydone;
}
mfr.offset = 0;
mfr.length = lenfr;
mfr.command = mRequest.getOperationCode();
mfr.transaction_id = mRequest.getTransactionID();
//create new file
mto.fd = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (mto.fd < 0) {
result = MTP_RESPONSE_GENERAL_ERROR;
//close(mfr.fd);
//return result;
goto copydone;
}
fchown(mto.fd, getuid(), mFileGroup);
// set permissions
mask = umask(0);
fchmod(mto.fd, mFilePermission);
umask(mask);
//Copy the file
while (lenfr > 0){
len = lenfr > 16384? 16384: lenfr;
count = read(mfr.fd, buf, len);
if (count < 0) {
result = MTP_RESPONSE_GENERAL_ERROR;
break;
}
count = write(mto.fd, buf, count);
if (lenfr < 0) {
result = MTP_RESPONSE_GENERAL_ERROR;
break;
}
lenfr -= count;
offset += count;
lseek(mfr.fd, offset, SEEK_SET);
lseek(mto.fd, offset, SEEK_SET);
}
if (result == MTP_RESPONSE_OK)
mResponse.setParameter(1, handle);
copydone:
//end DB
mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
result == MTP_RESPONSE_OK);
auto end = std::chrono::steady_clock::now();
std::chrono::duration diff = end - start;
struct stat sstat;
fstat(mto.fd, &sstat);
uint64_t finalsize = sstat.st_size;
ALOGV("Copy a file over MTP. Time: %f s, Size: %" PRIu64 ", Rate: %f bytes/s",
diff.count(), finalsize, ((double) finalsize) / diff.count());
close(mto.fd);
close(mfr.fd);
return result;
}