Files
android_bootable_recovery/mtp/legacy/MtpDevice.cpp
bigbiff bigbiff af32bb9c4f MTP FFS updates:
This update splits old MTP code and new MTP code from Google
into two trees, legacy and ffs. Depending on the SDK level,
the build system will select the correct version. The reason
for separating the versions out are due to older android trees
not supporting the updated MTP code from Google.

Most MTP code is from Google, with additions needed from
implementing the Java functions in C++ for TWRP and FFS.

We assume if you are in android-9.0 or above, your kernel
has support for FFS over MTP. Verify that your init.rc
is mounting the MTP FFS driver to the proper location.

Change-Id: I4b107b239bd9bc5699527f9c8c77d9079f264a7e
2019-03-20 14:28:21 -05:00

852 lines
23 KiB
C++

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
*/
#include "MtpDevice.h"
#include "MtpDebug.h"
#include "MtpDeviceInfo.h"
#include "MtpObjectInfo.h"
#include "MtpProperty.h"
#include "MtpStorageInfo.h"
#include "MtpStringBuffer.h"
#include "MtpUtils.h"
#include "MtpDataPacket.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <endian.h>
#include <usbhost/usbhost.h>
#if 0
static bool isMtpDevice(uint16_t vendor, uint16_t product) {
// Sandisk Sansa Fuze
if (vendor == 0x0781 && product == 0x74c2)
return true;
// Samsung YP-Z5
if (vendor == 0x04e8 && product == 0x503c)
return true;
return false;
}
#endif
#ifdef HAS_USBHOST_TIMEOUT
static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
#endif
MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
struct usb_device *device = usb_device_new(deviceName, fd);
if (!device) {
MTPE("usb_device_new failed for %s", deviceName);
return NULL;
}
struct usb_descriptor_header* desc;
struct usb_descriptor_iter iter;
usb_descriptor_iter_init(device, &iter);
while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
if (desc->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
interface->bInterfaceSubClass == 1 && // Still Image Capture
interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
{
#ifdef HAS_USBHOST_TIMEOUT
char* manufacturerName = usb_device_get_manufacturer_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
char* productName = usb_device_get_product_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
#else
char* manufacturerName = usb_device_get_manufacturer_name(device);
char* productName = usb_device_get_product_name(device);
#endif
MTPD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
free(manufacturerName);
free(productName);
} else if (interface->bInterfaceClass == 0xFF &&
interface->bInterfaceSubClass == 0xFF &&
interface->bInterfaceProtocol == 0) {
#ifdef HAS_USBHOST_TIMEOUT
char* interfaceName = usb_device_get_string(device, interface->iInterface, USB_CONTROL_TRANSFER_TIMEOUT_MS);
#else
char* interfaceName = usb_device_get_string(device, interface->iInterface);
#endif
if (!interfaceName) {
continue;
} else if (strcmp(interfaceName, "MTP")) {
free(interfaceName);
continue;
}
free(interfaceName);
// Looks like an android style MTP device
#ifdef HAS_USBHOST_TIMEOUT
char* manufacturerName = usb_device_get_manufacturer_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
char* productName = usb_device_get_product_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
#else
char* manufacturerName = usb_device_get_manufacturer_name(device);
char* productName = usb_device_get_product_name(device);
#endif
MTPI("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
free(manufacturerName);
free(productName);
}
#if 0
else {
// look for special cased devices based on vendor/product ID
// we are doing this mainly for testing purposes
uint16_t vendor = usb_device_get_vendor_id(device);
uint16_t product = usb_device_get_product_id(device);
if (!isMtpDevice(vendor, product)) {
// not an MTP or PTP device
continue;
}
// request MTP OS string and descriptor
// some music players need to see this before entering MTP mode.
char buffer[256];
memset(buffer, 0, sizeof(buffer));
int ret = usb_device_control_transfer(device,
USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
0, buffer, sizeof(buffer), 0);
MTPE("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
if (ret > 0) {
MTPI("got MTP string %s\n", buffer);
ret = usb_device_control_transfer(device,
USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
0, 4, buffer, sizeof(buffer), 0);
MTPI("OS descriptor got %d\n", ret);
} else {
MTPI("no MTP string\n");
}
}
#endif
// if we got here, then we have a likely MTP or PTP device
// interface should be followed by three endpoints
struct usb_endpoint_descriptor *ep;
struct usb_endpoint_descriptor *ep_in_desc = NULL;
struct usb_endpoint_descriptor *ep_out_desc = NULL;
struct usb_endpoint_descriptor *ep_intr_desc = NULL;
for (int i = 0; i < 3; i++) {
ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
MTPE("endpoints not found\n");
usb_device_close(device);
return NULL;
}
if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
ep_in_desc = ep;
else
ep_out_desc = ep;
} else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
ep_intr_desc = ep;
}
}
if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
MTPE("endpoints not found\n");
usb_device_close(device);
return NULL;
}
if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
MTPE("usb_device_claim_interface failed errno: %d\n", errno);
usb_device_close(device);
return NULL;
}
MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
ep_in_desc, ep_out_desc, ep_intr_desc);
mtpDevice->initialize();
return mtpDevice;
}
}
usb_device_close(device);
MTPE("device not found");
return NULL;
}
MtpDevice::MtpDevice(struct usb_device* device, int interface,
const struct usb_endpoint_descriptor *ep_in,
const struct usb_endpoint_descriptor *ep_out,
const struct usb_endpoint_descriptor *ep_intr)
: mDevice(device),
mInterface(interface),
mRequestIn1(NULL),
mRequestIn2(NULL),
mRequestOut(NULL),
mRequestIntr(NULL),
mDeviceInfo(NULL),
mSessionID(0),
mTransactionID(0),
mReceivedResponse(false)
{
mRequestIn1 = usb_request_new(device, ep_in);
mRequestIn2 = usb_request_new(device, ep_in);
mRequestOut = usb_request_new(device, ep_out);
mRequestIntr = usb_request_new(device, ep_intr);
}
MtpDevice::~MtpDevice() {
close();
for (size_t i = 0; i < mDeviceProperties.size(); i++)
delete mDeviceProperties[i];
usb_request_free(mRequestIn1);
usb_request_free(mRequestIn2);
usb_request_free(mRequestOut);
usb_request_free(mRequestIntr);
}
void MtpDevice::initialize() {
openSession();
mDeviceInfo = getDeviceInfo();
if (mDeviceInfo) {
if (mDeviceInfo->mDeviceProperties) {
int count = mDeviceInfo->mDeviceProperties->size();
for (int i = 0; i < count; i++) {
MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
MtpProperty* property = getDevicePropDesc(propCode);
if (property)
mDeviceProperties.push(property);
}
}
}
}
void MtpDevice::close() {
if (mDevice) {
usb_device_release_interface(mDevice, mInterface);
usb_device_close(mDevice);
mDevice = NULL;
}
}
void MtpDevice::print() {
if (mDeviceInfo) {
mDeviceInfo->print();
if (mDeviceInfo->mDeviceProperties) {
MTPI("***** DEVICE PROPERTIES *****\n");
int count = mDeviceInfo->mDeviceProperties->size();
for (int i = 0; i < count; i++) {
MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
MtpProperty* property = getDevicePropDesc(propCode);
if (property) {
property->print();
delete property;
}
}
}
}
if (mDeviceInfo->mPlaybackFormats) {
MTPI("***** OBJECT PROPERTIES *****\n");
int count = mDeviceInfo->mPlaybackFormats->size();
for (int i = 0; i < count; i++) {
MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
MTPI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
MtpObjectPropertyList* props = getObjectPropsSupported(format);
if (props) {
for (size_t j = 0; j < props->size(); j++) {
MtpObjectProperty prop = (*props)[j];
MtpProperty* property = getObjectPropDesc(prop, format);
if (property) {
property->print();
delete property;
} else {
MTPI("could not fetch property: %s",
MtpDebug::getObjectPropCodeName(prop));
}
}
}
}
}
}
const char* MtpDevice::getDeviceName() {
if (mDevice)
return usb_device_get_name(mDevice);
else
return "???";
}
bool MtpDevice::openSession() {
android::Mutex::Autolock autoLock(mMutex);
mSessionID = 0;
mTransactionID = 0;
MtpSessionID newSession = 1;
mRequest.reset();
mRequest.setParameter(1, newSession);
if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
return false;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
newSession = mResponse.getParameter(1);
else if (ret != MTP_RESPONSE_OK)
return false;
mSessionID = newSession;
mTransactionID = 1;
return true;
}
bool MtpDevice::closeSession() {
// FIXME
return true;
}
MtpDeviceInfo* MtpDevice::getDeviceInfo() {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpDeviceInfo* info = new MtpDeviceInfo;
info->read(mData);
return info;
}
return NULL;
}
MtpStorageIDList* MtpDevice::getStorageIDs() {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
return mData.getAUInt32();
}
return NULL;
}
MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, storageID);
if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpStorageInfo* info = new MtpStorageInfo(storageID);
info->read(mData);
return info;
}
return NULL;
}
MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
MtpObjectFormat format, MtpObjectHandle parent) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, storageID);
mRequest.setParameter(2, format);
mRequest.setParameter(3, parent);
if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
return mData.getAUInt32();
}
return NULL;
}
MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
android::Mutex::Autolock autoLock(mMutex);
// FIXME - we might want to add some caching here
mRequest.reset();
mRequest.setParameter(1, handle);
if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpObjectInfo* info = new MtpObjectInfo(handle);
info->read(mData);
return info;
}
return NULL;
}
void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, handle);
if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
return mData.getData(outLength);
}
}
outLength = 0;
return NULL;
}
MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
MtpObjectHandle parent = info->mParent;
if (parent == 0)
parent = MTP_PARENT_ROOT;
mRequest.setParameter(1, info->mStorageID);
mRequest.setParameter(2, info->mParent);
mData.putUInt32(info->mStorageID);
mData.putUInt16(info->mFormat);
mData.putUInt16(info->mProtectionStatus);
mData.putUInt32(info->mCompressedSize);
mData.putUInt16(info->mThumbFormat);
mData.putUInt32(info->mThumbCompressedSize);
mData.putUInt32(info->mThumbPixWidth);
mData.putUInt32(info->mThumbPixHeight);
mData.putUInt32(info->mImagePixWidth);
mData.putUInt32(info->mImagePixHeight);
mData.putUInt32(info->mImagePixDepth);
mData.putUInt32(info->mParent);
mData.putUInt16(info->mAssociationType);
mData.putUInt32(info->mAssociationDesc);
mData.putUInt32(info->mSequenceNumber);
mData.putString(info->mName);
char created[100], modified[100];
formatDateTime(info->mDateCreated, created, sizeof(created));
formatDateTime(info->mDateModified, modified, sizeof(modified));
mData.putString(created);
mData.putString(modified);
if (info->mKeywords)
mData.putString(info->mKeywords);
else
mData.putEmptyString();
if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
info->mStorageID = mResponse.getParameter(1);
info->mParent = mResponse.getParameter(2);
info->mHandle = mResponse.getParameter(3);
return info->mHandle;
}
}
return (MtpObjectHandle)-1;
}
bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
android::Mutex::Autolock autoLock(mMutex);
int remaining = info->mCompressedSize;
mRequest.reset();
mRequest.setParameter(1, info->mHandle);
if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
// send data header
writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
char buffer[65536];
while (remaining > 0) {
int count = read(srcFD, buffer, sizeof(buffer));
if (count > 0) {
int written = mData.write(mRequestOut, buffer, count);
// FIXME check error
remaining -= count;
} else {
break;
}
}
}
MtpResponseCode ret = readResponse();
return (remaining == 0 && ret == MTP_RESPONSE_OK);
}
bool MtpDevice::deleteObject(MtpObjectHandle handle) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, handle);
if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK)
return true;
}
return false;
}
MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
MtpObjectInfo* info = getObjectInfo(handle);
if (info) {
MtpObjectHandle parent = info->mParent;
delete info;
return parent;
} else {
return -1;
}
}
MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
MtpObjectInfo* info = getObjectInfo(handle);
if (info) {
MtpObjectHandle storageId = info->mStorageID;
delete info;
return storageId;
} else {
return -1;
}
}
MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, format);
if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
return mData.getAUInt16();
}
return NULL;
}
MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, code);
if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpProperty* property = new MtpProperty;
property->read(mData);
return property;
}
return NULL;
}
MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
android::Mutex::Autolock autoLock(mMutex);
mRequest.reset();
mRequest.setParameter(1, code);
mRequest.setParameter(2, format);
if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
return NULL;
if (!readData())
return NULL;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpProperty* property = new MtpProperty;
property->read(mData);
return property;
}
return NULL;
}
bool MtpDevice::readObject(MtpObjectHandle handle,
bool (* callback)(void* data, int offset, int length, void* clientData),
int objectSize, void* clientData) {
android::Mutex::Autolock autoLock(mMutex);
bool result = false;
mRequest.reset();
mRequest.setParameter(1, handle);
if (sendRequest(MTP_OPERATION_GET_OBJECT)
&& mData.readDataHeader(mRequestIn1)) {
uint32_t length = mData.getContainerLength();
if ((int)length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
MTPE("readObject error objectSize: %d, length: %d",
objectSize, length);
goto fail;
}
length -= MTP_CONTAINER_HEADER_SIZE;
uint32_t remaining = length;
int offset = 0;
int initialDataLength = 0;
void* initialData = mData.getData(initialDataLength);
if (initialData) {
if (initialDataLength > 0) {
if (!callback(initialData, 0, initialDataLength, clientData))
goto fail;
remaining -= initialDataLength;
offset += initialDataLength;
}
free(initialData);
}
// USB reads greater than 16K don't work
char buffer1[16384], buffer2[16384];
mRequestIn1->buffer = buffer1;
mRequestIn2->buffer = buffer2;
struct usb_request* req = mRequestIn1;
void* writeBuffer = NULL;
int writeLength = 0;
while (remaining > 0 || writeBuffer) {
if (remaining > 0) {
// queue up a read request
req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
if (mData.readDataAsync(req)) {
MTPE("readDataAsync failed");
goto fail;
}
} else {
req = NULL;
}
if (writeBuffer) {
// write previous buffer
if (!callback(writeBuffer, offset, writeLength, clientData)) {
MTPE("write failed");
// wait for pending read before failing
if (req)
mData.readDataWait(mDevice);
goto fail;
}
offset += writeLength;
writeBuffer = NULL;
}
// wait for read to complete
if (req) {
int read = mData.readDataWait(mDevice);
if (read < 0)
goto fail;
if (read > 0) {
writeBuffer = req->buffer;
writeLength = read;
remaining -= read;
req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
} else {
writeBuffer = NULL;
}
}
}
MtpResponseCode response = readResponse();
if (response == MTP_RESPONSE_OK)
result = true;
}
fail:
return result;
}
// reads the object's data and writes it to the specified file path
bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
MTPI("readObject: %s", destPath);
int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, 0640);
if (fd < 0) {
MTPE("open failed for %s", destPath);
return false;
}
fchown(fd, getuid(), group);
// set permissions
int mask = umask(0);
fchmod(fd, perm);
umask(mask);
android::Mutex::Autolock autoLock(mMutex);
bool result = false;
mRequest.reset();
mRequest.setParameter(1, handle);
if (sendRequest(MTP_OPERATION_GET_OBJECT)
&& mData.readDataHeader(mRequestIn1)) {
uint32_t length = mData.getContainerLength();
if (length < MTP_CONTAINER_HEADER_SIZE)
goto fail;
length -= MTP_CONTAINER_HEADER_SIZE;
uint32_t remaining = length;
int initialDataLength = 0;
void* initialData = mData.getData(initialDataLength);
if (initialData) {
if (initialDataLength > 0) {
if (write(fd, initialData, initialDataLength) != initialDataLength) {
free(initialData);
goto fail;
}
remaining -= initialDataLength;
}
free(initialData);
}
// USB reads greater than 16K don't work
char buffer1[16384], buffer2[16384];
mRequestIn1->buffer = buffer1;
mRequestIn2->buffer = buffer2;
struct usb_request* req = mRequestIn1;
void* writeBuffer = NULL;
int writeLength = 0;
while (remaining > 0 || writeBuffer) {
if (remaining > 0) {
// queue up a read request
req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
if (mData.readDataAsync(req)) {
MTPE("readDataAsync failed");
goto fail;
}
} else {
req = NULL;
}
if (writeBuffer) {
// write previous buffer
if (write(fd, writeBuffer, writeLength) != writeLength) {
MTPE("write failed");
// wait for pending read before failing
if (req)
mData.readDataWait(mDevice);
goto fail;
}
writeBuffer = NULL;
}
// wait for read to complete
if (req) {
int read = mData.readDataWait(mDevice);
if (read < 0)
goto fail;
if (read > 0) {
writeBuffer = req->buffer;
writeLength = read;
remaining -= read;
req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
} else {
writeBuffer = NULL;
}
}
}
MtpResponseCode response = readResponse();
if (response == MTP_RESPONSE_OK)
result = true;
}
fail:
::close(fd);
return result;
}
bool MtpDevice::sendRequest(MtpOperationCode operation) {
MTPD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
mReceivedResponse = false;
mRequest.setOperationCode(operation);
if (mTransactionID > 0)
mRequest.setTransactionID(mTransactionID++);
int ret = mRequest.write(mRequestOut);
mRequest.dump();
return (ret > 0);
}
bool MtpDevice::sendData() {
MTPD("sendData\n");
mData.setOperationCode(mRequest.getOperationCode());
mData.setTransactionID(mRequest.getTransactionID());
int ret = mData.write(mRequestOut);
mData.dump();
return (ret > 0);
}
bool MtpDevice::readData() {
mData.reset();
int ret = mData.read(mRequestIn1);
MTPD("readData returned %d\n", ret);
if (ret >= MTP_CONTAINER_HEADER_SIZE) {
if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
MTPD("got response packet instead of data packet");
// we got a response packet rather than data
// copy it to mResponse
mResponse.copyFrom(mData);
mReceivedResponse = true;
return false;
}
mData.dump();
return true;
}
else {
MTPE("readResponse failed\n");
return false;
}
}
bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
mData.setOperationCode(operation);
mData.setTransactionID(mRequest.getTransactionID());
return (!mData.writeDataHeader(mRequestOut, dataLength));
}
MtpResponseCode MtpDevice::readResponse() {
MTPD("readResponse\n");
if (mReceivedResponse) {
mReceivedResponse = false;
return mResponse.getResponseCode();
}
int ret = mResponse.read(mRequestIn1);
// handle zero length packets, which might occur if the data transfer
// ends on a packet boundary
if (ret == 0)
ret = mResponse.read(mRequestIn1);
if (ret >= MTP_CONTAINER_HEADER_SIZE) {
mResponse.dump();
return mResponse.getResponseCode();
} else {
MTPE("readResponse failed\n");
return -1;
}
}