MTP - Implement CopyObject(0x101A) operate on Android O

4866 ワード

win 10で内部メモリ/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;
}