Merge "MTP host: Add support for reading files from an MTP device via ParcelFileDescriptor"
This commit is contained in:
committed by
Android (Google) Code Review
commit
2f493f0ff1
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
@@ -64,6 +65,11 @@ public class MtpClient {
|
||||
return native_get_storage_id(deviceID, objectID);
|
||||
}
|
||||
|
||||
// create a file descriptor for reading the contents of an object over MTP
|
||||
public ParcelFileDescriptor openFile(int deviceID, int objectID) {
|
||||
return native_open_file(deviceID, objectID);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
// called when a new MTP device has been discovered
|
||||
void deviceAdded(int id);
|
||||
@@ -94,4 +100,5 @@ public class MtpClient {
|
||||
private native boolean native_delete_object(int deviceID, int objectID);
|
||||
private native int native_get_parent(int deviceID, int objectID);
|
||||
private native int native_get_storage_id(int deviceID, int objectID);
|
||||
private native ParcelFileDescriptor native_open_file(int deviceID, int objectID);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "MtpClient.h"
|
||||
#include "MtpDevice.h"
|
||||
#include "MtpObjectInfo.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
@@ -38,6 +39,19 @@ static jmethodID method_deviceAdded;
|
||||
static jmethodID method_deviceRemoved;
|
||||
static jfieldID field_context;
|
||||
|
||||
static struct file_descriptor_offsets_t
|
||||
{
|
||||
jclass mClass;
|
||||
jmethodID mConstructor;
|
||||
jfieldID mDescriptor;
|
||||
} gFileDescriptorOffsets;
|
||||
|
||||
static struct parcel_file_descriptor_offsets_t
|
||||
{
|
||||
jclass mClass;
|
||||
jmethodID mConstructor;
|
||||
} gParcelFileDescriptorOffsets;
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
|
||||
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
|
||||
@@ -187,6 +201,38 @@ android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jobject
|
||||
android_media_MtpClient_open_file(JNIEnv *env, jobject thiz,
|
||||
jint device_id, jint object_id)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
|
||||
MtpDevice* device = client->getDevice(device_id);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
MtpObjectInfo* info = device->getObjectInfo(object_id);
|
||||
if (!info)
|
||||
return NULL;
|
||||
int object_size = info->mCompressedSize;
|
||||
delete info;
|
||||
int fd = device->readObject(object_id, object_size);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
|
||||
gFileDescriptorOffsets.mConstructor);
|
||||
if (fileDescriptor != NULL) {
|
||||
env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
|
||||
gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
@@ -197,6 +243,8 @@ static JNINativeMethod gMethods[] = {
|
||||
{"native_delete_object", "(II)Z", (void *)android_media_MtpClient_delete_object},
|
||||
{"native_get_parent", "(II)I", (void *)android_media_MtpClient_get_parent},
|
||||
{"native_get_storage_id", "(II)I", (void *)android_media_MtpClient_get_storage_id},
|
||||
{"native_open_file", "(II)Landroid/os/ParcelFileDescriptor;",
|
||||
(void *)android_media_MtpClient_open_file},
|
||||
};
|
||||
|
||||
static const char* const kClassPathName = "android/media/MtpClient";
|
||||
@@ -228,6 +276,21 @@ int register_android_media_MtpClient(JNIEnv *env)
|
||||
return -1;
|
||||
}
|
||||
|
||||
clazz = env->FindClass("java/io/FileDescriptor");
|
||||
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
|
||||
gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
|
||||
gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
|
||||
gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
|
||||
LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
|
||||
"Unable to find descriptor field in java.io.FileDescriptor");
|
||||
|
||||
clazz = env->FindClass("android/os/ParcelFileDescriptor");
|
||||
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
|
||||
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
|
||||
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
|
||||
LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
|
||||
"Unable to find constructor for android.os.ParcelFileDescriptor");
|
||||
|
||||
return AndroidRuntime::registerNativeMethods(env,
|
||||
"android/media/MtpClient", gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <usbhost/usbhost.h>
|
||||
|
||||
#include "MtpDataPacket.h"
|
||||
#include "MtpStringBuffer.h"
|
||||
|
||||
@@ -391,6 +393,35 @@ int MtpDataPacket::read(struct usb_endpoint *ep) {
|
||||
return length;
|
||||
}
|
||||
|
||||
int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) {
|
||||
int packetSize = usb_endpoint_max_packet(ep);
|
||||
int read = 0;
|
||||
while (read < length) {
|
||||
int ret = transfer(ep, (char *)buffer + read, packetSize);
|
||||
if (ret < 0) {
|
||||
printf("MtpDataPacket::readData returning %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
read += ret;
|
||||
}
|
||||
printf("MtpDataPacket::readData returning %d\n", read);
|
||||
return read;
|
||||
}
|
||||
|
||||
int MtpDataPacket::readDataHeader(struct usb_endpoint *ep) {
|
||||
int length = transfer(ep, mBuffer, usb_endpoint_max_packet(ep));
|
||||
if (length >= 0)
|
||||
mPacketSize = length;
|
||||
return length;
|
||||
}
|
||||
|
||||
int MtpDataPacket::writeDataHeader(struct usb_endpoint *ep, uint32_t length) {
|
||||
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
|
||||
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
|
||||
int ret = transfer(ep, mBuffer, MTP_CONTAINER_HEADER_SIZE);
|
||||
return (ret < 0 ? ret : 0);
|
||||
}
|
||||
|
||||
int MtpDataPacket::write(struct usb_endpoint *ep) {
|
||||
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
|
||||
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
|
||||
@@ -403,6 +434,19 @@ int MtpDataPacket::write(struct usb_endpoint *ep) {
|
||||
return (ret < 0 ? ret : 0);
|
||||
}
|
||||
|
||||
int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) {
|
||||
int ret = 0;
|
||||
int packetSize = usb_endpoint_max_packet(ep);
|
||||
while (length > 0) {
|
||||
int write = (length > packetSize ? packetSize : length);
|
||||
int ret = transfer(ep, buffer, write);
|
||||
if (ret < 0)
|
||||
break;
|
||||
length -= ret;
|
||||
}
|
||||
return (ret < 0 ? ret : 0);
|
||||
}
|
||||
|
||||
#endif // MTP_HOST
|
||||
|
||||
void* MtpDataPacket::getData(int& outLength) const {
|
||||
|
||||
@@ -98,7 +98,12 @@ public:
|
||||
|
||||
#ifdef MTP_HOST
|
||||
int read(struct usb_endpoint *ep);
|
||||
int readData(struct usb_endpoint *ep, void* buffer, int length);
|
||||
int readDataHeader(struct usb_endpoint *ep);
|
||||
|
||||
int writeDataHeader(struct usb_endpoint *ep, uint32_t length);
|
||||
int write(struct usb_endpoint *ep);
|
||||
int write(struct usb_endpoint *ep, void* buffer, uint32_t length);
|
||||
#endif
|
||||
|
||||
inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "MtpProperty.h"
|
||||
#include "MtpStorageInfo.h"
|
||||
#include "MtpStringBuffer.h"
|
||||
#include "MtpUtils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -31,6 +32,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <usbhost/usbhost.h>
|
||||
|
||||
@@ -93,6 +95,8 @@ const char* MtpDevice::getDeviceName() {
|
||||
}
|
||||
|
||||
bool MtpDevice::openSession() {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mSessionID = 0;
|
||||
mTransactionID = 0;
|
||||
MtpSessionID newSession = 1;
|
||||
@@ -117,6 +121,8 @@ bool MtpDevice::closeSession() {
|
||||
}
|
||||
|
||||
MtpDeviceInfo* MtpDevice::getDeviceInfo() {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
|
||||
return NULL;
|
||||
@@ -132,6 +138,8 @@ MtpDeviceInfo* MtpDevice::getDeviceInfo() {
|
||||
}
|
||||
|
||||
MtpStorageIDList* MtpDevice::getStorageIDs() {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
|
||||
return NULL;
|
||||
@@ -145,6 +153,8 @@ MtpStorageIDList* MtpDevice::getStorageIDs() {
|
||||
}
|
||||
|
||||
MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, storageID);
|
||||
if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
|
||||
@@ -162,6 +172,8 @@ MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
|
||||
|
||||
MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
|
||||
MtpObjectFormat format, MtpObjectHandle parent) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, storageID);
|
||||
mRequest.setParameter(2, format);
|
||||
@@ -178,6 +190,8 @@ MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
|
||||
}
|
||||
|
||||
MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
// FIXME - we might want to add some caching here
|
||||
|
||||
mRequest.reset();
|
||||
@@ -196,6 +210,8 @@ MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
|
||||
}
|
||||
|
||||
void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, handle);
|
||||
if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
|
||||
@@ -208,7 +224,90 @@ void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
|
||||
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()) {
|
||||
printf("MTP_OPERATION_SEND_OBJECT_INFO sent\n");
|
||||
MtpResponseCode ret = readResponse();
|
||||
printf("sendObjectInfo response: %04X\n", ret);
|
||||
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) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
int remaining = info->mCompressedSize;
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, info->mHandle);
|
||||
if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
|
||||
printf("MTP_OPERATION_SEND_OBJECT sent\n");
|
||||
// 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(mEndpointOut, buffer, count);
|
||||
printf("wrote %d\n", written);
|
||||
// FIXME check error
|
||||
remaining -= count;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MtpResponseCode ret = readResponse();
|
||||
return (remaining == 0 && ret == MTP_RESPONSE_OK);
|
||||
}
|
||||
|
||||
bool MtpDevice::deleteObject(MtpObjectHandle handle) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, handle);
|
||||
if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
|
||||
@@ -236,6 +335,8 @@ MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
|
||||
}
|
||||
|
||||
MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
|
||||
Mutex::Autolock autoLock(mMutex);
|
||||
|
||||
mRequest.reset();
|
||||
mRequest.setParameter(1, code);
|
||||
if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
|
||||
@@ -251,6 +352,98 @@ MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class ReadObjectThread : public Thread {
|
||||
private:
|
||||
MtpDevice* mDevice;
|
||||
MtpObjectHandle mHandle;
|
||||
int mObjectSize;
|
||||
void* mInitialData;
|
||||
int mInitialDataLength;
|
||||
int mFD;
|
||||
|
||||
public:
|
||||
ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
|
||||
: mDevice(device),
|
||||
mHandle(handle),
|
||||
mObjectSize(objectSize),
|
||||
mInitialData(NULL),
|
||||
mInitialDataLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ReadObjectThread() {
|
||||
if (mFD >= 0)
|
||||
close(mFD);
|
||||
free(mInitialData);
|
||||
}
|
||||
|
||||
// returns file descriptor
|
||||
int init() {
|
||||
mDevice->mRequest.reset();
|
||||
mDevice->mRequest.setParameter(1, mHandle);
|
||||
if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
|
||||
&& mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
|
||||
|
||||
// mData will contain header and possibly the beginning of the object data
|
||||
mInitialData = mDevice->mData.getData(mInitialDataLength);
|
||||
|
||||
// create a pipe for the client to read from
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) < 0) {
|
||||
LOGE("pipe failed (%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mFD = pipefd[1];
|
||||
return pipefd[0];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool threadLoop() {
|
||||
int remaining = mObjectSize;
|
||||
if (mInitialData) {
|
||||
write(mFD, mInitialData, mInitialDataLength);
|
||||
remaining -= mInitialDataLength;
|
||||
free(mInitialData);
|
||||
mInitialData = NULL;
|
||||
}
|
||||
|
||||
char buffer[65536];
|
||||
while (remaining > 0) {
|
||||
int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
|
||||
int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
|
||||
int written;
|
||||
if (count >= 0) {
|
||||
int written = write(mFD, buffer, count);
|
||||
// FIXME check error
|
||||
remaining -= count;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MtpResponseCode ret = mDevice->readResponse();
|
||||
mDevice->mMutex.unlock();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// returns the file descriptor for a pipe to read the object's data
|
||||
int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
|
||||
mMutex.lock();
|
||||
|
||||
ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
|
||||
int fd = thread->init();
|
||||
if (fd < 0) {
|
||||
delete thread;
|
||||
mMutex.unlock();
|
||||
} else {
|
||||
thread->run("ReadObjectThread");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool MtpDevice::sendRequest(MtpOperationCode operation) {
|
||||
LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
|
||||
@@ -262,7 +455,7 @@ bool MtpDevice::sendRequest(MtpOperationCode operation) {
|
||||
return (ret > 0);
|
||||
}
|
||||
|
||||
bool MtpDevice::sendData(MtpOperationCode operation) {
|
||||
bool MtpDevice::sendData() {
|
||||
LOGD("sendData\n");
|
||||
mData.setOperationCode(mRequest.getOperationCode());
|
||||
mData.setTransactionID(mRequest.getTransactionID());
|
||||
@@ -285,6 +478,12 @@ bool MtpDevice::readData() {
|
||||
}
|
||||
}
|
||||
|
||||
bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
|
||||
mData.setOperationCode(operation);
|
||||
mData.setTransactionID(mRequest.getTransactionID());
|
||||
return (!mData.writeDataHeader(mEndpointOut, dataLength));
|
||||
}
|
||||
|
||||
MtpResponseCode MtpDevice::readResponse() {
|
||||
LOGD("readResponse\n");
|
||||
int ret = mResponse.read(mEndpointIn);
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "MtpResponsePacket.h"
|
||||
#include "MtpTypes.h"
|
||||
|
||||
#include <utils/threads.h>
|
||||
|
||||
struct usb_device;
|
||||
|
||||
namespace android {
|
||||
@@ -52,6 +54,9 @@ private:
|
||||
MtpDataPacket mData;
|
||||
MtpResponsePacket mResponse;
|
||||
|
||||
// to ensure only one MTP transaction at a time
|
||||
Mutex mMutex;
|
||||
|
||||
public:
|
||||
MtpDevice(struct usb_device* device, int interface,
|
||||
struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
|
||||
@@ -73,16 +78,24 @@ public:
|
||||
MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
|
||||
MtpObjectInfo* getObjectInfo(MtpObjectHandle handle);
|
||||
void* getThumbnail(MtpObjectHandle handle, int& outLength);
|
||||
MtpObjectHandle sendObjectInfo(MtpObjectInfo* info);
|
||||
bool sendObject(MtpObjectInfo* info, int srcFD);
|
||||
bool deleteObject(MtpObjectHandle handle);
|
||||
MtpObjectHandle getParent(MtpObjectHandle handle);
|
||||
MtpObjectHandle getStorageID(MtpObjectHandle handle);
|
||||
|
||||
MtpProperty* getDevicePropDesc(MtpDeviceProperty code);
|
||||
|
||||
// returns the file descriptor for a pipe to read the object's data
|
||||
int readObject(MtpObjectHandle handle, int objectSize);
|
||||
|
||||
private:
|
||||
friend class ReadObjectThread;
|
||||
|
||||
bool sendRequest(MtpOperationCode operation);
|
||||
bool sendData(MtpOperationCode operation);
|
||||
bool sendData();
|
||||
bool readData();
|
||||
bool writeDataHeader(MtpOperationCode operation, int dataLength);
|
||||
MtpResponseCode readResponse();
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user