diff options
Diffstat (limited to 'media/mtp')
38 files changed, 6202 insertions, 0 deletions
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk new file mode 100644 index 0000000..7502f6e --- /dev/null +++ b/media/mtp/Android.mk @@ -0,0 +1,78 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) + +ifneq ($(TARGET_SIMULATOR),true) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + MtpClient.cpp \ + MtpCursor.cpp \ + MtpDataPacket.cpp \ + MtpDebug.cpp \ + MtpDevice.cpp \ + MtpEventPacket.cpp \ + MtpDeviceInfo.cpp \ + MtpObjectInfo.cpp \ + MtpPacket.cpp \ + MtpProperty.cpp \ + MtpRequestPacket.cpp \ + MtpResponsePacket.cpp \ + MtpServer.cpp \ + MtpStorageInfo.cpp \ + MtpStringBuffer.cpp \ + MtpStorage.cpp \ + MtpUtils.cpp \ + +LOCAL_MODULE:= libmtp + +LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST + +include $(BUILD_STATIC_LIBRARY) + +endif + +ifeq ($(HOST_OS),linux) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + MtpClient.cpp \ + MtpCursor.cpp \ + MtpDataPacket.cpp \ + MtpDebug.cpp \ + MtpDevice.cpp \ + MtpEventPacket.cpp \ + MtpDeviceInfo.cpp \ + MtpObjectInfo.cpp \ + MtpPacket.cpp \ + MtpProperty.cpp \ + MtpRequestPacket.cpp \ + MtpResponsePacket.cpp \ + MtpStorageInfo.cpp \ + MtpStringBuffer.cpp \ + MtpStorage.cpp \ + MtpUtils.cpp \ + +LOCAL_MODULE:= libmtp + +LOCAL_CFLAGS := -DMTP_HOST + +include $(BUILD_HOST_STATIC_LIBRARY) + +endif diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp new file mode 100644 index 0000000..ceb6a43 --- /dev/null +++ b/media/mtp/MtpClient.cpp @@ -0,0 +1,262 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpClient" + +#include "MtpDebug.h" +#include "MtpClient.h" +#include "MtpDevice.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 <usbhost/usbhost.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include <linux/usb/ch9.h> +#else +#include <linux/usb_ch9.h> +#endif + +namespace android { + +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; +} + +class MtpClientThread : public Thread { +private: + MtpClient* mClient; + +public: + MtpClientThread(MtpClient* client) + : mClient(client) + { + } + + virtual bool threadLoop() { + return mClient->threadLoop(); + } +}; + + +MtpClient::MtpClient() + : mThread(NULL), + mUsbHostContext(NULL), + mDone(false) +{ +} + +MtpClient::~MtpClient() { + usb_host_cleanup(mUsbHostContext); +} + +bool MtpClient::start() { + Mutex::Autolock autoLock(mMutex); + + if (mThread) + return true; + + mUsbHostContext = usb_host_init(); + if (!mUsbHostContext) + return false; + + mThread = new MtpClientThread(this); + mThread->run("MtpClientThread"); + // wait for the thread to do initial device discovery before returning + mThreadStartCondition.wait(mMutex); + + return true; +} + +void MtpClient::stop() { + mDone = true; +} + +MtpDevice* MtpClient::getDevice(int id) { + for (int i = 0; i < mDeviceList.size(); i++) { + MtpDevice* device = mDeviceList[i]; + if (device->getID() == id) + return device; + } + return NULL; +} + +bool MtpClient::usbDeviceAdded(const char *devname) { + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + struct usb_device *device = usb_device_open(devname); + if (!device) { + LOGE("usb_device_open failed\n"); + return mDone; + } + + 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) + { + LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), + usb_device_get_product_name(device)); + } else if (interface->bInterfaceClass == 0xFF && + interface->bInterfaceSubClass == 0xFF && + interface->bInterfaceProtocol == 0) { + char* interfaceName = usb_device_get_string(device, interface->iInterface); + if (!interfaceName || strcmp(interfaceName, "MTP")) + continue; + // Looks like an android style MTP device + LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), + usb_device_get_product_name(device)); + } 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_send_control(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD, + USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE, + 0, sizeof(buffer), buffer); + printf("usb_device_send_control returned %d errno: %d\n", ret, errno); + if (ret > 0) { + printf("got MTP string %s\n", buffer); + ret = usb_device_send_control(device, + USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1, + 0, 4, sizeof(buffer), buffer); + printf("OS descriptor got %d\n", ret); + } else { + printf("no MTP string\n"); + } + } + + // 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) { + LOGE("endpoints not found\n"); + return mDone; + } + 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) { + LOGE("endpoints not found\n"); + return mDone; + } + + struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc); + struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc); + struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc); + + if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { + LOGE("usb_device_claim_interface failed errno: %d\n", errno); + usb_endpoint_close(ep_in); + usb_endpoint_close(ep_out); + usb_endpoint_close(ep_intr); + return mDone; + } + + MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, + ep_in, ep_out, ep_intr); + mDeviceList.add(mtpDevice); + mtpDevice->initialize(); + deviceAdded(mtpDevice); + return mDone; + } + } + + usb_device_close(device); + return mDone; +} + +bool MtpClient::usbDeviceRemoved(const char *devname) { + for (int i = 0; i < mDeviceList.size(); i++) { + MtpDevice* device = mDeviceList[i]; + if (!strcmp(devname, device->getDeviceName())) { + deviceRemoved(device); + mDeviceList.removeAt(i); + delete device; + LOGD("Camera removed!\n"); + break; + } + } + return mDone; +} + +bool MtpClient::usbDiscoveryDone() { + Mutex::Autolock autoLock(mMutex); + mThreadStartCondition.signal(); + return mDone; +} + +bool MtpClient::threadLoop() { + usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this); + return false; +} + +int MtpClient::usb_device_added(const char *devname, void* client_data) { + LOGD("usb_device_added %s\n", devname); + return ((MtpClient *)client_data)->usbDeviceAdded(devname); +} + +int MtpClient::usb_device_removed(const char *devname, void* client_data) { + LOGD("usb_device_removed %s\n", devname); + return ((MtpClient *)client_data)->usbDeviceRemoved(devname); +} + +int MtpClient::usb_discovery_done(void* client_data) { + LOGD("usb_discovery_done\n"); + return ((MtpClient *)client_data)->usbDiscoveryDone(); +} + +} // namespace android diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h new file mode 100644 index 0000000..fa5c527 --- /dev/null +++ b/media/mtp/MtpClient.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef _MTP_CLIENT_H +#define _MTP_CLIENT_H + +#include "MtpTypes.h" + +#include <utils/threads.h> + +struct usb_host_context; + +namespace android { + +class MtpClientThread; + +class MtpClient { +private: + MtpDeviceList mDeviceList; + MtpClientThread* mThread; + Condition mThreadStartCondition; + Mutex mMutex; + struct usb_host_context* mUsbHostContext; + bool mDone; + +public: + MtpClient(); + virtual ~MtpClient(); + + bool start(); + void stop(); + + inline MtpDeviceList& getDeviceList() { return mDeviceList; } + MtpDevice* getDevice(int id); + + + virtual void deviceAdded(MtpDevice *device) = 0; + virtual void deviceRemoved(MtpDevice *device) = 0; + +private: + // these return true if we should stop monitoring USB and clean up + bool usbDeviceAdded(const char *devname); + bool usbDeviceRemoved(const char *devname); + bool usbDiscoveryDone(); + + friend class MtpClientThread; + bool threadLoop(); + static int usb_device_added(const char *devname, void* client_data); + static int usb_device_removed(const char *devname, void* client_data); + static int usb_discovery_done(void* client_data); +}; + +}; // namespace android + +#endif // _MTP_CLIENT_H diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp new file mode 100644 index 0000000..35d90dc --- /dev/null +++ b/media/mtp/MtpCursor.cpp @@ -0,0 +1,461 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpCursor" + +#include "MtpDebug.h" +#include "MtpClient.h" +#include "MtpCursor.h" +#include "MtpDevice.h" +#include "MtpDeviceInfo.h" +#include "MtpObjectInfo.h" +#include "MtpStorageInfo.h" + + +#include "binder/CursorWindow.h" + +namespace android { + +/* Device Column IDs */ +/* These must match the values in MtpCursor.java */ +#define DEVICE_ROW_ID 1 +#define DEVICE_MANUFACTURER 2 +#define DEVICE_MODEL 3 + +/* Storage Column IDs */ +/* These must match the values in MtpCursor.java */ +#define STORAGE_ROW_ID 101 +#define STORAGE_IDENTIFIER 102 +#define STORAGE_DESCRIPTION 103 + +/* Object Column IDs */ +/* These must match the values in MtpCursor.java */ +#define OBJECT_ROW_ID 201 +#define OBJECT_STORAGE_ID 202 +#define OBJECT_FORMAT 203 +#define OBJECT_PROTECTION_STATUS 204 +#define OBJECT_SIZE 205 +#define OBJECT_THUMB_FORMAT 206 +#define OBJECT_THUMB_SIZE 207 +#define OBJECT_THUMB_WIDTH 208 +#define OBJECT_THUMB_HEIGHT 209 +#define OBJECT_IMAGE_WIDTH 210 +#define OBJECT_IMAGE_HEIGHT 211 +#define OBJECT_IMAGE_DEPTH 212 +#define OBJECT_PARENT 213 +#define OBJECT_ASSOCIATION_TYPE 214 +#define OBJECT_ASSOCIATION_DESC 215 +#define OBJECT_SEQUENCE_NUMBER 216 +#define OBJECT_NAME 217 +#define OBJECT_DATE_CREATED 218 +#define OBJECT_DATE_MODIFIED 219 +#define OBJECT_KEYWORDS 220 +#define OBJECT_THUMB 221 + +MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID, + MtpStorageID storageID, MtpObjectHandle objectID, + int columnCount, int* columns) + : mClient(client), + mQueryType(queryType), + mDeviceID(deviceID), + mStorageID(storageID), + mQbjectID(objectID), + mColumnCount(columnCount), + mColumns(NULL) +{ + if (columns) { + mColumns = new int[columnCount]; + memcpy(mColumns, columns, columnCount * sizeof(int)); + } +} + +MtpCursor::~MtpCursor() { + delete[] mColumns; +} + +int MtpCursor::fillWindow(CursorWindow* window, int startPos) { + LOGD("MtpCursor::fillWindow mQueryType: %d\n", mQueryType); + + switch (mQueryType) { + case DEVICE: + return fillDevices(window, startPos); + case DEVICE_ID: + return fillDevice(window, startPos); + case STORAGE: + return fillStorages(window, startPos); + case STORAGE_ID: + return fillStorage(window, startPos); + case OBJECT: + return fillObjects(window, 0, startPos); + case OBJECT_ID: + return fillObject(window, startPos); + case STORAGE_CHILDREN: + return fillObjects(window, -1, startPos); + case OBJECT_CHILDREN: + return fillObjects(window, mQbjectID, startPos); + default: + LOGE("MtpCursor::fillWindow: unknown query type %d\n", mQueryType); + return 0; + } +} + +int MtpCursor::fillDevices(CursorWindow* window, int startPos) { + int count = 0; + MtpDeviceList& deviceList = mClient->getDeviceList(); + for (int i = 0; i < deviceList.size(); i++) { + MtpDevice* device = deviceList[i]; + if (fillDevice(window, device, startPos)) { + count++; + startPos++; + } else { + break; + } + } + return count; +} + +int MtpCursor::fillDevice(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillDevice(window, device, startPos)) + return 1; + else + return 0; +} + +int MtpCursor::fillStorages(CursorWindow* window, int startPos) { + int count = 0; + MtpDevice* device = mClient->getDevice(mDeviceID); + if (!device) + return 0; + MtpStorageIDList* storageIDs = device->getStorageIDs(); + if (!storageIDs) + return 0; + + for (int i = 0; i < storageIDs->size(); i++) { + MtpStorageID storageID = (*storageIDs)[i]; + if (fillStorage(window, device, storageID, startPos)) { + count++; + startPos++; + } else { + break; + } + } + delete storageIDs; + return count; +} + +int MtpCursor::fillStorage(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillStorage(window, device, mStorageID, startPos)) + return 1; + else + return 0; +} + +int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) { + int count = 0; + MtpDevice* device = mClient->getDevice(mDeviceID); + if (!device) + return 0; + MtpObjectHandleList* handles = device->getObjectHandles(mStorageID, 0, parent); + if (!handles) + return 0; + + for (int i = 0; i < handles->size(); i++) { + MtpObjectHandle handle = (*handles)[i]; + if (fillObject(window, device, handle, startPos)) { + count++; + startPos++; + } else { + break; + } + } + delete handles; + return count; +} + +int MtpCursor::fillObject(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillObject(window, device, mQbjectID, startPos)) + return 1; + else + return 0; +} + +bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) { + MtpDeviceInfo* deviceInfo = device->getDeviceInfo(); + if (!deviceInfo) + return false; + if (!prepareRow(window)) + return false; + + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case DEVICE_ROW_ID: + if (!putLong(window, device->getID(), row, i)) + return false; + break; + case DEVICE_MANUFACTURER: + if (!putString(window, deviceInfo->mManufacturer, row, i)) + return false; + break; + case DEVICE_MODEL: + if (!putString(window, deviceInfo->mModel, row, i)) + return false; + break; + default: + LOGE("fillDevice: unknown column %d\n", mColumns[i]); + return false; + } + } + + return true; +} + +bool MtpCursor::fillStorage(CursorWindow* window, MtpDevice* device, + MtpStorageID storageID, int row) { + +LOGD("fillStorage %d\n", storageID); + + MtpStorageInfo* storageInfo = device->getStorageInfo(storageID); + if (!storageInfo) + return false; + if (!prepareRow(window)) { + delete storageInfo; + return false; + } + + const char* text; + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case STORAGE_ROW_ID: + if (!putLong(window, storageID, row, i)) + goto fail; + break; + case STORAGE_IDENTIFIER: + text = storageInfo->mVolumeIdentifier; + if (!text || !text[0]) + text = "Camera Storage"; + if (!putString(window, text, row, i)) + goto fail; + break; + case STORAGE_DESCRIPTION: + text = storageInfo->mStorageDescription; + if (!text || !text[0]) + text = "Storage Description"; + if (!putString(window, text, row, i)) + goto fail; + break; + default: + LOGE("fillStorage: unknown column %d\n", mColumns[i]); + goto fail; + } + } + + delete storageInfo; + return true; + +fail: + delete storageInfo; + return false; +} + +bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device, + MtpObjectHandle objectID, int row) { + + MtpObjectInfo* objectInfo = device->getObjectInfo(objectID); + if (!objectInfo) + return false; + // objectInfo->print(); + if (!prepareRow(window)) { + delete objectInfo; + return false; + } + + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case OBJECT_ROW_ID: + if (!putLong(window, objectID, row, i)) + goto fail; + break; + case OBJECT_STORAGE_ID: + if (!putLong(window, objectInfo->mStorageID, row, i)) + goto fail; + break; + case OBJECT_FORMAT: + if (!putLong(window, objectInfo->mFormat, row, i)) + goto fail; + break; + case OBJECT_PROTECTION_STATUS: + if (!putLong(window, objectInfo->mProtectionStatus, row, i)) + goto fail; + break; + case OBJECT_SIZE: + if (!putLong(window, objectInfo->mCompressedSize, row, i)) + goto fail; + break; + case OBJECT_THUMB_FORMAT: + if (!putLong(window, objectInfo->mThumbFormat, row, i)) + goto fail; + break; + case OBJECT_THUMB_SIZE: + if (!putLong(window, objectInfo->mThumbCompressedSize, row, i)) + goto fail; + break; + case OBJECT_THUMB_WIDTH: + if (!putLong(window, objectInfo->mThumbPixWidth, row, i)) + goto fail; + break; + case OBJECT_THUMB_HEIGHT: + if (!putLong(window, objectInfo->mThumbPixHeight, row, i)) + goto fail; + break; + case OBJECT_IMAGE_WIDTH: + if (!putLong(window, objectInfo->mImagePixWidth, row, i)) + goto fail; + break; + case OBJECT_IMAGE_HEIGHT: + if (!putLong(window, objectInfo->mImagePixHeight, row, i)) + goto fail; + break; + case OBJECT_IMAGE_DEPTH: + if (!putLong(window, objectInfo->mImagePixDepth, row, i)) + goto fail; + break; + case OBJECT_PARENT: + if (!putLong(window, objectInfo->mParent, row, i)) + goto fail; + break; + case OBJECT_ASSOCIATION_TYPE: + if (!putLong(window, objectInfo->mAssociationType, row, i)) + goto fail; + break; + case OBJECT_ASSOCIATION_DESC: + if (!putLong(window, objectInfo->mAssociationDesc, row, i)) + goto fail; + break; + case OBJECT_SEQUENCE_NUMBER: + if (!putLong(window, objectInfo->mSequenceNumber, row, i)) + goto fail; + break; + case OBJECT_NAME: + if (!putString(window, objectInfo->mName, row, i)) + goto fail; + break; + case OBJECT_DATE_CREATED: + if (!putLong(window, objectInfo->mDateCreated, row, i)) + goto fail; + break; + case OBJECT_DATE_MODIFIED: + if (!putLong(window, objectInfo->mDateModified, row, i)) + goto fail; + break; + case OBJECT_KEYWORDS: + if (!putString(window, objectInfo->mKeywords, row, i)) + goto fail; + break; + case OBJECT_THUMB: + if (!putThumbnail(window, objectID, objectInfo->mFormat, row, i)) + goto fail; + break; + default: + LOGE("fillObject: unknown column %d\n", mColumns[i]); + goto fail; + } + } + + delete objectInfo; + return true; + +fail: + delete objectInfo; + return false; +} + +bool MtpCursor::prepareRow(CursorWindow* window) { + if (!window->setNumColumns(mColumnCount)) { + LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount); + return false; + } + field_slot_t * fieldDir = window->allocRow(); + if (!fieldDir) { + LOGE("Failed allocating fieldDir"); + return false; + } + return true; +} + + +bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) { + if (!window->putLong(row, column, value)) { + window->freeLastRow(); + LOGE("Failed allocating space for a long in column %d", column); + return false; + } + return true; +} + +bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int column) { + int size = strlen(text) + 1; + int offset = window->alloc(size); + if (!offset) { + window->freeLastRow(); + LOGE("Failed allocating %u bytes for text/blob %s", size, text); + return false; + } + window->copyIn(offset, (const uint8_t*)text, size); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(row, column); + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + return true; +} + +bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID, + MtpObjectFormat format, int row, int column) { + MtpDevice* device = mClient->getDevice(mDeviceID); + void* thumbnail; + int size, offset; + if (format == MTP_FORMAT_ASSOCIATION) { + thumbnail = NULL; + size = offset = 0; + } else { + thumbnail = device->getThumbnail(objectID, size); + + LOGV("putThumbnail: %p, size: %d\n", thumbnail, size); + offset = window->alloc(size); + if (!offset) { + window->freeLastRow(); + LOGE("Failed allocating %u bytes for thumbnail", size); + return false; + } + } + if (thumbnail) + window->copyIn(offset, (const uint8_t*)thumbnail, size); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(row, column); + fieldSlot->type = FIELD_TYPE_BLOB; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + return true; +} + +} // namespace android diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h new file mode 100644 index 0000000..2e03c29 --- /dev/null +++ b/media/mtp/MtpCursor.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef _MTP_CURSOR_H +#define _MTP_CURSOR_H + +#include "MtpTypes.h" + +namespace android { + +class CursorWindow; + +class MtpCursor { +private: + enum { + DEVICE = 1, + DEVICE_ID = 2, + STORAGE = 3, + STORAGE_ID = 4, + OBJECT = 5, + OBJECT_ID = 6, + STORAGE_CHILDREN = 7, + OBJECT_CHILDREN = 8, + }; + + MtpClient* mClient; + int mQueryType; + int mDeviceID; + MtpStorageID mStorageID; + MtpObjectHandle mQbjectID; + int mColumnCount; + int* mColumns; + +public: + MtpCursor(MtpClient* client, int queryType, int deviceID, + MtpStorageID storageID, MtpObjectHandle objectID, + int columnCount, int* columns); + virtual ~MtpCursor(); + + int fillWindow(CursorWindow* window, int startPos); + +private: + int fillDevices(CursorWindow* window, int startPos); + int fillDevice(CursorWindow* window, int startPos); + int fillStorages(CursorWindow* window, int startPos); + int fillStorage(CursorWindow* window, int startPos); + int fillObjects(CursorWindow* window, int parent, int startPos); + int fillObject(CursorWindow* window, int startPos); + + bool fillDevice(CursorWindow* window, MtpDevice* device, int startPos); + bool fillStorage(CursorWindow* window, MtpDevice* device, + MtpStorageID storageID, int row); + bool fillObject(CursorWindow* window, MtpDevice* device, + MtpObjectHandle objectID, int row); + + bool prepareRow(CursorWindow* window); + bool putLong(CursorWindow* window, int64_t value, int row, int column); + bool putString(CursorWindow* window, const char* text, int row, int column); + bool putThumbnail(CursorWindow* window, MtpObjectHandle objectID, + MtpObjectFormat format, int row, int column); +}; + +}; // namespace android + +#endif // _MTP_CURSOR_H diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp new file mode 100644 index 0000000..e1d1a92 --- /dev/null +++ b/media/mtp/MtpDataPacket.cpp @@ -0,0 +1,489 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpDataPacket" + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> + +#include <usbhost/usbhost.h> + +#include "MtpDataPacket.h" +#include "MtpStringBuffer.h" + +namespace android { + +MtpDataPacket::MtpDataPacket() + : MtpPacket(512), + mOffset(MTP_CONTAINER_HEADER_SIZE) +{ +} + +MtpDataPacket::~MtpDataPacket() { +} + +void MtpDataPacket::reset() { + MtpPacket::reset(); + mOffset = MTP_CONTAINER_HEADER_SIZE; +} + +void MtpDataPacket::setOperationCode(MtpOperationCode code) { + MtpPacket::putUInt16(MTP_CONTAINER_CODE_OFFSET, code); +} + +void MtpDataPacket::setTransactionID(MtpTransactionID id) { + MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id); +} + +uint16_t MtpDataPacket::getUInt16() { + int offset = mOffset; + uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8); + mOffset += 2; + return result; +} + +uint32_t MtpDataPacket::getUInt32() { + int offset = mOffset; + uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) | + ((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24); + mOffset += 4; + return result; +} + +uint64_t MtpDataPacket::getUInt64() { + int offset = mOffset; + uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) | + ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) | + ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) | + ((uint64_t)mBuffer[offset + 6] << 48) | ((uint64_t)mBuffer[offset + 7] << 56); + mOffset += 8; + return result; +} + +void MtpDataPacket::getUInt128(uint128_t& value) { + value[0] = getUInt32(); + value[1] = getUInt32(); + value[2] = getUInt32(); + value[3] = getUInt32(); +} + +void MtpDataPacket::getString(MtpStringBuffer& string) +{ + string.readFromPacket(this); +} + +Int8List* MtpDataPacket::getAInt8() { + Int8List* result = new Int8List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getInt8()); + return result; +} + +UInt8List* MtpDataPacket::getAUInt8() { + UInt8List* result = new UInt8List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getUInt8()); + return result; +} + +Int16List* MtpDataPacket::getAInt16() { + Int16List* result = new Int16List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getInt16()); + return result; +} + +UInt16List* MtpDataPacket::getAUInt16() { + UInt16List* result = new UInt16List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getUInt16()); + return result; +} + +Int32List* MtpDataPacket::getAInt32() { + Int32List* result = new Int32List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getInt32()); + return result; +} + +UInt32List* MtpDataPacket::getAUInt32() { + UInt32List* result = new UInt32List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getUInt32()); + return result; +} + +Int64List* MtpDataPacket::getAInt64() { + Int64List* result = new Int64List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getInt64()); + return result; +} + +UInt64List* MtpDataPacket::getAUInt64() { + UInt64List* result = new UInt64List; + int count = getUInt32(); + for (int i = 0; i < count; i++) + result->push(getUInt64()); + return result; +} + +void MtpDataPacket::putInt8(int8_t value) { + allocate(mOffset + 1); + mBuffer[mOffset++] = (uint8_t)value; + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putUInt8(uint8_t value) { + allocate(mOffset + 1); + mBuffer[mOffset++] = (uint8_t)value; + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putInt16(int16_t value) { + allocate(mOffset + 2); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putUInt16(uint16_t value) { + allocate(mOffset + 2); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putInt32(int32_t value) { + allocate(mOffset + 4); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putUInt32(uint32_t value) { + allocate(mOffset + 4); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putInt64(int64_t value) { + allocate(mOffset + 8); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putUInt64(uint64_t value) { + allocate(mOffset + 8); + mBuffer[mOffset++] = (uint8_t)(value & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF); + mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF); + if (mPacketSize < mOffset) + mPacketSize = mOffset; +} + +void MtpDataPacket::putInt128(const int128_t& value) { + putInt32(value[0]); + putInt32(value[1]); + putInt32(value[2]); + putInt32(value[3]); +} + +void MtpDataPacket::putUInt128(const uint128_t& value) { + putUInt32(value[0]); + putUInt32(value[1]); + putUInt32(value[2]); + putUInt32(value[3]); +} + +void MtpDataPacket::putInt128(int64_t value) { + putInt64(value); + putInt64(value < 0 ? -1 : 0); +} + +void MtpDataPacket::putUInt128(uint64_t value) { + putUInt64(value); + putUInt64(0); +} + +void MtpDataPacket::putAInt8(const int8_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putInt8(*values++); +} + +void MtpDataPacket::putAUInt8(const uint8_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putUInt8(*values++); +} + +void MtpDataPacket::putAInt16(const int16_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putInt16(*values++); +} + +void MtpDataPacket::putAUInt16(const uint16_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putUInt16(*values++); +} + +void MtpDataPacket::putAUInt16(const UInt16List* values) { + size_t count = (values ? values->size() : 0); + putUInt32(count); + for (size_t i = 0; i < count; i++) + putUInt16((*values)[i]); +} + +void MtpDataPacket::putAInt32(const int32_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putInt32(*values++); +} + +void MtpDataPacket::putAUInt32(const uint32_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putUInt32(*values++); +} + +void MtpDataPacket::putAUInt32(const UInt32List* list) { + if (!list) { + putEmptyArray(); + } else { + size_t size = list->size(); + putUInt32(size); + for (size_t i = 0; i < size; i++) + putUInt32((*list)[i]); + } +} + +void MtpDataPacket::putAInt64(const int64_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putInt64(*values++); +} + +void MtpDataPacket::putAUInt64(const uint64_t* values, int count) { + putUInt32(count); + for (int i = 0; i < count; i++) + putUInt64(*values++); +} + +void MtpDataPacket::putString(const MtpStringBuffer& string) { + string.writeToPacket(this); +} + +void MtpDataPacket::putString(const char* s) { + MtpStringBuffer string(s); + string.writeToPacket(this); +} + +void MtpDataPacket::putString(const uint16_t* string) { + int count = 0; + for (int i = 0; i < 256; i++) { + if (string[i]) + count++; + else + break; + } + putUInt8(count > 0 ? count + 1 : 0); + for (int i = 0; i < count; i++) + putUInt16(string[i]); + // only terminate with zero if string is not empty + if (count > 0) + putUInt16(0); +} + +#ifdef MTP_DEVICE +int MtpDataPacket::read(int fd) { + // first read the header + int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret != MTP_CONTAINER_HEADER_SIZE) + return -1; + // then the following data + int total = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); + int remaining = total - MTP_CONTAINER_HEADER_SIZE; + ret = ::read(fd, &mBuffer[0] + MTP_CONTAINER_HEADER_SIZE, remaining); + if (ret != remaining) + return -1; + + mPacketSize = total; + mOffset = MTP_CONTAINER_HEADER_SIZE; + return total; +} + +int MtpDataPacket::readDataHeader(int fd) { + int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret > 0) + mPacketSize = ret; + else + mPacketSize = 0; + return ret; +} + +int MtpDataPacket::write(int fd) { + MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); + MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); + // send header separately from data + int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret == MTP_CONTAINER_HEADER_SIZE) + ret = ::write(fd, mBuffer + MTP_CONTAINER_HEADER_SIZE, + mPacketSize - MTP_CONTAINER_HEADER_SIZE); + return (ret < 0 ? ret : 0); +} + +int MtpDataPacket::writeDataHeader(int fd, uint32_t length) { + MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length); + MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); + int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + return (ret < 0 ? ret : 0); +} +#endif // MTP_DEVICE + +#ifdef MTP_HOST +int MtpDataPacket::read(struct usb_endpoint *ep) { + // first read the header + int length = transfer(ep, mBuffer, mBufferSize); + if (length >= MTP_CONTAINER_HEADER_SIZE) { + // look at the length field to see if the data spans multiple packets + uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); + while (totalLength > length) { + allocate(length + mAllocationIncrement); + int ret = transfer(ep, mBuffer + length, mAllocationIncrement); + if (ret >= 0) + length += ret; + else { + length = ret; + break; + } + } + } + if (length >= 0) + mPacketSize = length; + return length; +} + +int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) { + int read = 0; + while (read < length) { + int ret = transfer(ep, (char *)buffer + read, length - read); + if (ret < 0) { + return ret; + } + read += ret; + } + return read; +} + +// Queue a read request. Call readDataWait to wait for result +int MtpDataPacket::readDataAsync(struct usb_endpoint *ep, void* buffer, int length) { + if (usb_endpoint_queue(ep, buffer, length)) { + LOGE("usb_endpoint_queue failed, errno: %d", errno); + return -1; + } + return 0; +} + +// Wait for result of readDataAsync +int MtpDataPacket::readDataWait(struct usb_endpoint *ep) { + int ep_num; + return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num); +} + +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); + + // send header separately from data + int ret = transfer(ep, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret == MTP_CONTAINER_HEADER_SIZE) + ret = transfer(ep, mBuffer + MTP_CONTAINER_HEADER_SIZE, + mPacketSize - MTP_CONTAINER_HEADER_SIZE); + return (ret < 0 ? ret : 0); +} + +int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) { + int ret = transfer(ep, buffer, length); + return (ret < 0 ? ret : 0); +} + +#endif // MTP_HOST + +void* MtpDataPacket::getData(int& outLength) const { + int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE; + if (length > 0) { + void* result = malloc(length); + if (result) { + memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length); + outLength = length; + return result; + } + } + outLength = 0; + return NULL; +} + +} // namespace android diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h new file mode 100644 index 0000000..3ae6226 --- /dev/null +++ b/media/mtp/MtpDataPacket.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#ifndef _MTP_DATA_PACKET_H +#define _MTP_DATA_PACKET_H + +#include "MtpPacket.h" +#include "mtp.h" + +namespace android { + +class MtpStringBuffer; + +class MtpDataPacket : public MtpPacket { +private: + // current offset for get/put methods + int mOffset; + +public: + MtpDataPacket(); + virtual ~MtpDataPacket(); + + virtual void reset(); + + void setOperationCode(MtpOperationCode code); + void setTransactionID(MtpTransactionID id); + + inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; } + inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; } + uint16_t getUInt16(); + inline int16_t getInt16() { return (int16_t)getUInt16(); } + uint32_t getUInt32(); + inline int32_t getInt32() { return (int32_t)getUInt32(); } + uint64_t getUInt64(); + inline int64_t getInt64() { return (int64_t)getUInt64(); } + void getUInt128(uint128_t& value); + inline void getInt128(int128_t& value) { getUInt128((uint128_t&)value); } + void getString(MtpStringBuffer& string); + + Int8List* getAInt8(); + UInt8List* getAUInt8(); + Int16List* getAInt16(); + UInt16List* getAUInt16(); + Int32List* getAInt32(); + UInt32List* getAUInt32(); + Int64List* getAInt64(); + UInt64List* getAUInt64(); + + void putInt8(int8_t value); + void putUInt8(uint8_t value); + void putInt16(int16_t value); + void putUInt16(uint16_t value); + void putInt32(int32_t value); + void putUInt32(uint32_t value); + void putInt64(int64_t value); + void putUInt64(uint64_t value); + void putInt128(const int128_t& value); + void putUInt128(const uint128_t& value); + void putInt128(int64_t value); + void putUInt128(uint64_t value); + + void putAInt8(const int8_t* values, int count); + void putAUInt8(const uint8_t* values, int count); + void putAInt16(const int16_t* values, int count); + void putAUInt16(const uint16_t* values, int count); + void putAUInt16(const UInt16List* values); + void putAInt32(const int32_t* values, int count); + void putAUInt32(const uint32_t* values, int count); + void putAUInt32(const UInt32List* list); + void putAInt64(const int64_t* values, int count); + void putAUInt64(const uint64_t* values, int count); + void putString(const MtpStringBuffer& string); + void putString(const char* string); + void putString(const uint16_t* string); + inline void putEmptyString() { putUInt8(0); } + inline void putEmptyArray() { putUInt32(0); } + + +#ifdef MTP_DEVICE + // fill our buffer with data from the given file descriptor + int read(int fd); + int readDataHeader(int fd); + + // write our data to the given file descriptor + int write(int fd); + int writeDataHeader(int fd, uint32_t length); +#endif + +#ifdef MTP_HOST + int read(struct usb_endpoint *ep); + int readData(struct usb_endpoint *ep, void* buffer, int length); + int readDataAsync(struct usb_endpoint *ep, void* buffer, int length); + int readDataWait(struct usb_endpoint *ep); + 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; } + inline uint32_t getContainerLength() const { return MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); } + void* getData(int& outLength) const; +}; + +}; // namespace android + +#endif // _MTP_DATA_PACKET_H diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h new file mode 100644 index 0000000..900b517 --- /dev/null +++ b/media/mtp/MtpDatabase.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef _MTP_DATABASE_H +#define _MTP_DATABASE_H + +#include "MtpTypes.h" + +namespace android { + +class MtpDataPacket; +class MtpProperty; + +class MtpDatabase { +public: + virtual ~MtpDatabase() {} + + // called from SendObjectInfo to reserve a database entry for the incoming file + virtual MtpObjectHandle beginSendObject(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified) = 0; + + // called to report success or failure of the SendObject file transfer + // success should signal a notification of the new object's creation, + // failure should remove the database entry created in beginSendObject + virtual void endSendObject(const char* path, + MtpObjectHandle handle, + MtpObjectFormat format, + int64_t size, + bool succeeded) = 0; + + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent) = 0; + + virtual int getNumObjects(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent) = 0; + + // callee should delete[] the results from these + // results can be NULL + virtual MtpObjectFormatList* getSupportedPlaybackFormats() = 0; + virtual MtpObjectFormatList* getSupportedCaptureFormats() = 0; + virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format) = 0; + virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0; + + virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0; + + virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, + MtpObjectFormat format, + MtpObjectProperty property, + int groupCode, int depth, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet) = 0; + + virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength) = 0; + + virtual MtpResponseCode deleteFile(MtpObjectHandle handle) = 0; + + virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle) = 0; + + virtual MtpResponseCode setObjectReferences(MtpObjectHandle handle, + MtpObjectHandleList* references) = 0; + + virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty property, + MtpObjectFormat format) = 0; + + virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0; + + virtual void sessionStarted() = 0; + + virtual void sessionEnded() = 0; +}; + +}; // namespace android + +#endif // _MTP_DATABASE_H diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp new file mode 100644 index 0000000..1668ecf --- /dev/null +++ b/media/mtp/MtpDebug.cpp @@ -0,0 +1,396 @@ +/* + * 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. + */ + +#include "MtpDebug.h" + +namespace android { + +struct CodeEntry { + const char* name; + uint16_t code; +}; + +static const CodeEntry sOperationCodes[] = { + { "MTP_OPERATION_GET_DEVICE_INFO", 0x1001 }, + { "MTP_OPERATION_OPEN_SESSION", 0x1002 }, + { "MTP_OPERATION_CLOSE_SESSION", 0x1003 }, + { "MTP_OPERATION_GET_STORAGE_IDS", 0x1004 }, + { "MTP_OPERATION_GET_STORAGE_INFO", 0x1005 }, + { "MTP_OPERATION_GET_NUM_OBJECTS", 0x1006 }, + { "MTP_OPERATION_GET_OBJECT_HANDLES", 0x1007 }, + { "MTP_OPERATION_GET_OBJECT_INFO", 0x1008 }, + { "MTP_OPERATION_GET_OBJECT", 0x1009 }, + { "MTP_OPERATION_GET_THUMB", 0x100A }, + { "MTP_OPERATION_DELETE_OBJECT", 0x100B }, + { "MTP_OPERATION_SEND_OBJECT_INFO", 0x100C }, + { "MTP_OPERATION_SEND_OBJECT", 0x100D }, + { "MTP_OPERATION_INITIATE_CAPTURE", 0x100E }, + { "MTP_OPERATION_FORMAT_STORE", 0x100F }, + { "MTP_OPERATION_RESET_DEVICE", 0x1010 }, + { "MTP_OPERATION_SELF_TEST", 0x1011 }, + { "MTP_OPERATION_SET_OBJECT_PROTECTION", 0x1012 }, + { "MTP_OPERATION_POWER_DOWN", 0x1013 }, + { "MTP_OPERATION_GET_DEVICE_PROP_DESC", 0x1014 }, + { "MTP_OPERATION_GET_DEVICE_PROP_VALUE", 0x1015 }, + { "MTP_OPERATION_SET_DEVICE_PROP_VALUE", 0x1016 }, + { "MTP_OPERATION_RESET_DEVICE_PROP_VALUE", 0x1017 }, + { "MTP_OPERATION_TERMINATE_OPEN_CAPTURE", 0x1018 }, + { "MTP_OPERATION_MOVE_OBJECT", 0x1019 }, + { "MTP_OPERATION_COPY_OBJECT", 0x101A }, + { "MTP_OPERATION_GET_PARTIAL_OBJECT", 0x101B }, + { "MTP_OPERATION_INITIATE_OPEN_CAPTURE", 0x101C }, + { "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED", 0x9801 }, + { "MTP_OPERATION_GET_OBJECT_PROP_DESC", 0x9802 }, + { "MTP_OPERATION_GET_OBJECT_PROP_VALUE", 0x9803 }, + { "MTP_OPERATION_SET_OBJECT_PROP_VALUE", 0x9804 }, + { "MTP_OPERATION_GET_OBJECT_PROP_LIST", 0x9805 }, + { "MTP_OPERATION_SET_OBJECT_PROP_LIST", 0x9806 }, + { "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 }, + { "MTP_OPERATION_SEND_OBJECT_PROP_LIST", 0x9808 }, + { "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 }, + { "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 }, + { "MTP_OPERATION_SKIP", 0x9820 }, + { 0, 0 }, +}; + +static const CodeEntry sFormatCodes[] = { + { "MTP_FORMAT_UNDEFINED", 0x3000 }, + { "MTP_FORMAT_ASSOCIATION", 0x3001 }, + { "MTP_FORMAT_SCRIPT", 0x3002 }, + { "MTP_FORMAT_EXECUTABLE", 0x3003 }, + { "MTP_FORMAT_TEXT", 0x3004 }, + { "MTP_FORMAT_HTML", 0x3005 }, + { "MTP_FORMAT_DPOF", 0x3006 }, + { "MTP_FORMAT_AIFF", 0x3007 }, + { "MTP_FORMAT_WAV", 0x3008 }, + { "MTP_FORMAT_MP3", 0x3009 }, + { "MTP_FORMAT_AVI", 0x300A }, + { "MTP_FORMAT_MPEG", 0x300B }, + { "MTP_FORMAT_ASF", 0x300C }, + { "MTP_FORMAT_DEFINED", 0x3800 }, + { "MTP_FORMAT_EXIF_JPEG", 0x3801 }, + { "MTP_FORMAT_TIFF_EP", 0x3802 }, + { "MTP_FORMAT_FLASHPIX", 0x3803 }, + { "MTP_FORMAT_BMP", 0x3804 }, + { "MTP_FORMAT_CIFF", 0x3805 }, + { "MTP_FORMAT_GIF", 0x3807 }, + { "MTP_FORMAT_JFIF", 0x3808 }, + { "MTP_FORMAT_CD", 0x3809 }, + { "MTP_FORMAT_PICT", 0x380A }, + { "MTP_FORMAT_PNG", 0x380B }, + { "MTP_FORMAT_TIFF", 0x380D }, + { "MTP_FORMAT_TIFF_IT", 0x380E }, + { "MTP_FORMAT_JP2", 0x380F }, + { "MTP_FORMAT_JPX", 0x3810 }, + { "MTP_FORMAT_UNDEFINED_FIRMWARE", 0xB802 }, + { "MTP_FORMAT_WINDOWS_IMAGE_FORMAT", 0xB881 }, + { "MTP_FORMAT_UNDEFINED_AUDIO", 0xB900 }, + { "MTP_FORMAT_WMA", 0xB901 }, + { "MTP_FORMAT_OGG", 0xB902 }, + { "MTP_FORMAT_AAC", 0xB903 }, + { "MTP_FORMAT_AUDIBLE", 0xB904 }, + { "MTP_FORMAT_FLAC", 0xB906 }, + { "MTP_FORMAT_UNDEFINED_VIDEO", 0xB980 }, + { "MTP_FORMAT_WMV", 0xB981 }, + { "MTP_FORMAT_MP4_CONTAINER", 0xB982 }, + { "MTP_FORMAT_MP2", 0xB983 }, + { "MTP_FORMAT_3GP_CONTAINER", 0xB984 }, + { "MTP_FORMAT_UNDEFINED_COLLECTION", 0xBA00 }, + { "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM", 0xBA01 }, + { "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM", 0xBA02 }, + { "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM", 0xBA03 }, + { "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM", 0xBA04 }, + { "MTP_FORMAT_ABSTRACT_AV_PLAYLIST", 0xBA05 }, + { "MTP_FORMAT_ABSTRACT_CONTACT_GROUP", 0xBA06 }, + { "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER", 0xBA07 }, + { "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION", 0xBA08 }, + { "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST", 0xBA09 }, + { "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST", 0xBA0A }, + { "MTP_FORMAT_ABSTRACT_MEDIACAST", 0xBA0B }, + { "MTP_FORMAT_WPL_PLAYLIST", 0xBA10 }, + { "MTP_FORMAT_M3U_PLAYLIST", 0xBA11 }, + { "MTP_FORMAT_MPL_PLAYLIST", 0xBA12 }, + { "MTP_FORMAT_ASX_PLAYLIST", 0xBA13 }, + { "MTP_FORMAT_PLS_PLAYLIST", 0xBA14 }, + { "MTP_FORMAT_UNDEFINED_DOCUMENT", 0xBA80 }, + { "MTP_FORMAT_ABSTRACT_DOCUMENT", 0xBA81 }, + { "MTP_FORMAT_XML_DOCUMENT", 0xBA82 }, + { "MTP_FORMAT_MS_WORD_DOCUMENT", 0xBA83 }, + { "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT", 0xBA84 }, + { "MTP_FORMAT_MS_EXCEL_SPREADSHEET", 0xBA85 }, + { "MTP_FORMAT_MS_POWERPOINT_PRESENTATION", 0xBA86 }, + { "MTP_FORMAT_UNDEFINED_MESSAGE", 0xBB00 }, + { "MTP_FORMAT_ABSTRACT_MESSSAGE", 0xBB01 }, + { "MTP_FORMAT_UNDEFINED_CONTACT", 0xBB80 }, + { "MTP_FORMAT_ABSTRACT_CONTACT", 0xBB81 }, + { "MTP_FORMAT_VCARD_2", 0xBB82 }, + { 0, 0 }, +}; + +static const CodeEntry sObjectPropCodes[] = { + { "MTP_PROPERTY_STORAGE_ID", 0xDC01 }, + { "MTP_PROPERTY_OBJECT_FORMAT", 0xDC02 }, + { "MTP_PROPERTY_PROTECTION_STATUS", 0xDC03 }, + { "MTP_PROPERTY_OBJECT_SIZE", 0xDC04 }, + { "MTP_PROPERTY_ASSOCIATION_TYPE", 0xDC05 }, + { "MTP_PROPERTY_ASSOCIATION_DESC", 0xDC06 }, + { "MTP_PROPERTY_OBJECT_FILE_NAME", 0xDC07 }, + { "MTP_PROPERTY_DATE_CREATED", 0xDC08 }, + { "MTP_PROPERTY_DATE_MODIFIED", 0xDC09 }, + { "MTP_PROPERTY_KEYWORDS", 0xDC0A }, + { "MTP_PROPERTY_PARENT_OBJECT", 0xDC0B }, + { "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS", 0xDC0C }, + { "MTP_PROPERTY_HIDDEN", 0xDC0D }, + { "MTP_PROPERTY_SYSTEM_OBJECT", 0xDC0E }, + { "MTP_PROPERTY_PERSISTENT_UID", 0xDC41 }, + { "MTP_PROPERTY_SYNC_ID", 0xDC42 }, + { "MTP_PROPERTY_PROPERTY_BAG", 0xDC43 }, + { "MTP_PROPERTY_NAME", 0xDC44 }, + { "MTP_PROPERTY_CREATED_BY", 0xDC45 }, + { "MTP_PROPERTY_ARTIST", 0xDC46 }, + { "MTP_PROPERTY_DATE_AUTHORED", 0xDC47 }, + { "MTP_PROPERTY_DESCRIPTION", 0xDC48 }, + { "MTP_PROPERTY_URL_REFERENCE", 0xDC49 }, + { "MTP_PROPERTY_LANGUAGE_LOCALE", 0xDC4A }, + { "MTP_PROPERTY_COPYRIGHT_INFORMATION", 0xDC4B }, + { "MTP_PROPERTY_SOURCE", 0xDC4C }, + { "MTP_PROPERTY_ORIGIN_LOCATION", 0xDC4D }, + { "MTP_PROPERTY_DATE_ADDED", 0xDC4E }, + { "MTP_PROPERTY_NON_CONSUMABLE", 0xDC4F }, + { "MTP_PROPERTY_CORRUPT_UNPLAYABLE", 0xDC50 }, + { "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER", 0xDC51 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT", 0xDC81 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE", 0xDC82 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT", 0xDC83 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH", 0xDC84 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION", 0xDC85 }, + { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA", 0xDC86 }, + { "MTP_PROPERTY_WIDTH", 0xDC87 }, + { "MTP_PROPERTY_HEIGHT", 0xDC88 }, + { "MTP_PROPERTY_DURATION", 0xDC89 }, + { "MTP_PROPERTY_RATING", 0xDC8A }, + { "MTP_PROPERTY_TRACK", 0xDC8B }, + { "MTP_PROPERTY_GENRE", 0xDC8C }, + { "MTP_PROPERTY_CREDITS", 0xDC8D }, + { "MTP_PROPERTY_LYRICS", 0xDC8E }, + { "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID", 0xDC8F }, + { "MTP_PROPERTY_PRODUCED_BY", 0xDC90 }, + { "MTP_PROPERTY_USE_COUNT", 0xDC91 }, + { "MTP_PROPERTY_SKIP_COUNT", 0xDC92 }, + { "MTP_PROPERTY_LAST_ACCESSED", 0xDC93 }, + { "MTP_PROPERTY_PARENTAL_RATING", 0xDC94 }, + { "MTP_PROPERTY_META_GENRE", 0xDC95 }, + { "MTP_PROPERTY_COMPOSER", 0xDC96 }, + { "MTP_PROPERTY_EFFECTIVE_RATING", 0xDC97 }, + { "MTP_PROPERTY_SUBTITLE", 0xDC98 }, + { "MTP_PROPERTY_ORIGINAL_RELEASE_DATE", 0xDC99 }, + { "MTP_PROPERTY_ALBUM_NAME", 0xDC9A }, + { "MTP_PROPERTY_ALBUM_ARTIST", 0xDC9B }, + { "MTP_PROPERTY_MOOD", 0xDC9C }, + { "MTP_PROPERTY_DRM_STATUS", 0xDC9D }, + { "MTP_PROPERTY_SUB_DESCRIPTION", 0xDC9E }, + { "MTP_PROPERTY_IS_CROPPED", 0xDCD1 }, + { "MTP_PROPERTY_IS_COLOUR_CORRECTED", 0xDCD2 }, + { "MTP_PROPERTY_IMAGE_BIT_DEPTH", 0xDCD3 }, + { "MTP_PROPERTY_F_NUMBER", 0xDCD4 }, + { "MTP_PROPERTY_EXPOSURE_TIME", 0xDCD5 }, + { "MTP_PROPERTY_EXPOSURE_INDEX", 0xDCD6 }, + { "MTP_PROPERTY_TOTAL_BITRATE", 0xDE91 }, + { "MTP_PROPERTY_BITRATE_TYPE", 0xDE92 }, + { "MTP_PROPERTY_SAMPLE_RATE", 0xDE93 }, + { "MTP_PROPERTY_NUMBER_OF_CHANNELS", 0xDE94 }, + { "MTP_PROPERTY_AUDIO_BIT_DEPTH", 0xDE95 }, + { "MTP_PROPERTY_SCAN_TYPE", 0xDE97 }, + { "MTP_PROPERTY_AUDIO_WAVE_CODEC", 0xDE99 }, + { "MTP_PROPERTY_AUDIO_BITRATE", 0xDE9A }, + { "MTP_PROPERTY_VIDEO_FOURCC_CODEC", 0xDE9B }, + { "MTP_PROPERTY_VIDEO_BITRATE", 0xDE9C }, + { "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS", 0xDE9D }, + { "MTP_PROPERTY_KEYFRAME_DISTANCE", 0xDE9E }, + { "MTP_PROPERTY_BUFFER_SIZE", 0xDE9F }, + { "MTP_PROPERTY_ENCODING_QUALITY", 0xDEA0 }, + { "MTP_PROPERTY_ENCODING_PROFILE", 0xDEA1 }, + { "MTP_PROPERTY_DISPLAY_NAME", 0xDCE0 }, + { "MTP_PROPERTY_BODY_TEXT", 0xDCE1 }, + { "MTP_PROPERTY_SUBJECT", 0xDCE2 }, + { "MTP_PROPERTY_PRIORITY", 0xDCE3 }, + { "MTP_PROPERTY_GIVEN_NAME", 0xDD00 }, + { "MTP_PROPERTY_MIDDLE_NAMES", 0xDD01 }, + { "MTP_PROPERTY_FAMILY_NAME", 0xDD02 }, + { "MTP_PROPERTY_PREFIX", 0xDD03 }, + { "MTP_PROPERTY_SUFFIX", 0xDD04 }, + { "MTP_PROPERTY_PHONETIC_GIVEN_NAME", 0xDD05 }, + { "MTP_PROPERTY_PHONETIC_FAMILY_NAME", 0xDD06 }, + { "MTP_PROPERTY_EMAIL_PRIMARY", 0xDD07 }, + { "MTP_PROPERTY_EMAIL_PERSONAL_1", 0xDD08 }, + { "MTP_PROPERTY_EMAIL_PERSONAL_2", 0xDD09 }, + { "MTP_PROPERTY_EMAIL_BUSINESS_1", 0xDD0A }, + { "MTP_PROPERTY_EMAIL_BUSINESS_2", 0xDD0B }, + { "MTP_PROPERTY_EMAIL_OTHERS", 0xDD0C }, + { "MTP_PROPERTY_PHONE_NUMBER_PRIMARY", 0xDD0D }, + { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL", 0xDD0E }, + { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2", 0xDD0F }, + { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS", 0xDD10 }, + { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2", 0xDD11 }, + { "MTP_PROPERTY_PHONE_NUMBER_MOBILE", 0xDD12 }, + { "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2", 0xDD13 }, + { "MTP_PROPERTY_FAX_NUMBER_PRIMARY", 0xDD14 }, + { "MTP_PROPERTY_FAX_NUMBER_PERSONAL", 0xDD15 }, + { "MTP_PROPERTY_FAX_NUMBER_BUSINESS", 0xDD16 }, + { "MTP_PROPERTY_PAGER_NUMBER", 0xDD17 }, + { "MTP_PROPERTY_PHONE_NUMBER_OTHERS", 0xDD18 }, + { "MTP_PROPERTY_PRIMARY_WEB_ADDRESS", 0xDD19 }, + { "MTP_PROPERTY_PERSONAL_WEB_ADDRESS", 0xDD1A }, + { "MTP_PROPERTY_BUSINESS_WEB_ADDRESS", 0xDD1B }, + { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS", 0xDD1C }, + { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2", 0xDD1D }, + { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3", 0xDD1E }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL", 0xDD1F }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1", 0xDD20 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2", 0xDD21 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY", 0xDD22 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION", 0xDD23 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE", 0xDD24 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY", 0xDD25 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL", 0xDD26 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1", 0xDD27 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2", 0xDD28 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY", 0xDD29 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION", 0xDD2A }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE", 0xDD2B }, + { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY", 0xDD2C }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL", 0xDD2D }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1", 0xDD2E }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2", 0xDD2F }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY", 0xDD30 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION", 0xDD31 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE", 0xDD32 }, + { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY", 0xDD33 }, + { "MTP_PROPERTY_ORGANIZATION_NAME", 0xDD34 }, + { "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME", 0xDD35 }, + { "MTP_PROPERTY_ROLE", 0xDD36 }, + { "MTP_PROPERTY_BIRTHDATE", 0xDD37 }, + { "MTP_PROPERTY_MESSAGE_TO", 0xDD40 }, + { "MTP_PROPERTY_MESSAGE_CC", 0xDD41 }, + { "MTP_PROPERTY_MESSAGE_BCC", 0xDD42 }, + { "MTP_PROPERTY_MESSAGE_READ", 0xDD43 }, + { "MTP_PROPERTY_MESSAGE_RECEIVED_TIME", 0xDD44 }, + { "MTP_PROPERTY_MESSAGE_SENDER", 0xDD45 }, + { "MTP_PROPERTY_ACTIVITY_BEGIN_TIME", 0xDD50 }, + { "MTP_PROPERTY_ACTIVITY_END_TIME", 0xDD51 }, + { "MTP_PROPERTY_ACTIVITY_LOCATION", 0xDD52 }, + { "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES", 0xDD54 }, + { "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES", 0xDD55 }, + { "MTP_PROPERTY_ACTIVITY_RESOURCES", 0xDD56 }, + { "MTP_PROPERTY_ACTIVITY_ACCEPTED", 0xDD57 }, + { "MTP_PROPERTY_ACTIVITY_TENTATIVE", 0xDD58 }, + { "MTP_PROPERTY_ACTIVITY_DECLINED", 0xDD59 }, + { "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME", 0xDD5A }, + { "MTP_PROPERTY_ACTIVITY_OWNER", 0xDD5B }, + { "MTP_PROPERTY_ACTIVITY_STATUS", 0xDD5C }, + { "MTP_PROPERTY_OWNER", 0xDD5D }, + { "MTP_PROPERTY_EDITOR", 0xDD5E }, + { "MTP_PROPERTY_WEBMASTER", 0xDD5F }, + { "MTP_PROPERTY_URL_SOURCE", 0xDD60 }, + { "MTP_PROPERTY_URL_DESTINATION", 0xDD61 }, + { "MTP_PROPERTY_TIME_BOOKMARK", 0xDD62 }, + { "MTP_PROPERTY_OBJECT_BOOKMARK", 0xDD63 }, + { "MTP_PROPERTY_BYTE_BOOKMARK", 0xDD64 }, + { "MTP_PROPERTY_LAST_BUILD_DATE", 0xDD70 }, + { "MTP_PROPERTY_TIME_TO_LIVE", 0xDD71 }, + { "MTP_PROPERTY_MEDIA_GUID", 0xDD72 }, + { 0, 0 }, +}; + +static const CodeEntry sDevicePropCodes[] = { + { "MTP_DEVICE_PROPERTY_UNDEFINED", 0x5000 }, + { "MTP_DEVICE_PROPERTY_BATTERY_LEVEL", 0x5001 }, + { "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE", 0x5002 }, + { "MTP_DEVICE_PROPERTY_IMAGE_SIZE", 0x5003 }, + { "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING", 0x5004 }, + { "MTP_DEVICE_PROPERTY_WHITE_BALANCE", 0x5005 }, + { "MTP_DEVICE_PROPERTY_RGB_GAIN", 0x5006 }, + { "MTP_DEVICE_PROPERTY_F_NUMBER", 0x5007 }, + { "MTP_DEVICE_PROPERTY_FOCAL_LENGTH", 0x5008 }, + { "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE", 0x5009 }, + { "MTP_DEVICE_PROPERTY_FOCUS_MODE", 0x500A }, + { "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE", 0x500B }, + { "MTP_DEVICE_PROPERTY_FLASH_MODE", 0x500C }, + { "MTP_DEVICE_PROPERTY_EXPOSURE_TIME", 0x500D }, + { "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE", 0x500E }, + { "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX", 0x500F }, + { "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION", 0x5010 }, + { "MTP_DEVICE_PROPERTY_DATETIME", 0x5011 }, + { "MTP_DEVICE_PROPERTY_CAPTURE_DELAY", 0x5012 }, + { "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE", 0x5013 }, + { "MTP_DEVICE_PROPERTY_CONTRAST", 0x5014 }, + { "MTP_DEVICE_PROPERTY_SHARPNESS", 0x5015 }, + { "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM", 0x5016 }, + { "MTP_DEVICE_PROPERTY_EFFECT_MODE", 0x5017 }, + { "MTP_DEVICE_PROPERTY_BURST_NUMBER", 0x5018 }, + { "MTP_DEVICE_PROPERTY_BURST_INTERVAL", 0x5019 }, + { "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER", 0x501A }, + { "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL", 0x501B }, + { "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE", 0x501C }, + { "MTP_DEVICE_PROPERTY_UPLOAD_URL", 0x501D }, + { "MTP_DEVICE_PROPERTY_ARTIST", 0x501E }, + { "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO", 0x501F }, + { "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER", 0xD401 }, + { "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME", 0xD402 }, + { "MTP_DEVICE_PROPERTY_VOLUME", 0xD403 }, + { "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED", 0xD404 }, + { "MTP_DEVICE_PROPERTY_DEVICE_ICON", 0xD405 }, + { "MTP_DEVICE_PROPERTY_PLAYBACK_RATE", 0xD410 }, + { "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT", 0xD411 }, + { "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX", 0xD412 }, + { "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO", 0xD406 }, + { "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE", 0xD407 }, + { 0, 0 }, +}; + +static const char* getCodeName(uint16_t code, const CodeEntry* table) { + const CodeEntry* entry = table; + while (entry->name) { + if (entry->code == code) + return entry->name; + entry++; + } + return "UNKNOWN"; +} + +const char* MtpDebug::getOperationCodeName(MtpOperationCode code) { + return getCodeName(code, sOperationCodes); +} + +const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) { + if (code == 0) + return "NONE"; + return getCodeName(code, sFormatCodes); +} + +const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; + return getCodeName(code, sObjectPropCodes); +} + +const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; + return getCodeName(code, sDevicePropCodes); +} + +} // namespace android diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h new file mode 100644 index 0000000..5b53e31 --- /dev/null +++ b/media/mtp/MtpDebug.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef _MTP_DEBUG_H +#define _MTP_DEBUG_H + +// #define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "MtpTypes.h" + +namespace android { + +class MtpDebug { +public: + static const char* getOperationCodeName(MtpOperationCode code); + static const char* getFormatCodeName(MtpObjectFormat code); + static const char* getObjectPropCodeName(MtpPropertyCode code); + static const char* getDevicePropCodeName(MtpPropertyCode code); +}; + +}; // namespace android + +#endif // _MTP_DEBUG_H diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp new file mode 100644 index 0000000..416ebfe --- /dev/null +++ b/media/mtp/MtpDevice.cpp @@ -0,0 +1,495 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpDevice" + +#include "MtpDebug.h" +#include "MtpDevice.h" +#include "MtpDeviceInfo.h" +#include "MtpObjectInfo.h" +#include "MtpProperty.h" +#include "MtpStorageInfo.h" +#include "MtpStringBuffer.h" +#include "MtpUtils.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> + +namespace android { + +MtpDevice::MtpDevice(struct usb_device* device, int interface, + struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, + struct usb_endpoint *ep_intr) + : mDevice(device), + mInterface(interface), + mEndpointIn(ep_in), + mEndpointOut(ep_out), + mEndpointIntr(ep_intr), + mDeviceInfo(NULL), + mID(usb_device_get_unique_id(device)), + mSessionID(0), + mTransactionID(0) +{ +} + +MtpDevice::~MtpDevice() { + close(); + for (int i = 0; i < mDeviceProperties.size(); i++) + delete mDeviceProperties[i]; +} + +void MtpDevice::initialize() { + openSession(); + mDeviceInfo = getDeviceInfo(); + if (mDeviceInfo) { + mDeviceInfo->print(); + + 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) { + property->print(); + mDeviceProperties.push(property); + } + } + } + } +} + +void MtpDevice::close() { + if (mDevice) { + usb_device_release_interface(mDevice, mInterface); + usb_device_close(mDevice); + mDevice = NULL; + } +} + +const char* MtpDevice::getDeviceName() { + if (mDevice) + return usb_device_get_name(mDevice); + else + return "???"; +} + +bool MtpDevice::openSession() { + 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() { + 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() { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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(mEndpointOut, buffer, count); + // 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)) { + MtpResponseCode ret = readResponse(); + if (ret == MTP_RESPONSE_OK) + return true; + } + return false; +} + +MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) { + MtpObjectInfo* info = getObjectInfo(handle); + if (info) + return info->mParent; + else + return -1; +} + +MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) { + MtpObjectInfo* info = getObjectInfo(handle); + if (info) + return info->mStorageID; + else + return -1; +} + +MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) { + 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; +} + +// 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) { + LOGD("readObject: %s", destPath); + int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC); + if (fd < 0) { + LOGE("open failed for %s", destPath); + return false; + } + + fchown(fd, getuid(), group); + // set permissions + int mask = umask(0); + fchmod(fd, perm); + umask(mask); + + Mutex::Autolock autoLock(mMutex); + bool result = false; + + mRequest.reset(); + mRequest.setParameter(1, handle); + if (sendRequest(MTP_OPERATION_GET_OBJECT) + && mData.readDataHeader(mEndpointIn)) { + 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) + goto fail; + remaining -= initialDataLength; + } + free(initialData); + } + + // USB reads greater than 16K don't work + char buffer1[16384], buffer2[16384]; + char* readBuffer = buffer1; + char* writeBuffer = NULL; + int writeLength = 0; + + while (remaining > 0 || writeBuffer) { + if (remaining > 0) { + // queue up a read request + int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); + if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) { + LOGE("readDataAsync failed"); + goto fail; + } + } else { + readBuffer = NULL; + } + + if (writeBuffer) { + // write previous buffer + if (write(fd, writeBuffer, writeLength) != writeLength) { + LOGE("write failed"); + // wait for pending read before failing + if (readBuffer) + mData.readDataWait(mEndpointIn); + goto fail; + } + writeBuffer = NULL; + } + + // wait for read to complete + if (readBuffer) { + int read = mData.readDataWait(mEndpointIn); + if (read < 0) + goto fail; + + writeBuffer = readBuffer; + writeLength = read; + remaining -= read; + readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1); + } + } + + MtpResponseCode response = readResponse(); + if (response == MTP_RESPONSE_OK) + result = true; + } + +fail: + ::close(fd); + return result; +} + +bool MtpDevice::sendRequest(MtpOperationCode operation) { + LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); + mRequest.setOperationCode(operation); + if (mTransactionID > 0) + mRequest.setTransactionID(mTransactionID++); + int ret = mRequest.write(mEndpointOut); + mRequest.dump(); + return (ret > 0); +} + +bool MtpDevice::sendData() { + LOGV("sendData\n"); + mData.setOperationCode(mRequest.getOperationCode()); + mData.setTransactionID(mRequest.getTransactionID()); + int ret = mData.write(mEndpointOut); + mData.dump(); + return (ret > 0); +} + +bool MtpDevice::readData() { + mData.reset(); + int ret = mData.read(mEndpointIn); + LOGV("readData returned %d\n", ret); + if (ret >= MTP_CONTAINER_HEADER_SIZE) { + mData.dump(); + return true; + } + else { + LOGV("readResponse failed\n"); + return false; + } +} + +bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) { + mData.setOperationCode(operation); + mData.setTransactionID(mRequest.getTransactionID()); + return (!mData.writeDataHeader(mEndpointOut, dataLength)); +} + +MtpResponseCode MtpDevice::readResponse() { + LOGV("readResponse\n"); + int ret = mResponse.read(mEndpointIn); + if (ret >= MTP_CONTAINER_HEADER_SIZE) { + mResponse.dump(); + return mResponse.getResponseCode(); + } + else { + LOGD("readResponse failed\n"); + return -1; + } +} + +} // namespace android diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h new file mode 100644 index 0000000..21c85d5 --- /dev/null +++ b/media/mtp/MtpDevice.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef _MTP_DEVICE_H +#define _MTP_DEVICE_H + +#include "MtpRequestPacket.h" +#include "MtpDataPacket.h" +#include "MtpResponsePacket.h" +#include "MtpTypes.h" + +#include <utils/threads.h> + +struct usb_device; + +namespace android { + +class MtpDeviceInfo; +class MtpObjectInfo; +class MtpStorageInfo; + +class MtpDevice { +private: + struct usb_device* mDevice; + int mInterface; + struct usb_endpoint* mEndpointIn; + struct usb_endpoint* mEndpointOut; + struct usb_endpoint* mEndpointIntr; + MtpDeviceInfo* mDeviceInfo; + MtpPropertyList mDeviceProperties; + + // a unique ID for the device + int mID; + + // current session ID + MtpSessionID mSessionID; + // current transaction ID + MtpTransactionID mTransactionID; + + MtpRequestPacket mRequest; + 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, + struct usb_endpoint *ep_intr); + virtual ~MtpDevice(); + + inline int getID() const { return mID; } + + void initialize(); + void close(); + const char* getDeviceName(); + + bool openSession(); + bool closeSession(); + + MtpDeviceInfo* getDeviceInfo(); + MtpStorageIDList* getStorageIDs(); + MtpStorageInfo* getStorageInfo(MtpStorageID storageID); + 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); + + bool readObject(MtpObjectHandle handle, const char* destPath, int group, + int perm); + +private: + bool sendRequest(MtpOperationCode operation); + bool sendData(); + bool readData(); + bool writeDataHeader(MtpOperationCode operation, int dataLength); + MtpResponseCode readResponse(); + +}; + +}; // namespace android + +#endif // _MTP_DEVICE_H diff --git a/media/mtp/MtpDeviceInfo.cpp b/media/mtp/MtpDeviceInfo.cpp new file mode 100644 index 0000000..5a9322e --- /dev/null +++ b/media/mtp/MtpDeviceInfo.cpp @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpDeviceInfo" + +#include "MtpDebug.h" +#include "MtpDataPacket.h" +#include "MtpDeviceInfo.h" +#include "MtpStringBuffer.h" + +namespace android { + +MtpDeviceInfo::MtpDeviceInfo() + : mStandardVersion(0), + mVendorExtensionID(0), + mVendorExtensionVersion(0), + mVendorExtensionDesc(NULL), + mFunctionalCode(0), + mOperations(NULL), + mEvents(NULL), + mDeviceProperties(NULL), + mCaptureFormats(NULL), + mPlaybackFormats(NULL), + mManufacturer(NULL), + mModel(NULL), + mVersion(NULL), + mSerial(NULL) +{ +} + +MtpDeviceInfo::~MtpDeviceInfo() { + if (mVendorExtensionDesc) + free(mVendorExtensionDesc); + delete mOperations; + delete mEvents; + delete mDeviceProperties; + delete mCaptureFormats; + delete mPlaybackFormats; + if (mManufacturer) + free(mManufacturer); + if (mModel) + free(mModel); + if (mVersion) + free(mVersion); + if (mSerial) + free(mSerial); +} + +void MtpDeviceInfo::read(MtpDataPacket& packet) { + MtpStringBuffer string; + + // read the device info + mStandardVersion = packet.getUInt16(); + mVendorExtensionID = packet.getUInt32(); + mVendorExtensionVersion = packet.getUInt16(); + + packet.getString(string); + mVendorExtensionDesc = strdup((const char *)string); + + mFunctionalCode = packet.getUInt16(); + mOperations = packet.getAUInt16(); + mEvents = packet.getAUInt16(); + mDeviceProperties = packet.getAUInt16(); + mCaptureFormats = packet.getAUInt16(); + mPlaybackFormats = packet.getAUInt16(); + + packet.getString(string); + mManufacturer = strdup((const char *)string); + packet.getString(string); + mModel = strdup((const char *)string); + packet.getString(string); + mVersion = strdup((const char *)string); + packet.getString(string); + mSerial = strdup((const char *)string); +} + +void MtpDeviceInfo::print() { + LOGV("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n", + mStandardVersion, mVendorExtensionID, mVendorExtensionVersion); + LOGV("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n", + mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial); +} + +} // namespace android diff --git a/media/mtp/MtpDeviceInfo.h b/media/mtp/MtpDeviceInfo.h new file mode 100644 index 0000000..2abaa10 --- /dev/null +++ b/media/mtp/MtpDeviceInfo.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef _MTP_DEVICE_INFO_H +#define _MTP_DEVICE_INFO_H + +struct stat; + +namespace android { + +class MtpDataPacket; + +class MtpDeviceInfo { +public: + uint16_t mStandardVersion; + uint32_t mVendorExtensionID; + uint16_t mVendorExtensionVersion; + char* mVendorExtensionDesc; + uint16_t mFunctionalCode; + UInt16List* mOperations; + UInt16List* mEvents; + MtpDevicePropertyList* mDeviceProperties; + MtpObjectFormatList* mCaptureFormats; + MtpObjectFormatList* mPlaybackFormats; + char* mManufacturer; + char* mModel; + char* mVersion; + char* mSerial; + +public: + MtpDeviceInfo(); + virtual ~MtpDeviceInfo(); + + void read(MtpDataPacket& packet); + + void print(); +}; + +}; // namespace android + +#endif // _MTP_DEVICE_INFO_H diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp new file mode 100644 index 0000000..fc74542 --- /dev/null +++ b/media/mtp/MtpEventPacket.cpp @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpEventPacket" + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#ifdef MTP_DEVICE +#include <linux/usb/f_mtp.h> +#endif + +#include "MtpEventPacket.h" + +namespace android { + +MtpEventPacket::MtpEventPacket() + : MtpPacket(512) +{ +} + +MtpEventPacket::~MtpEventPacket() { +} + +#ifdef MTP_DEVICE +int MtpEventPacket::write(int fd) { + struct mtp_event event; + + putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); + putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT); + + event.data = mBuffer; + event.length = mPacketSize; + int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event); + return (ret < 0 ? ret : 0); +} +#endif + +#ifdef MTP_HOST + // read our buffer from the given endpoint +int MtpEventPacket::read(struct usb_endpoint *ep) { + int ret = transfer(ep, mBuffer, mBufferSize); + if (ret >= 0) + mPacketSize = ret; + else + mPacketSize = 0; + return ret; +} +#endif + +} // namespace android + diff --git a/media/mtp/MtpEventPacket.h b/media/mtp/MtpEventPacket.h new file mode 100644 index 0000000..30ae869 --- /dev/null +++ b/media/mtp/MtpEventPacket.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef _MTP_EVENT_PACKET_H +#define _MTP_EVENT_PACKET_H + +#include "MtpPacket.h" +#include "mtp.h" + +namespace android { + +class MtpEventPacket : public MtpPacket { + +public: + MtpEventPacket(); + virtual ~MtpEventPacket(); + +#ifdef MTP_DEVICE + // write our data to the given file descriptor + int write(int fd); +#endif + +#ifdef MTP_HOST + // read our buffer from the given endpoint + int read(struct usb_endpoint *ep); +#endif + + inline MtpEventCode getEventCode() const { return getContainerCode(); } + inline void setEventCode(MtpEventCode code) + { return setContainerCode(code); } +}; + +}; // namespace android + +#endif // _MTP_EVENT_PACKET_H diff --git a/media/mtp/MtpObjectInfo.cpp b/media/mtp/MtpObjectInfo.cpp new file mode 100644 index 0000000..ea68c3b --- /dev/null +++ b/media/mtp/MtpObjectInfo.cpp @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpObjectInfo" + +#include "MtpDebug.h" +#include "MtpDataPacket.h" +#include "MtpObjectInfo.h" +#include "MtpStringBuffer.h" +#include "MtpUtils.h" + +namespace android { + +MtpObjectInfo::MtpObjectInfo(MtpObjectHandle handle) + : mHandle(handle), + mStorageID(0), + mFormat(0), + mProtectionStatus(0), + mCompressedSize(0), + mThumbFormat(0), + mThumbCompressedSize(0), + mThumbPixWidth(0), + mThumbPixHeight(0), + mImagePixWidth(0), + mImagePixHeight(0), + mImagePixDepth(0), + mParent(0), + mAssociationType(0), + mAssociationDesc(0), + mSequenceNumber(0), + mName(NULL), + mDateCreated(0), + mDateModified(0), + mKeywords(NULL) +{ +} + +MtpObjectInfo::~MtpObjectInfo() { + if (mName) + free(mName); + if (mKeywords) + free(mKeywords); +} + +void MtpObjectInfo::read(MtpDataPacket& packet) { + MtpStringBuffer string; + time_t time; + + mStorageID = packet.getUInt32(); + mFormat = packet.getUInt16(); + mProtectionStatus = packet.getUInt16(); + mCompressedSize = packet.getUInt32(); + mThumbFormat = packet.getUInt16(); + mThumbCompressedSize = packet.getUInt32(); + mThumbPixWidth = packet.getUInt32(); + mThumbPixHeight = packet.getUInt32(); + mImagePixWidth = packet.getUInt32(); + mImagePixHeight = packet.getUInt32(); + mImagePixDepth = packet.getUInt32(); + mParent = packet.getUInt32(); + mAssociationType = packet.getUInt16(); + mAssociationDesc = packet.getUInt32(); + mSequenceNumber = packet.getUInt32(); + + packet.getString(string); + mName = strdup((const char *)string); + + packet.getString(string); + if (parseDateTime((const char*)string, time)) + mDateCreated = time; + + packet.getString(string); + if (parseDateTime((const char*)string, time)) + mDateModified = time; + + packet.getString(string); + mKeywords = strdup((const char *)string); +} + +void MtpObjectInfo::print() { + LOGD("MtpObject Info %08X: %s\n", mHandle, mName); + LOGD(" mStorageID: %08X mFormat: %04X mProtectionStatus: %d\n", + mStorageID, mFormat, mProtectionStatus); + LOGD(" mCompressedSize: %d mThumbFormat: %04X mThumbCompressedSize: %d\n", + mCompressedSize, mFormat, mThumbCompressedSize); + LOGD(" mThumbPixWidth: %d mThumbPixHeight: %d\n", mThumbPixWidth, mThumbPixHeight); + LOGD(" mImagePixWidth: %d mImagePixHeight: %d mImagePixDepth: %d\n", + mImagePixWidth, mImagePixHeight, mImagePixDepth); + LOGD(" mParent: %08X mAssociationType: %04X mAssociationDesc: %04X\n", + mParent, mAssociationType, mAssociationDesc); + LOGD(" mSequenceNumber: %d mDateCreated: %ld mDateModified: %ld mKeywords: %s\n", + mSequenceNumber, mDateCreated, mDateModified, mKeywords); +} + +} // namespace android diff --git a/media/mtp/MtpObjectInfo.h b/media/mtp/MtpObjectInfo.h new file mode 100644 index 0000000..c7a449c --- /dev/null +++ b/media/mtp/MtpObjectInfo.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef _MTP_OBJECT_INFO_H +#define _MTP_OBJECT_INFO_H + +#include "MtpTypes.h" + +namespace android { + +class MtpDataPacket; + +class MtpObjectInfo { +public: + MtpObjectHandle mHandle; + MtpStorageID mStorageID; + MtpObjectFormat mFormat; + uint16_t mProtectionStatus; + uint32_t mCompressedSize; + MtpObjectFormat mThumbFormat; + uint32_t mThumbCompressedSize; + uint32_t mThumbPixWidth; + uint32_t mThumbPixHeight; + uint32_t mImagePixWidth; + uint32_t mImagePixHeight; + uint32_t mImagePixDepth; + MtpObjectHandle mParent; + uint16_t mAssociationType; + uint32_t mAssociationDesc; + uint32_t mSequenceNumber; + char* mName; + time_t mDateCreated; + time_t mDateModified; + char* mKeywords; + +public: + MtpObjectInfo(MtpObjectHandle handle); + virtual ~MtpObjectInfo(); + + void read(MtpDataPacket& packet); + + void print(); +}; + +}; // namespace android + +#endif // _MTP_OBJECT_INFO_H diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp new file mode 100644 index 0000000..42bf8ba --- /dev/null +++ b/media/mtp/MtpPacket.cpp @@ -0,0 +1,154 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpPacket" + +#include "MtpDebug.h" +#include "MtpPacket.h" +#include "mtp.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> + +#include <usbhost/usbhost.h> + +namespace android { + +MtpPacket::MtpPacket(int bufferSize) + : mBuffer(NULL), + mBufferSize(bufferSize), + mAllocationIncrement(bufferSize), + mPacketSize(0) +{ + mBuffer = (uint8_t *)malloc(bufferSize); + if (!mBuffer) { + LOGE("out of memory!"); + abort(); + } +} + +MtpPacket::~MtpPacket() { + if (mBuffer) + free(mBuffer); +} + +void MtpPacket::reset() { + allocate(MTP_CONTAINER_HEADER_SIZE); + mPacketSize = MTP_CONTAINER_HEADER_SIZE; + memset(mBuffer, 0, mBufferSize); +} + +void MtpPacket::allocate(int length) { + if (length > mBufferSize) { + int newLength = length + mAllocationIncrement; + mBuffer = (uint8_t *)realloc(mBuffer, newLength); + if (!mBuffer) { + LOGE("out of memory!"); + abort(); + } + mBufferSize = newLength; + } +} + +void MtpPacket::dump() { +#define DUMP_BYTES_PER_ROW 16 + char buffer[500]; + char* bufptr = buffer; + + for (int i = 0; i < mPacketSize; i++) { + sprintf(bufptr, "%02X ", mBuffer[i]); + bufptr += strlen(bufptr); + if (i % DUMP_BYTES_PER_ROW == (DUMP_BYTES_PER_ROW - 1)) { + LOGV("%s", buffer); + bufptr = buffer; + } + } + if (bufptr != buffer) { + // print last line + LOGV("%s", buffer); + } + LOGV("\n"); +} + +uint16_t MtpPacket::getUInt16(int offset) const { + return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset]; +} + +uint32_t MtpPacket::getUInt32(int offset) const { + return ((uint32_t)mBuffer[offset + 3] << 24) | ((uint32_t)mBuffer[offset + 2] << 16) | + ((uint32_t)mBuffer[offset + 1] << 8) | (uint32_t)mBuffer[offset]; +} + +void MtpPacket::putUInt16(int offset, uint16_t value) { + mBuffer[offset++] = (uint8_t)(value & 0xFF); + mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF); +} + +void MtpPacket::putUInt32(int offset, uint32_t value) { + mBuffer[offset++] = (uint8_t)(value & 0xFF); + mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF); + mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF); + mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF); +} + +uint16_t MtpPacket::getContainerCode() const { + return getUInt16(MTP_CONTAINER_CODE_OFFSET); +} + +void MtpPacket::setContainerCode(uint16_t code) { + putUInt16(MTP_CONTAINER_CODE_OFFSET, code); +} + +MtpTransactionID MtpPacket::getTransactionID() const { + return getUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET); +} + +void MtpPacket::setTransactionID(MtpTransactionID id) { + putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id); +} + +uint32_t MtpPacket::getParameter(int index) const { + if (index < 1 || index > 5) { + LOGE("index %d out of range in MtpRequestPacket::getParameter", index); + return 0; + } + return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t)); +} + +void MtpPacket::setParameter(int index, uint32_t value) { + if (index < 1 || index > 5) { + LOGE("index %d out of range in MtpResponsePacket::setParameter", index); + return; + } + int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t); + if (mPacketSize < offset + sizeof(uint32_t)) + mPacketSize = offset + sizeof(uint32_t); + putUInt32(offset, value); +} + +#ifdef MTP_HOST +int MtpPacket::transfer(struct usb_endpoint *ep, void* buffer, int length) { + if (usb_endpoint_queue(ep, buffer, length)) { + LOGE("usb_endpoint_queue failed, errno: %d", errno); + return -1; + } + int ep_num; + return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num); +} +#endif + +} // namespace android diff --git a/media/mtp/MtpPacket.h b/media/mtp/MtpPacket.h new file mode 100644 index 0000000..9c8d6da --- /dev/null +++ b/media/mtp/MtpPacket.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef _MTP_PACKET_H +#define _MTP_PACKET_H + +#include "MtpTypes.h" + +struct usb_endpoint; + +namespace android { + +class MtpPacket { + +protected: + uint8_t* mBuffer; + // current size of the buffer + int mBufferSize; + // number of bytes to add when resizing the buffer + int mAllocationIncrement; + // size of the data in the packet + int mPacketSize; + +public: + MtpPacket(int bufferSize); + virtual ~MtpPacket(); + + // sets packet size to the default container size and sets buffer to zero + virtual void reset(); + + void allocate(int length); + void dump(); + + uint16_t getContainerCode() const; + void setContainerCode(uint16_t code); + + MtpTransactionID getTransactionID() const; + void setTransactionID(MtpTransactionID id); + + uint32_t getParameter(int index) const; + void setParameter(int index, uint32_t value); + +#ifdef MTP_HOST + int transfer(struct usb_endpoint *ep, void* buffer, int length); +#endif + +protected: + uint16_t getUInt16(int offset) const; + uint32_t getUInt32(int offset) const; + void putUInt16(int offset, uint16_t value); + void putUInt32(int offset, uint32_t value); +}; + +}; // namespace android + +#endif // _MTP_PACKET_H diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp new file mode 100644 index 0000000..86889c3 --- /dev/null +++ b/media/mtp/MtpProperty.cpp @@ -0,0 +1,445 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpProperty" + +#include "MtpDataPacket.h" +#include "MtpProperty.h" +#include "MtpStringBuffer.h" +#include "MtpUtils.h" + +namespace android { + +MtpProperty::MtpProperty() + : mCode(0), + mType(0), + mWriteable(false), + mDefaultArrayLength(0), + mDefaultArrayValues(NULL), + mCurrentArrayLength(0), + mCurrentArrayValues(NULL), + mGroupCode(0), + mFormFlag(kFormNone), + mEnumLength(0), + mEnumValues(NULL) +{ + memset(&mDefaultValue, 0, sizeof(mDefaultValue)); + memset(&mCurrentValue, 0, sizeof(mCurrentValue)); + memset(&mMinimumValue, 0, sizeof(mMinimumValue)); + memset(&mMaximumValue, 0, sizeof(mMaximumValue)); +} + +MtpProperty::MtpProperty(MtpPropertyCode propCode, + MtpDataType type, + bool writeable, + int defaultValue) + : mCode(propCode), + mType(type), + mWriteable(writeable), + mDefaultArrayLength(0), + mDefaultArrayValues(NULL), + mCurrentArrayLength(0), + mCurrentArrayValues(NULL), + mGroupCode(-1), // disable multiple properties in GetObjectPropList for now + mFormFlag(kFormNone), + mEnumLength(0), + mEnumValues(NULL) +{ + memset(&mDefaultValue, 0, sizeof(mDefaultValue)); + memset(&mCurrentValue, 0, sizeof(mCurrentValue)); + memset(&mMinimumValue, 0, sizeof(mMinimumValue)); + memset(&mMaximumValue, 0, sizeof(mMaximumValue)); + + if (defaultValue) { + switch (type) { + case MTP_TYPE_INT8: + mDefaultValue.u.i8 = defaultValue; + break; + case MTP_TYPE_UINT8: + mDefaultValue.u.u8 = defaultValue; + break; + case MTP_TYPE_INT16: + mDefaultValue.u.i16 = defaultValue; + break; + case MTP_TYPE_UINT16: + mDefaultValue.u.u16 = defaultValue; + break; + case MTP_TYPE_INT32: + mDefaultValue.u.i32 = defaultValue; + break; + case MTP_TYPE_UINT32: + mDefaultValue.u.u32 = defaultValue; + break; + case MTP_TYPE_INT64: + mDefaultValue.u.i64 = defaultValue; + break; + case MTP_TYPE_UINT64: + mDefaultValue.u.u64 = defaultValue; + break; + default: + LOGE("unknown type %04X in MtpProperty::MtpProperty", type); + } + } +} + +MtpProperty::~MtpProperty() { + if (mType == MTP_TYPE_STR) { + // free all strings + free(mDefaultValue.str); + free(mCurrentValue.str); + free(mMinimumValue.str); + free(mMaximumValue.str); + if (mDefaultArrayValues) { + for (int i = 0; i < mDefaultArrayLength; i++) + free(mDefaultArrayValues[i].str); + } + if (mCurrentArrayValues) { + for (int i = 0; i < mCurrentArrayLength; i++) + free(mCurrentArrayValues[i].str); + } + if (mEnumValues) { + for (int i = 0; i < mEnumLength; i++) + free(mEnumValues[i].str); + } + } + delete[] mDefaultArrayValues; + delete[] mCurrentArrayValues; + delete[] mEnumValues; +} + +void MtpProperty::read(MtpDataPacket& packet) { + bool deviceProp = isDeviceProperty(); + + mCode = packet.getUInt16(); + mType = packet.getUInt16(); + mWriteable = (packet.getUInt8() == 1); + switch (mType) { + case MTP_TYPE_AINT8: + case MTP_TYPE_AUINT8: + case MTP_TYPE_AINT16: + case MTP_TYPE_AUINT16: + case MTP_TYPE_AINT32: + case MTP_TYPE_AUINT32: + case MTP_TYPE_AINT64: + case MTP_TYPE_AUINT64: + case MTP_TYPE_AINT128: + case MTP_TYPE_AUINT128: + mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength); + if (deviceProp) + mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength); + break; + default: + readValue(packet, mDefaultValue); + if (deviceProp) + readValue(packet, mCurrentValue); + } + if (!deviceProp) + mGroupCode = packet.getUInt32(); + mFormFlag = packet.getUInt8(); + + if (mFormFlag == kFormRange) { + readValue(packet, mMinimumValue); + readValue(packet, mMaximumValue); + readValue(packet, mStepSize); + } else if (mFormFlag == kFormEnum) { + mEnumLength = packet.getUInt16(); + mEnumValues = new MtpPropertyValue[mEnumLength]; + for (int i = 0; i < mEnumLength; i++) + readValue(packet, mEnumValues[i]); + } +} + +void MtpProperty::write(MtpDataPacket& packet) { + bool deviceProp = isDeviceProperty(); + + packet.putUInt16(mCode); + packet.putUInt16(mType); + packet.putUInt8(mWriteable ? 1 : 0); + + switch (mType) { + case MTP_TYPE_AINT8: + case MTP_TYPE_AUINT8: + case MTP_TYPE_AINT16: + case MTP_TYPE_AUINT16: + case MTP_TYPE_AINT32: + case MTP_TYPE_AUINT32: + case MTP_TYPE_AINT64: + case MTP_TYPE_AUINT64: + case MTP_TYPE_AINT128: + case MTP_TYPE_AUINT128: + writeArrayValues(packet, mDefaultArrayValues, mDefaultArrayLength); + if (deviceProp) + writeArrayValues(packet, mCurrentArrayValues, mCurrentArrayLength); + break; + default: + writeValue(packet, mDefaultValue); + if (deviceProp) + writeValue(packet, mCurrentValue); + } + packet.putUInt32(mGroupCode); + if (!deviceProp) + packet.putUInt8(mFormFlag); + if (mFormFlag == kFormRange) { + writeValue(packet, mMinimumValue); + writeValue(packet, mMaximumValue); + writeValue(packet, mStepSize); + } else if (mFormFlag == kFormEnum) { + packet.putUInt16(mEnumLength); + for (int i = 0; i < mEnumLength; i++) + writeValue(packet, mEnumValues[i]); + } +} + +void MtpProperty::setDefaultValue(const uint16_t* string) { + free(mDefaultValue.str); + if (string) { + MtpStringBuffer buffer(string); + mDefaultValue.str = strdup(buffer); + } + else + mDefaultValue.str = NULL; +} + +void MtpProperty::setCurrentValue(const uint16_t* string) { + free(mCurrentValue.str); + if (string) { + MtpStringBuffer buffer(string); + mCurrentValue.str = strdup(buffer); + } + else + mCurrentValue.str = NULL; +} + +void MtpProperty::setFormRange(int min, int max, int step) { + mFormFlag = kFormRange; + switch (mType) { + case MTP_TYPE_INT8: + mMinimumValue.u.i8 = min; + mMaximumValue.u.i8 = max; + mStepSize.u.i8 = step; + break; + case MTP_TYPE_UINT8: + mMinimumValue.u.u8 = min; + mMaximumValue.u.u8 = max; + mStepSize.u.u8 = step; + break; + case MTP_TYPE_INT16: + mMinimumValue.u.i16 = min; + mMaximumValue.u.i16 = max; + mStepSize.u.i16 = step; + break; + case MTP_TYPE_UINT16: + mMinimumValue.u.u16 = min; + mMaximumValue.u.u16 = max; + mStepSize.u.u16 = step; + break; + case MTP_TYPE_INT32: + mMinimumValue.u.i32 = min; + mMaximumValue.u.i32 = max; + mStepSize.u.i32 = step; + break; + case MTP_TYPE_UINT32: + mMinimumValue.u.u32 = min; + mMaximumValue.u.u32 = max; + mStepSize.u.u32 = step; + break; + case MTP_TYPE_INT64: + mMinimumValue.u.i64 = min; + mMaximumValue.u.i64 = max; + mStepSize.u.i64 = step; + break; + case MTP_TYPE_UINT64: + mMinimumValue.u.u64 = min; + mMaximumValue.u.u64 = max; + mStepSize.u.u64 = step; + break; + default: + LOGE("unsupported type for MtpProperty::setRange"); + break; + } +} + +void MtpProperty::setFormEnum(const int* values, int count) { + mFormFlag = kFormEnum; + delete[] mEnumValues; + mEnumValues = new MtpPropertyValue[count]; + mEnumLength = count; + + for (int i = 0; i < count; i++) { + int value = *values++; + switch (mType) { + case MTP_TYPE_INT8: + mEnumValues[i].u.i8 = value; + break; + case MTP_TYPE_UINT8: + mEnumValues[i].u.u8 = value; + break; + case MTP_TYPE_INT16: + mEnumValues[i].u.i16 = value; + break; + case MTP_TYPE_UINT16: + mEnumValues[i].u.u16 = value; + break; + case MTP_TYPE_INT32: + mEnumValues[i].u.i32 = value; + break; + case MTP_TYPE_UINT32: + mEnumValues[i].u.u32 = value; + break; + case MTP_TYPE_INT64: + mEnumValues[i].u.i64 = value; + break; + case MTP_TYPE_UINT64: + mEnumValues[i].u.u64 = value; + break; + default: + LOGE("unsupported type for MtpProperty::setEnum"); + break; + } + } +} + +void MtpProperty::print() { + LOGV("MtpProperty %04X\n", mCode); + LOGV(" type %04X\n", mType); + LOGV(" writeable %s\n", (mWriteable ? "true" : "false")); +} + +void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) { + MtpStringBuffer stringBuffer; + + switch (mType) { + case MTP_TYPE_INT8: + case MTP_TYPE_AINT8: + value.u.i8 = packet.getInt8(); + break; + case MTP_TYPE_UINT8: + case MTP_TYPE_AUINT8: + value.u.u8 = packet.getUInt8(); + break; + case MTP_TYPE_INT16: + case MTP_TYPE_AINT16: + value.u.i16 = packet.getInt16(); + break; + case MTP_TYPE_UINT16: + case MTP_TYPE_AUINT16: + value.u.u16 = packet.getUInt16(); + break; + case MTP_TYPE_INT32: + case MTP_TYPE_AINT32: + value.u.i32 = packet.getInt32(); + break; + case MTP_TYPE_UINT32: + case MTP_TYPE_AUINT32: + value.u.u32 = packet.getUInt32(); + break; + case MTP_TYPE_INT64: + case MTP_TYPE_AINT64: + value.u.i64 = packet.getInt64(); + break; + case MTP_TYPE_UINT64: + case MTP_TYPE_AUINT64: + value.u.u64 = packet.getUInt64(); + break; + case MTP_TYPE_INT128: + case MTP_TYPE_AINT128: + packet.getInt128(value.u.i128); + break; + case MTP_TYPE_UINT128: + case MTP_TYPE_AUINT128: + packet.getUInt128(value.u.u128); + break; + case MTP_TYPE_STR: + packet.getString(stringBuffer); + value.str = strdup(stringBuffer); + break; + default: + LOGE("unknown type %04X in MtpProperty::readValue", mType); + } +} + +void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) { + MtpStringBuffer stringBuffer; + + switch (mType) { + case MTP_TYPE_INT8: + case MTP_TYPE_AINT8: + packet.putInt8(value.u.i8); + break; + case MTP_TYPE_UINT8: + case MTP_TYPE_AUINT8: + packet.putUInt8(value.u.u8); + break; + case MTP_TYPE_INT16: + case MTP_TYPE_AINT16: + packet.putInt16(value.u.i16); + break; + case MTP_TYPE_UINT16: + case MTP_TYPE_AUINT16: + packet.putUInt16(value.u.u16); + break; + case MTP_TYPE_INT32: + case MTP_TYPE_AINT32: + packet.putInt32(value.u.i32); + break; + case MTP_TYPE_UINT32: + case MTP_TYPE_AUINT32: + packet.putUInt32(value.u.u32); + break; + case MTP_TYPE_INT64: + case MTP_TYPE_AINT64: + packet.putInt64(value.u.i64); + break; + case MTP_TYPE_UINT64: + case MTP_TYPE_AUINT64: + packet.putUInt64(value.u.u64); + break; + case MTP_TYPE_INT128: + case MTP_TYPE_AINT128: + packet.putInt128(value.u.i128); + break; + case MTP_TYPE_UINT128: + case MTP_TYPE_AUINT128: + packet.putUInt128(value.u.u128); + break; + case MTP_TYPE_STR: + if (value.str) + packet.putString(value.str); + else + packet.putEmptyString(); + break; + default: + LOGE("unknown type %04X in MtpProperty::writeValue", mType); + } +} + +MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) { + length = packet.getUInt32(); + if (length == 0) + return NULL; + MtpPropertyValue* result = new MtpPropertyValue[length]; + for (int i = 0; i < length; i++) + readValue(packet, result[i]); + return result; +} + +void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) { + packet.putUInt32(length); + for (int i = 0; i < length; i++) + writeValue(packet, values[i]); +} + +} // namespace android diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h new file mode 100644 index 0000000..c12399c --- /dev/null +++ b/media/mtp/MtpProperty.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef _MTP_PROPERTY_H +#define _MTP_PROPERTY_H + +#include "MtpTypes.h" + +namespace android { + +class MtpDataPacket; + +struct MtpPropertyValue { + union { + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; + int128_t i128; + uint128_t u128; + } u; + // string in UTF8 format + char* str; +}; + +class MtpProperty { +public: + MtpPropertyCode mCode; + MtpDataType mType; + bool mWriteable; + MtpPropertyValue mDefaultValue; + MtpPropertyValue mCurrentValue; + + // for array types + int mDefaultArrayLength; + MtpPropertyValue* mDefaultArrayValues; + int mCurrentArrayLength; + MtpPropertyValue* mCurrentArrayValues; + + enum { + kFormNone = 0, + kFormRange = 1, + kFormEnum = 2, + }; + + uint32_t mGroupCode; + uint8_t mFormFlag; + + // for range form + MtpPropertyValue mMinimumValue; + MtpPropertyValue mMaximumValue; + MtpPropertyValue mStepSize; + + // for enum form + int mEnumLength; + MtpPropertyValue* mEnumValues; + +public: + MtpProperty(); + MtpProperty(MtpPropertyCode propCode, + MtpDataType type, + bool writeable = false, + int defaultValue = 0); + virtual ~MtpProperty(); + + inline MtpPropertyCode getPropertyCode() const { return mCode; } + + void read(MtpDataPacket& packet); + void write(MtpDataPacket& packet); + + void setDefaultValue(const uint16_t* string); + void setCurrentValue(const uint16_t* string); + + void setFormRange(int min, int max, int step); + void setFormEnum(const int* values, int count); + + void print(); + + inline bool isDeviceProperty() const { + return ( ((mCode & 0xF000) == 0x5000) + || ((mCode & 0xF800) == 0xD000)); + } + +private: + void readValue(MtpDataPacket& packet, MtpPropertyValue& value); + void writeValue(MtpDataPacket& packet, MtpPropertyValue& value); + MtpPropertyValue* readArrayValues(MtpDataPacket& packet, int& length); + void writeArrayValues(MtpDataPacket& packet, + MtpPropertyValue* values, int length); +}; + +}; // namespace android + +#endif // _MTP_PROPERTY_H diff --git a/media/mtp/MtpRequestPacket.cpp b/media/mtp/MtpRequestPacket.cpp new file mode 100644 index 0000000..8ece580 --- /dev/null +++ b/media/mtp/MtpRequestPacket.cpp @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpRequestPacket" + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> + +#include "MtpRequestPacket.h" + +namespace android { + +MtpRequestPacket::MtpRequestPacket() + : MtpPacket(512) +{ +} + +MtpRequestPacket::~MtpRequestPacket() { +} + +#ifdef MTP_DEVICE +int MtpRequestPacket::read(int fd) { + int ret = ::read(fd, mBuffer, mBufferSize); + if (ret >= 0) + mPacketSize = ret; + else + mPacketSize = 0; + return ret; +} +#endif + +#ifdef MTP_HOST + // write our buffer to the given endpoint (host mode) +int MtpRequestPacket::write(struct usb_endpoint *ep) +{ + putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); + putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_COMMAND); + return transfer(ep, mBuffer, mPacketSize); +} +#endif + +} // namespace android diff --git a/media/mtp/MtpRequestPacket.h b/media/mtp/MtpRequestPacket.h new file mode 100644 index 0000000..df518f2 --- /dev/null +++ b/media/mtp/MtpRequestPacket.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef _MTP_REQUEST_PACKET_H +#define _MTP_REQUEST_PACKET_H + +#include "MtpPacket.h" +#include "mtp.h" + +namespace android { + +class MtpRequestPacket : public MtpPacket { + +public: + MtpRequestPacket(); + virtual ~MtpRequestPacket(); + +#ifdef MTP_DEVICE + // fill our buffer with data from the given file descriptor + int read(int fd); +#endif + +#ifdef MTP_HOST + // write our buffer to the given endpoint + int write(struct usb_endpoint *ep); +#endif + + inline MtpOperationCode getOperationCode() const { return getContainerCode(); } + inline void setOperationCode(MtpOperationCode code) + { return setContainerCode(code); } +}; + +}; // namespace android + +#endif // _MTP_REQUEST_PACKET_H diff --git a/media/mtp/MtpResponsePacket.cpp b/media/mtp/MtpResponsePacket.cpp new file mode 100644 index 0000000..3ef714e --- /dev/null +++ b/media/mtp/MtpResponsePacket.cpp @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpResponsePacket" + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> + +#include "MtpResponsePacket.h" + +namespace android { + +MtpResponsePacket::MtpResponsePacket() + : MtpPacket(512) +{ +} + +MtpResponsePacket::~MtpResponsePacket() { +} + +#ifdef MTP_DEVICE +int MtpResponsePacket::write(int fd) { + putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); + putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE); + int ret = ::write(fd, mBuffer, mPacketSize); + return (ret < 0 ? ret : 0); +} +#endif + +#ifdef MTP_HOST + // read our buffer from the given endpoint +int MtpResponsePacket::read(struct usb_endpoint *ep) { + int ret = transfer(ep, mBuffer, mBufferSize); + if (ret >= 0) + mPacketSize = ret; + else + mPacketSize = 0; + return ret; +} +#endif + +} // namespace android + diff --git a/media/mtp/MtpResponsePacket.h b/media/mtp/MtpResponsePacket.h new file mode 100644 index 0000000..373f8f9 --- /dev/null +++ b/media/mtp/MtpResponsePacket.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef _MTP_RESPONSE_PACKET_H +#define _MTP_RESPONSE_PACKET_H + +#include "MtpPacket.h" +#include "mtp.h" + +namespace android { + +class MtpResponsePacket : public MtpPacket { + +public: + MtpResponsePacket(); + virtual ~MtpResponsePacket(); + +#ifdef MTP_DEVICE + // write our data to the given file descriptor + int write(int fd); +#endif + +#ifdef MTP_HOST + // read our buffer from the given endpoint + int read(struct usb_endpoint *ep); +#endif + + inline MtpResponseCode getResponseCode() const { return getContainerCode(); } + inline void setResponseCode(MtpResponseCode code) + { return setContainerCode(code); } +}; + +}; // namespace android + +#endif // _MTP_RESPONSE_PACKET_H diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp new file mode 100644 index 0000000..ca13636 --- /dev/null +++ b/media/mtp/MtpServer.cpp @@ -0,0 +1,829 @@ +/* + * 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. + */ + +#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 <sys/stat.h> +#include <dirent.h> + +#include <cutils/properties.h> + +#define LOG_TAG "MtpServer" + +#include "MtpDebug.h" +#include "MtpDatabase.h" +#include "MtpProperty.h" +#include "MtpServer.h" +#include "MtpStorage.h" +#include "MtpStringBuffer.h" + +#include <linux/usb/f_mtp.h> + +namespace android { + +static const MtpOperationCode kSupportedOperationCodes[] = { + MTP_OPERATION_GET_DEVICE_INFO, + MTP_OPERATION_OPEN_SESSION, + MTP_OPERATION_CLOSE_SESSION, + MTP_OPERATION_GET_STORAGE_IDS, + MTP_OPERATION_GET_STORAGE_INFO, + MTP_OPERATION_GET_NUM_OBJECTS, + MTP_OPERATION_GET_OBJECT_HANDLES, + MTP_OPERATION_GET_OBJECT_INFO, + MTP_OPERATION_GET_OBJECT, +// MTP_OPERATION_GET_THUMB, + MTP_OPERATION_DELETE_OBJECT, + MTP_OPERATION_SEND_OBJECT_INFO, + MTP_OPERATION_SEND_OBJECT, +// MTP_OPERATION_INITIATE_CAPTURE, +// MTP_OPERATION_FORMAT_STORE, +// MTP_OPERATION_RESET_DEVICE, +// MTP_OPERATION_SELF_TEST, +// MTP_OPERATION_SET_OBJECT_PROTECTION, +// MTP_OPERATION_POWER_DOWN, + MTP_OPERATION_GET_DEVICE_PROP_DESC, + MTP_OPERATION_GET_DEVICE_PROP_VALUE, + MTP_OPERATION_SET_DEVICE_PROP_VALUE, + MTP_OPERATION_RESET_DEVICE_PROP_VALUE, +// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, +// MTP_OPERATION_MOVE_OBJECT, +// MTP_OPERATION_COPY_OBJECT, +// MTP_OPERATION_GET_PARTIAL_OBJECT, +// MTP_OPERATION_INITIATE_OPEN_CAPTURE, + MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, + MTP_OPERATION_GET_OBJECT_PROP_DESC, + MTP_OPERATION_GET_OBJECT_PROP_VALUE, + MTP_OPERATION_SET_OBJECT_PROP_VALUE, + MTP_OPERATION_GET_OBJECT_PROP_LIST, +// MTP_OPERATION_SET_OBJECT_PROP_LIST, +// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, +// MTP_OPERATION_SEND_OBJECT_PROP_LIST, + MTP_OPERATION_GET_OBJECT_REFERENCES, + MTP_OPERATION_SET_OBJECT_REFERENCES, +// MTP_OPERATION_SKIP, +}; + +static const MtpEventCode kSupportedEventCodes[] = { + MTP_EVENT_OBJECT_ADDED, + MTP_EVENT_OBJECT_REMOVED, +}; + +MtpServer::MtpServer(int fd, MtpDatabase* database, + int fileGroup, int filePerm, int directoryPerm) + : mFD(fd), + mDatabase(database), + mFileGroup(fileGroup), + mFilePermission(filePerm), + mDirectoryPermission(directoryPerm), + mSessionID(0), + mSessionOpen(false), + mSendObjectHandle(kInvalidObjectHandle), + mSendObjectFormat(0), + mSendObjectFileSize(0) +{ +} + +MtpServer::~MtpServer() { +} + +void MtpServer::addStorage(const char* filePath) { + int index = mStorages.size() + 1; + index |= index << 16; // set high and low part to our index + MtpStorage* storage = new MtpStorage(index, filePath, mDatabase); + addStorage(storage); +} + +MtpStorage* MtpServer::getStorage(MtpStorageID id) { + for (int i = 0; i < mStorages.size(); i++) { + MtpStorage* storage = mStorages[i]; + if (storage->getStorageID() == id) + return storage; + } + return NULL; +} + +void MtpServer::run() { + int fd = mFD; + + LOGV("MtpServer::run fd: %d\n", fd); + + while (1) { + int ret = mRequest.read(fd); + if (ret < 0) { + LOGE("request read returned %d, errno: %d", ret, errno); + if (errno == ECANCELED) { + // return to top of loop and wait for next command + continue; + } + break; + } + MtpOperationCode operation = mRequest.getOperationCode(); + MtpTransactionID transaction = mRequest.getTransactionID(); + + LOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); + mRequest.dump(); + + // FIXME need to generalize this + bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO + || operation == MTP_OPERATION_SET_OBJECT_REFERENCES + || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE + || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); + if (dataIn) { + int ret = mData.read(fd); + if (ret < 0) { + LOGE("data read returned %d, errno: %d", ret, errno); + if (errno == ECANCELED) { + // return to top of loop and wait for next command + continue; + } + break; + } + LOGV("received data:"); + mData.dump(); + } else { + mData.reset(); + } + + if (handleRequest()) { + if (!dataIn && mData.hasData()) { + mData.setOperationCode(operation); + mData.setTransactionID(transaction); + LOGV("sending data:"); + mData.dump(); + ret = mData.write(fd); + if (ret < 0) { + LOGE("request write returned %d, errno: %d", ret, errno); + if (errno == ECANCELED) { + // return to top of loop and wait for next command + continue; + } + break; + } + } + + mResponse.setTransactionID(transaction); + LOGV("sending response %04X", mResponse.getResponseCode()); + ret = mResponse.write(fd); + mResponse.dump(); + if (ret < 0) { + LOGE("request write returned %d, errno: %d", ret, errno); + if (errno == ECANCELED) { + // return to top of loop and wait for next command + continue; + } + break; + } + } else { + LOGV("skipping response\n"); + } + } + + if (mSessionOpen) + mDatabase->sessionEnded(); +} + +void MtpServer::sendObjectAdded(MtpObjectHandle handle) { + if (mSessionOpen) { + LOGD("sendObjectAdded %d\n", handle); + mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED); + mEvent.setTransactionID(mRequest.getTransactionID()); + mEvent.setParameter(1, handle); + int ret = mEvent.write(mFD); + LOGD("mEvent.write returned %d\n", ret); + } +} + +void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { + if (mSessionOpen) { + LOGD("sendObjectRemoved %d\n", handle); + mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED); + mEvent.setTransactionID(mRequest.getTransactionID()); + mEvent.setParameter(1, handle); + int ret = mEvent.write(mFD); + LOGD("mEvent.write returned %d\n", ret); + } +} + +bool MtpServer::handleRequest() { + MtpOperationCode operation = mRequest.getOperationCode(); + MtpResponseCode response; + + mResponse.reset(); + + if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { + // FIXME - need to delete mSendObjectHandle from the database + LOGE("expected SendObject after SendObjectInfo"); + mSendObjectHandle = kInvalidObjectHandle; + } + + switch (operation) { + case MTP_OPERATION_GET_DEVICE_INFO: + response = doGetDeviceInfo(); + break; + case MTP_OPERATION_OPEN_SESSION: + response = doOpenSession(); + break; + case MTP_OPERATION_CLOSE_SESSION: + response = doCloseSession(); + break; + case MTP_OPERATION_GET_STORAGE_IDS: + response = doGetStorageIDs(); + break; + case MTP_OPERATION_GET_STORAGE_INFO: + response = doGetStorageInfo(); + break; + case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: + response = doGetObjectPropsSupported(); + break; + case MTP_OPERATION_GET_OBJECT_HANDLES: + response = doGetObjectHandles(); + break; + case MTP_OPERATION_GET_NUM_OBJECTS: + response = doGetNumObjects(); + break; + case MTP_OPERATION_GET_OBJECT_REFERENCES: + response = doGetObjectReferences(); + break; + case MTP_OPERATION_SET_OBJECT_REFERENCES: + response = doSetObjectReferences(); + break; + case MTP_OPERATION_GET_OBJECT_PROP_VALUE: + response = doGetObjectPropValue(); + break; + case MTP_OPERATION_SET_OBJECT_PROP_VALUE: + response = doSetObjectPropValue(); + break; + case MTP_OPERATION_GET_DEVICE_PROP_VALUE: + response = doGetDevicePropValue(); + break; + case MTP_OPERATION_SET_DEVICE_PROP_VALUE: + response = doSetDevicePropValue(); + break; + case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: + response = doResetDevicePropValue(); + break; + case MTP_OPERATION_GET_OBJECT_PROP_LIST: + response = doGetObjectPropList(); + break; + case MTP_OPERATION_GET_OBJECT_INFO: + response = doGetObjectInfo(); + break; + case MTP_OPERATION_GET_OBJECT: + response = doGetObject(); + break; + case MTP_OPERATION_SEND_OBJECT_INFO: + response = doSendObjectInfo(); + break; + case MTP_OPERATION_SEND_OBJECT: + response = doSendObject(); + break; + case MTP_OPERATION_DELETE_OBJECT: + response = doDeleteObject(); + break; + case MTP_OPERATION_GET_OBJECT_PROP_DESC: + response = doGetObjectPropDesc(); + break; + case MTP_OPERATION_GET_DEVICE_PROP_DESC: + response = doGetDevicePropDesc(); + break; + default: + LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); + response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + break; + } + + if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) + return false; + mResponse.setResponseCode(response); + return true; +} + +MtpResponseCode MtpServer::doGetDeviceInfo() { + MtpStringBuffer string; + char prop_value[PROPERTY_VALUE_MAX]; + + MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); + MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); + MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); + + // fill in device info + mData.putUInt16(MTP_STANDARD_VERSION); + mData.putUInt32(6); // MTP Vendor Extension ID + mData.putUInt16(MTP_STANDARD_VERSION); + string.set("microsoft.com: 1.0;"); + mData.putString(string); // MTP Extensions + mData.putUInt16(0); //Functional Mode + mData.putAUInt16(kSupportedOperationCodes, + sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported + mData.putAUInt16(kSupportedEventCodes, + sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported + mData.putAUInt16(deviceProperties); // Device Properties Supported + mData.putAUInt16(captureFormats); // Capture Formats + mData.putAUInt16(playbackFormats); // Playback Formats + // FIXME + string.set("Google, Inc."); + mData.putString(string); // Manufacturer + + property_get("ro.product.model", prop_value, "MTP Device"); + string.set(prop_value); + mData.putString(string); // Model + string.set("1.0"); + mData.putString(string); // Device Version + + property_get("ro.serialno", prop_value, "????????"); + string.set(prop_value); + mData.putString(string); // Serial Number + + delete playbackFormats; + delete captureFormats; + delete deviceProperties; + + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doOpenSession() { + if (mSessionOpen) { + mResponse.setParameter(1, mSessionID); + return MTP_RESPONSE_SESSION_ALREADY_OPEN; + } + mSessionID = mRequest.getParameter(1); + mSessionOpen = true; + + mDatabase->sessionStarted(); + + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doCloseSession() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + mSessionID = 0; + mSessionOpen = false; + mDatabase->sessionEnded(); + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetStorageIDs() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + + int count = mStorages.size(); + mData.putUInt32(count); + for (int i = 0; i < count; i++) + mData.putUInt32(mStorages[i]->getStorageID()); + + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetStorageInfo() { + MtpStringBuffer string; + + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpStorageID id = mRequest.getParameter(1); + MtpStorage* storage = getStorage(id); + if (!storage) + return MTP_RESPONSE_INVALID_STORAGE_ID; + + mData.putUInt16(storage->getType()); + mData.putUInt16(storage->getFileSystemType()); + mData.putUInt16(storage->getAccessCapability()); + mData.putUInt64(storage->getMaxCapacity()); + mData.putUInt64(storage->getFreeSpace()); + mData.putUInt32(1024*1024*1024); // Free Space in Objects + string.set(storage->getDescription()); + mData.putString(string); + mData.putEmptyString(); // Volume Identifier + + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetObjectPropsSupported() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpObjectFormat format = mRequest.getParameter(1); + MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format); + mData.putAUInt16(properties); + delete properties; + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetObjectHandles() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage + MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats + MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent + // 0x00000000 for all objects? + if (parent == 0xFFFFFFFF) + parent = 0; + + MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); + mData.putAUInt32(handles); + delete handles; + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetNumObjects() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage + MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats + MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent + // 0x00000000 for all objects? + if (parent == 0xFFFFFFFF) + parent = 0; + + int count = mDatabase->getNumObjects(storageID, format, parent); + if (count >= 0) { + mResponse.setParameter(1, count); + return MTP_RESPONSE_OK; + } else { + mResponse.setParameter(1, 0); + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } +} + +MtpResponseCode MtpServer::doGetObjectReferences() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpStorageID handle = mRequest.getParameter(1); + + // FIXME - check for invalid object handle + MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle); + if (handles) { + mData.putAUInt32(handles); + delete handles; + } else { + mData.putEmptyArray(); + } + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doSetObjectReferences() { + if (!mSessionOpen) + return MTP_RESPONSE_SESSION_NOT_OPEN; + MtpStorageID handle = mRequest.getParameter(1); + MtpObjectHandleList* references = mData.getAUInt32(); + MtpResponseCode result = mDatabase->setObjectReferences(handle, references); + delete references; + return result; +} + +MtpResponseCode MtpServer::doGetObjectPropValue() { + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectProperty property = mRequest.getParameter(2); + LOGD("GetObjectPropValue %d %s\n", handle, + MtpDebug::getObjectPropCodeName(property)); + + return mDatabase->getObjectPropertyValue(handle, property, mData); +} + +MtpResponseCode MtpServer::doSetObjectPropValue() { + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectProperty property = mRequest.getParameter(2); + LOGD("SetObjectPropValue %d %s\n", handle, + MtpDebug::getObjectPropCodeName(property)); + + return mDatabase->setObjectPropertyValue(handle, property, mData); +} + +MtpResponseCode MtpServer::doGetDevicePropValue() { + MtpDeviceProperty property = mRequest.getParameter(1); + LOGD("GetDevicePropValue %s\n", + MtpDebug::getDevicePropCodeName(property)); + + return mDatabase->getDevicePropertyValue(property, mData); +} + +MtpResponseCode MtpServer::doSetDevicePropValue() { + MtpDeviceProperty property = mRequest.getParameter(1); + LOGD("SetDevicePropValue %s\n", + MtpDebug::getDevicePropCodeName(property)); + + return mDatabase->setDevicePropertyValue(property, mData); +} + +MtpResponseCode MtpServer::doResetDevicePropValue() { + MtpDeviceProperty property = mRequest.getParameter(1); + LOGD("ResetDevicePropValue %s\n", + MtpDebug::getDevicePropCodeName(property)); + + return mDatabase->resetDeviceProperty(property); +} + +MtpResponseCode MtpServer::doGetObjectPropList() { + + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectFormat format = mRequest.getParameter(2); + MtpDeviceProperty property = mRequest.getParameter(3); + int groupCode = mRequest.getParameter(4); + int depth = mRequest.getParameter(4); + LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", + handle, MtpDebug::getFormatCodeName(format), + MtpDebug::getObjectPropCodeName(property), groupCode, depth); + + return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); +} + +MtpResponseCode MtpServer::doGetObjectInfo() { + MtpObjectHandle handle = mRequest.getParameter(1); + return mDatabase->getObjectInfo(handle, mData); +} + +MtpResponseCode MtpServer::doGetObject() { + MtpObjectHandle handle = mRequest.getParameter(1); + MtpString pathBuf; + int64_t fileLength; + int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength); + if (result != MTP_RESPONSE_OK) + return result; + + const char* filePath = (const char *)pathBuf; + mtp_file_range mfr; + mfr.fd = open(filePath, O_RDONLY); + if (mfr.fd < 0) { + return MTP_RESPONSE_GENERAL_ERROR; + } + mfr.offset = 0; + mfr.length = fileLength; + + // send data header + mData.setOperationCode(mRequest.getOperationCode()); + mData.setTransactionID(mRequest.getTransactionID()); + mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); + + // then transfer the file + int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); + close(mfr.fd); + if (ret < 0) { + if (errno == ECANCELED) + return MTP_RESPONSE_TRANSACTION_CANCELLED; + else + return MTP_RESPONSE_GENERAL_ERROR; + } + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doSendObjectInfo() { + MtpString path; + MtpStorageID storageID = mRequest.getParameter(1); + MtpStorage* storage = getStorage(storageID); + MtpObjectHandle parent = mRequest.getParameter(2); + if (!storage) + return MTP_RESPONSE_INVALID_STORAGE_ID; + + // special case the root + if (parent == MTP_PARENT_ROOT) { + path = storage->getPath(); + parent = 0; + } else { + int64_t dummy; + int result = mDatabase->getObjectFilePath(parent, path, dummy); + if (result != MTP_RESPONSE_OK) + return result; + } + + // read only the fields we need + mData.getUInt32(); // storage ID + MtpObjectFormat format = mData.getUInt16(); + mData.getUInt16(); // protection status + mSendObjectFileSize = mData.getUInt32(); + mData.getUInt16(); // thumb format + mData.getUInt32(); // thumb compressed size + mData.getUInt32(); // thumb pix width + mData.getUInt32(); // thumb pix height + mData.getUInt32(); // image pix width + mData.getUInt32(); // image pix height + mData.getUInt32(); // image bit depth + mData.getUInt32(); // parent + uint16_t associationType = mData.getUInt16(); + uint32_t associationDesc = mData.getUInt32(); // association desc + mData.getUInt32(); // sequence number + MtpStringBuffer name, created, modified; + mData.getString(name); // file name + mData.getString(created); // date created + mData.getString(modified); // date modified + // keywords follow + + LOGD("name: %s format: %04X\n", (const char *)name, format); + time_t modifiedTime; + if (!parseDateTime(modified, modifiedTime)) + modifiedTime = 0; + + if (path[path.size() - 1] != '/') + path += "/"; + path += (const char *)name; + + MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, + format, parent, storageID, mSendObjectFileSize, modifiedTime); + if (handle == kInvalidObjectHandle) { + return MTP_RESPONSE_GENERAL_ERROR; + } + + if (format == MTP_FORMAT_ASSOCIATION) { + mode_t mask = umask(0); + int ret = mkdir((const char *)path, mDirectoryPermission); + umask(mask); + if (ret && ret != -EEXIST) + return MTP_RESPONSE_GENERAL_ERROR; + chown((const char *)path, getuid(), mFileGroup); + } else { + mSendObjectFilePath = path; + // save the handle for the SendObject call, which should follow + mSendObjectHandle = handle; + mSendObjectFormat = format; + } + + mResponse.setParameter(1, storageID); + mResponse.setParameter(2, parent); + mResponse.setParameter(3, handle); + + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doSendObject() { + MtpResponseCode result = MTP_RESPONSE_OK; + mode_t mask; + int ret; + uint64_t actualSize = -1; + + if (mSendObjectHandle == kInvalidObjectHandle) { + LOGE("Expected SendObjectInfo before SendObject"); + result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; + goto done; + } + + // read the header + ret = mData.readDataHeader(mFD); + // FIXME - check for errors here. + + // reset so we don't attempt to send this back + mData.reset(); + + mtp_file_range mfr; + mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); + if (mfr.fd < 0) { + result = MTP_RESPONSE_GENERAL_ERROR; + goto done; + } + fchown(mfr.fd, getuid(), mFileGroup); + // set permissions + mask = umask(0); + fchmod(mfr.fd, mFilePermission); + umask(mask); + + mfr.offset = 0; + mfr.length = mSendObjectFileSize; + + LOGD("receiving %s\n", (const char *)mSendObjectFilePath); + // transfer the file + ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); + close(mfr.fd); + + LOGV("MTP_RECEIVE_FILE returned %d", ret); + + if (ret < 0) { + unlink(mSendObjectFilePath); + if (errno == ECANCELED) + result = MTP_RESPONSE_TRANSACTION_CANCELLED; + else + result = MTP_RESPONSE_GENERAL_ERROR; + } else if (mSendObjectFileSize == 0xFFFFFFFF) { + // actual size is likely > 4 gig so stat the file to compute actual length + struct stat s; + if (lstat(mSendObjectFilePath, &s) == 0) { + actualSize = s.st_size; + LOGD("actualSize: %lld\n", actualSize); + } + } + +done: + mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, + actualSize, result == MTP_RESPONSE_OK); + mSendObjectHandle = kInvalidObjectHandle; + mSendObjectFormat = 0; + return result; +} + +static void deleteRecursive(const char* path) { + char pathbuf[PATH_MAX]; + int pathLength = strlen(path); + if (pathLength >= sizeof(pathbuf) - 1) { + LOGE("path too long: %s\n", path); + } + strcpy(pathbuf, path); + if (pathbuf[pathLength - 1] != '/') { + pathbuf[pathLength++] = '/'; + } + char* fileSpot = pathbuf + pathLength; + int pathRemaining = sizeof(pathbuf) - pathLength - 1; + + DIR* dir = opendir(path); + if (!dir) { + LOGE("opendir %s failed: %s", path, strerror(errno)); + return; + } + + struct dirent* entry; + while ((entry = readdir(dir))) { + const char* name = entry->d_name; + + // ignore "." and ".." + if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { + continue; + } + + int nameLength = strlen(name); + if (nameLength > pathRemaining) { + LOGE("path %s/%s too long\n", path, name); + continue; + } + strcpy(fileSpot, name); + + int type = entry->d_type; + if (entry->d_type == DT_DIR) { + deleteRecursive(pathbuf); + rmdir(pathbuf); + } else { + unlink(pathbuf); + } + } + closedir(dir); +} + +static void deletePath(const char* path) { + struct stat statbuf; + if (stat(path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + deleteRecursive(path); + rmdir(path); + } else { + unlink(path); + } + } else { + LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); + } +} + +MtpResponseCode MtpServer::doDeleteObject() { + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectFormat format = mRequest.getParameter(2); + // FIXME - support deleting all objects if handle is 0xFFFFFFFF + // FIXME - implement deleting objects by format + + MtpString filePath; + int64_t fileLength; + int result = mDatabase->getObjectFilePath(handle, filePath, fileLength); + if (result == MTP_RESPONSE_OK) { + LOGV("deleting %s", (const char *)filePath); + deletePath((const char *)filePath); + return mDatabase->deleteFile(handle); + } else { + return result; + } +} + +MtpResponseCode MtpServer::doGetObjectPropDesc() { + MtpObjectProperty propCode = mRequest.getParameter(1); + MtpObjectFormat format = mRequest.getParameter(2); + LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), + MtpDebug::getFormatCodeName(format)); + MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); + if (!property) + return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; + property->write(mData); + delete property; + return MTP_RESPONSE_OK; +} + +MtpResponseCode MtpServer::doGetDevicePropDesc() { + MtpDeviceProperty propCode = mRequest.getParameter(1); + LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); + MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); + if (!property) + return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; + property->write(mData); + delete property; + return MTP_RESPONSE_OK; +} + +} // namespace android diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h new file mode 100644 index 0000000..e65ddb0 --- /dev/null +++ b/media/mtp/MtpServer.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#ifndef _MTP_SERVER_H +#define _MTP_SERVER_H + +#include "MtpRequestPacket.h" +#include "MtpDataPacket.h" +#include "MtpResponsePacket.h" +#include "MtpEventPacket.h" +#include "mtp.h" + +#include "MtpUtils.h" + +namespace android { + +class MtpDatabase; +class MtpStorage; + +class MtpServer { + +private: + // file descriptor for MTP kernel driver + int mFD; + + MtpDatabase* mDatabase; + + // group to own new files and folders + int mFileGroup; + // permissions for new files and directories + int mFilePermission; + int mDirectoryPermission; + + // current session ID + MtpSessionID mSessionID; + // true if we have an open session and mSessionID is valid + bool mSessionOpen; + + MtpRequestPacket mRequest; + MtpDataPacket mData; + MtpResponsePacket mResponse; + MtpEventPacket mEvent; + + MtpStorageList mStorages; + + // handle for new object, set by SendObjectInfo and used by SendObject + MtpObjectHandle mSendObjectHandle; + MtpObjectFormat mSendObjectFormat; + MtpString mSendObjectFilePath; + size_t mSendObjectFileSize; + +public: + MtpServer(int fd, MtpDatabase* database, + int fileGroup, int filePerm, int directoryPerm); + virtual ~MtpServer(); + + void addStorage(const char* filePath); + inline void addStorage(MtpStorage* storage) { mStorages.push(storage); } + MtpStorage* getStorage(MtpStorageID id); + void run(); + + void sendObjectAdded(MtpObjectHandle handle); + void sendObjectRemoved(MtpObjectHandle handle); + +private: + bool handleRequest(); + + MtpResponseCode doGetDeviceInfo(); + MtpResponseCode doOpenSession(); + MtpResponseCode doCloseSession(); + MtpResponseCode doGetStorageIDs(); + MtpResponseCode doGetStorageInfo(); + MtpResponseCode doGetObjectPropsSupported(); + MtpResponseCode doGetObjectHandles(); + MtpResponseCode doGetNumObjects(); + MtpResponseCode doGetObjectReferences(); + MtpResponseCode doSetObjectReferences(); + MtpResponseCode doGetObjectPropValue(); + MtpResponseCode doSetObjectPropValue(); + MtpResponseCode doGetDevicePropValue(); + MtpResponseCode doSetDevicePropValue(); + MtpResponseCode doResetDevicePropValue(); + MtpResponseCode doGetObjectPropList(); + MtpResponseCode doGetObjectInfo(); + MtpResponseCode doGetObject(); + MtpResponseCode doSendObjectInfo(); + MtpResponseCode doSendObject(); + MtpResponseCode doDeleteObject(); + MtpResponseCode doGetObjectPropDesc(); + MtpResponseCode doGetDevicePropDesc(); +}; + +}; // namespace android + +#endif // _MTP_SERVER_H diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp new file mode 100644 index 0000000..eccf186 --- /dev/null +++ b/media/mtp/MtpStorage.cpp @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpStorage" + +#include "MtpDebug.h" +#include "MtpDatabase.h" +#include "MtpStorage.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> + +namespace android { + +MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db) + : mStorageID(id), + mFilePath(filePath), + mDatabase(db), + mMaxCapacity(0) +{ + LOGD("MtpStorage id: %d path: %s\n", id, filePath); +} + +MtpStorage::~MtpStorage() { +} + +int MtpStorage::getType() const { + return MTP_STORAGE_FIXED_RAM; +} + +int MtpStorage::getFileSystemType() const { + return MTP_STORAGE_FILESYSTEM_HIERARCHICAL; +} + +int MtpStorage::getAccessCapability() const { + return MTP_STORAGE_READ_WRITE; +} + +uint64_t MtpStorage::getMaxCapacity() { + if (mMaxCapacity == 0) { + struct statfs stat; + if (statfs(mFilePath, &stat)) + return -1; + mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize; + } + return mMaxCapacity; +} + +uint64_t MtpStorage::getFreeSpace() { + struct statfs stat; + if (statfs(mFilePath, &stat)) + return -1; + return (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize; +} + +const char* MtpStorage::getDescription() const { + return "Device Storage"; +} + +} // namespace android diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h new file mode 100644 index 0000000..b13b926 --- /dev/null +++ b/media/mtp/MtpStorage.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef _MTP_STORAGE_H +#define _MTP_STORAGE_H + +#include "mtp.h" + +namespace android { + +class MtpDatabase; + +class MtpStorage { + +private: + MtpStorageID mStorageID; + const char* mFilePath; + MtpDatabase* mDatabase; + uint64_t mMaxCapacity; + +public: + MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db); + virtual ~MtpStorage(); + + inline MtpStorageID getStorageID() const { return mStorageID; } + int getType() const; + int getFileSystemType() const; + int getAccessCapability() const; + uint64_t getMaxCapacity(); + uint64_t getFreeSpace(); + const char* getDescription() const; + inline const char* getPath() const { return mFilePath; } +}; + +}; // namespace android + +#endif // _MTP_STORAGE_H diff --git a/media/mtp/MtpStorageInfo.cpp b/media/mtp/MtpStorageInfo.cpp new file mode 100644 index 0000000..ca64ac0 --- /dev/null +++ b/media/mtp/MtpStorageInfo.cpp @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpStorageInfo" + +#include "MtpDebug.h" +#include "MtpDataPacket.h" +#include "MtpStorageInfo.h" +#include "MtpStringBuffer.h" + +namespace android { + +MtpStorageInfo::MtpStorageInfo(MtpStorageID id) + : mStorageID(id), + mStorageType(0), + mFileSystemType(0), + mAccessCapability(0), + mMaxCapacity(0), + mFreeSpaceBytes(0), + mFreeSpaceObjects(0), + mStorageDescription(NULL), + mVolumeIdentifier(NULL) +{ +} + +MtpStorageInfo::~MtpStorageInfo() { + if (mStorageDescription) + free(mStorageDescription); + if (mVolumeIdentifier) + free(mVolumeIdentifier); +} + +void MtpStorageInfo::read(MtpDataPacket& packet) { + MtpStringBuffer string; + + // read the device info + mStorageType = packet.getUInt16(); + mFileSystemType = packet.getUInt16(); + mAccessCapability = packet.getUInt16(); + mMaxCapacity = packet.getUInt64(); + mFreeSpaceBytes = packet.getUInt64(); + mFreeSpaceObjects = packet.getUInt32(); + + packet.getString(string); + mStorageDescription = strdup((const char *)string); + packet.getString(string); + mVolumeIdentifier = strdup((const char *)string); +} + +void MtpStorageInfo::print() { + LOGD("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n", + mStorageID, mStorageType, mFileSystemType, mAccessCapability); + LOGD("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n", + mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects); + LOGD("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n", + mStorageDescription, mVolumeIdentifier); +} + +} // namespace android diff --git a/media/mtp/MtpStorageInfo.h b/media/mtp/MtpStorageInfo.h new file mode 100644 index 0000000..2cb626e --- /dev/null +++ b/media/mtp/MtpStorageInfo.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _MTP_STORAGE_INFO_H +#define _MTP_STORAGE_INFO_H + +#include "MtpTypes.h" + +namespace android { + +class MtpDataPacket; + +class MtpStorageInfo { +public: + MtpStorageID mStorageID; + uint16_t mStorageType; + uint16_t mFileSystemType; + uint16_t mAccessCapability; + uint64_t mMaxCapacity; + uint64_t mFreeSpaceBytes; + uint32_t mFreeSpaceObjects; + char* mStorageDescription; + char* mVolumeIdentifier; + +public: + MtpStorageInfo(MtpStorageID id); + virtual ~MtpStorageInfo(); + + void read(MtpDataPacket& packet); + + void print(); +}; + +}; // namespace android + +#endif // _MTP_STORAGE_INFO_H diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp new file mode 100644 index 0000000..fe8cf04 --- /dev/null +++ b/media/mtp/MtpStringBuffer.cpp @@ -0,0 +1,171 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpStringBuffer" + +#include <string.h> + +#include "MtpDataPacket.h" +#include "MtpStringBuffer.h" + +namespace android { + +MtpStringBuffer::MtpStringBuffer() + : mCharCount(0), + mByteCount(1) +{ + mBuffer[0] = 0; +} + +MtpStringBuffer::MtpStringBuffer(const char* src) + : mCharCount(0), + mByteCount(1) +{ + set(src); +} + +MtpStringBuffer::MtpStringBuffer(const uint16_t* src) + : mCharCount(0), + mByteCount(1) +{ + set(src); +} + +MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src) + : mCharCount(src.mCharCount), + mByteCount(src.mByteCount) +{ + memcpy(mBuffer, src.mBuffer, mByteCount); +} + + +MtpStringBuffer::~MtpStringBuffer() { +} + +void MtpStringBuffer::set(const char* src) { + int length = strlen(src); + if (length >= sizeof(mBuffer)) + length = sizeof(mBuffer) - 1; + memcpy(mBuffer, src, length); + + // count the characters + int count = 0; + char ch; + while ((ch = *src++) != 0) { + if ((ch & 0x80) == 0) { + // single byte character + } else if ((ch & 0xE0) == 0xC0) { + // two byte character + if (! *src++) { + // last character was truncated, so ignore last byte + length--; + break; + } + } else if ((ch & 0xF0) == 0xE0) { + // 3 byte char + if (! *src++) { + // last character was truncated, so ignore last byte + length--; + break; + } + if (! *src++) { + // last character was truncated, so ignore last two bytes + length -= 2; + break; + } + } + count++; + } + + mByteCount = length + 1; + mBuffer[length] = 0; + mCharCount = count; +} + +void MtpStringBuffer::set(const uint16_t* src) { + int count = 0; + uint16_t ch; + uint8_t* dest = mBuffer; + + while ((ch = *src++) != 0 && count < 255) { + if (ch >= 0x0800) { + *dest++ = (uint8_t)(0xE0 | (ch >> 12)); + *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F)); + *dest++ = (uint8_t)(0x80 | (ch & 0x3F)); + } else if (ch >= 0x80) { + *dest++ = (uint8_t)(0xC0 | (ch >> 6)); + *dest++ = (uint8_t)(0x80 | (ch & 0x3F)); + } else { + *dest++ = ch; + } + count++; + } + *dest++ = 0; + mCharCount = count; + mByteCount = dest - mBuffer; +} + +void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) { + int count = packet->getUInt8(); + uint8_t* dest = mBuffer; + for (int i = 0; i < count; i++) { + uint16_t ch = packet->getUInt16(); + if (ch >= 0x0800) { + *dest++ = (uint8_t)(0xE0 | (ch >> 12)); + *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F)); + *dest++ = (uint8_t)(0x80 | (ch & 0x3F)); + } else if (ch >= 0x80) { + *dest++ = (uint8_t)(0xC0 | (ch >> 6)); + *dest++ = (uint8_t)(0x80 | (ch & 0x3F)); + } else { + *dest++ = ch; + } + } + *dest++ = 0; + mCharCount = count; + mByteCount = dest - mBuffer; +} + +void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const { + int count = mCharCount; + const uint8_t* src = mBuffer; + packet->putUInt8(count > 0 ? count + 1 : 0); + + // expand utf8 to 16 bit chars + for (int i = 0; i < count; i++) { + uint16_t ch; + uint16_t ch1 = *src++; + if ((ch1 & 0x80) == 0) { + // single byte character + ch = ch1; + } else if ((ch1 & 0xE0) == 0xC0) { + // two byte character + uint16_t ch2 = *src++; + ch = ((ch1 & 0x1F) << 6) | (ch2 & 0x3F); + } else { + // three byte character + uint16_t ch2 = *src++; + uint16_t ch3 = *src++; + ch = ((ch1 & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F); + } + packet->putUInt16(ch); + } + // only terminate with zero if string is not empty + if (count > 0) + packet->putUInt16(0); +} + +} // namespace android diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h new file mode 100644 index 0000000..cbc8307 --- /dev/null +++ b/media/mtp/MtpStringBuffer.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef _MTP_STRING_BUFFER_H +#define _MTP_STRING_BUFFER_H + +#include <stdint.h> + +namespace android { + +class MtpDataPacket; + +// Represents a utf8 string, with a maximum of 255 characters +class MtpStringBuffer { + +private: + // mBuffer contains string in UTF8 format + // maximum 3 bytes/character, with 1 extra for zero termination + uint8_t mBuffer[255 * 3 + 1]; + int mCharCount; + int mByteCount; + +public: + MtpStringBuffer(); + MtpStringBuffer(const char* src); + MtpStringBuffer(const uint16_t* src); + MtpStringBuffer(const MtpStringBuffer& src); + virtual ~MtpStringBuffer(); + + void set(const char* src); + void set(const uint16_t* src); + + void readFromPacket(MtpDataPacket* packet); + void writeToPacket(MtpDataPacket* packet) const; + + inline int getCharCount() const { return mCharCount; } + inline int getByteCount() const { return mByteCount; } + + inline operator const char*() const { return (const char *)mBuffer; } +}; + +}; // namespace android + +#endif // _MTP_STRING_BUFFER_H diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h new file mode 100644 index 0000000..720c854 --- /dev/null +++ b/media/mtp/MtpTypes.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef _MTP_TYPES_H +#define _MTP_TYPES_H + +#include <stdint.h> +#include "utils/String8.h" +#include "utils/Vector.h" + +namespace android { + +typedef int32_t int128_t[4]; +typedef uint32_t uint128_t[4]; + +typedef uint16_t MtpOperationCode; +typedef uint16_t MtpResponseCode; +typedef uint16_t MtpEventCode; +typedef uint32_t MtpSessionID; +typedef uint32_t MtpStorageID; +typedef uint32_t MtpTransactionID; +typedef uint16_t MtpPropertyCode; +typedef uint16_t MtpDataType; +typedef uint16_t MtpObjectFormat; +typedef MtpPropertyCode MtpDeviceProperty; +typedef MtpPropertyCode MtpObjectProperty; + +// object handles are unique across all storage but only within a single session. +// object handles cannot be reused after an object is deleted. +// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes. +typedef uint32_t MtpObjectHandle; + +// Special values +#define MTP_PARENT_ROOT 0xFFFFFFFF // parent is root of the storage +#define kInvalidObjectHandle 0xFFFFFFFF + +class MtpStorage; +class MtpDevice; +class MtpProperty; + +typedef Vector<MtpStorage *> MtpStorageList; +typedef Vector<MtpDevice*> MtpDeviceList; +typedef Vector<MtpProperty*> MtpPropertyList; + +typedef Vector<uint8_t> UInt8List; +typedef Vector<uint16_t> UInt16List; +typedef Vector<uint32_t> UInt32List; +typedef Vector<uint64_t> UInt64List; +typedef Vector<int8_t> Int8List; +typedef Vector<int16_t> Int16List; +typedef Vector<int32_t> Int32List; +typedef Vector<int64_t> Int64List; + +typedef UInt16List MtpObjectPropertyList; +typedef UInt16List MtpDevicePropertyList; +typedef UInt16List MtpObjectFormatList; +typedef UInt32List MtpObjectHandleList; +typedef UInt16List MtpObjectPropertyList; +typedef UInt32List MtpStorageIDList; + +typedef String8 MtpString; + +}; // namespace android + +#endif // _MTP_TYPES_H diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp new file mode 100644 index 0000000..ab01ef5 --- /dev/null +++ b/media/mtp/MtpUtils.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#define LOG_TAG "MtpUtils" + +#include <stdio.h> +#include <time.h> + +#include <cutils/tztime.h> +#include "MtpUtils.h" + +namespace android { + +/* +DateTime strings follow a compatible subset of the definition found in ISO 8601, and +take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this +representation, YYYY shall be replaced by the year, MM replaced by the month (01-12), +DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date, +hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the +second (00-59). The ".s" is optional, and represents tenths of a second. +*/ + +bool parseDateTime(const char* dateTime, time_t& outSeconds) { + int year, month, day, hour, minute, second; + struct tm tm; + + if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d", + &year, &month, &day, &hour, &minute, &second) != 6) + return false; + const char* tail = dateTime + 15; + // skip optional tenth of second + if (tail[0] == '.' && tail[1]) + tail += 2; + //FIXME - support +/-hhmm + bool useUTC = (tail[0] == 'Z'); + + // hack to compute timezone + time_t dummy; + localtime_r(&dummy, &tm); + + tm.tm_sec = second; + tm.tm_min = minute; + tm.tm_hour = hour; + tm.tm_mday = day; + tm.tm_mon = month; + tm.tm_year = year - 1900; + tm.tm_wday = 0; + tm.tm_isdst = -1; + if (useUTC) + outSeconds = mktime(&tm); + else + outSeconds = mktime_tz(&tm, tm.tm_zone); + + return true; +} + +void formatDateTime(time_t seconds, char* buffer, int bufferLength) { + struct tm tm; + + localtime_r(&seconds, &tm); + snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d", + tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +} // namespace android diff --git a/media/mtp/MtpUtils.h b/media/mtp/MtpUtils.h new file mode 100644 index 0000000..61f9055 --- /dev/null +++ b/media/mtp/MtpUtils.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef _MTP_UTILS_H +#define _MTP_UTILS_H + +#include <stdint.h> + +namespace android { + +bool parseDateTime(const char* dateTime, time_t& outSeconds); +void formatDateTime(time_t seconds, char* buffer, int bufferLength); + +}; // namespace android + +#endif // _MTP_UTILS_H diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h new file mode 100644 index 0000000..8bc2e22 --- /dev/null +++ b/media/mtp/mtp.h @@ -0,0 +1,479 @@ +/* + * 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. + */ + +#ifndef _MTP_H +#define _MTP_H + +#include <stdint.h> +#include <stdlib.h> + +#define MTP_STANDARD_VERSION 100 + +// Container Types +#define MTP_CONTAINER_TYPE_UNDEFINED 0 +#define MTP_CONTAINER_TYPE_COMMAND 1 +#define MTP_CONTAINER_TYPE_DATA 2 +#define MTP_CONTAINER_TYPE_RESPONSE 3 +#define MTP_CONTAINER_TYPE_EVENT 4 + +// Container Offsets +#define MTP_CONTAINER_LENGTH_OFFSET 0 +#define MTP_CONTAINER_TYPE_OFFSET 4 +#define MTP_CONTAINER_CODE_OFFSET 6 +#define MTP_CONTAINER_TRANSACTION_ID_OFFSET 8 +#define MTP_CONTAINER_PARAMETER_OFFSET 12 +#define MTP_CONTAINER_HEADER_SIZE 12 + +// MTP Data Types +#define MTP_TYPE_UNDEFINED 0x0000 // Undefined +#define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer +#define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer +#define MTP_TYPE_INT16 0x0003 // Signed 16-bit integer +#define MTP_TYPE_UINT16 0x0004 // Unsigned 16-bit integer +#define MTP_TYPE_INT32 0x0005 // Signed 32-bit integer +#define MTP_TYPE_UINT32 0x0006 // Unsigned 32-bit integer +#define MTP_TYPE_INT64 0x0007 // Signed 64-bit integer +#define MTP_TYPE_UINT64 0x0008 // Unsigned 64-bit integer +#define MTP_TYPE_INT128 0x0009 // Signed 128-bit integer +#define MTP_TYPE_UINT128 0x000A // Unsigned 128-bit integer +#define MTP_TYPE_AINT8 0x4001 // Array of signed 8-bit integers +#define MTP_TYPE_AUINT8 0x4002 // Array of unsigned 8-bit integers +#define MTP_TYPE_AINT16 0x4003 // Array of signed 16-bit integers +#define MTP_TYPE_AUINT16 0x4004 // Array of unsigned 16-bit integers +#define MTP_TYPE_AINT32 0x4005 // Array of signed 32-bit integers +#define MTP_TYPE_AUINT32 0x4006 // Array of unsigned 32-bit integers +#define MTP_TYPE_AINT64 0x4007 // Array of signed 64-bit integers +#define MTP_TYPE_AUINT64 0x4008 // Array of unsigned 64-bit integers +#define MTP_TYPE_AINT128 0x4009 // Array of signed 128-bit integers +#define MTP_TYPE_AUINT128 0x400A // Array of unsigned 128-bit integers +#define MTP_TYPE_STR 0xFFFF // Variable-length Unicode string + +// MTP Format Codes +#define MTP_FORMAT_UNDEFINED 0x3000 // Undefined object +#define MTP_FORMAT_ASSOCIATION 0x3001 // Association (for example, a folder) +#define MTP_FORMAT_SCRIPT 0x3002 // Device model-specific script +#define MTP_FORMAT_EXECUTABLE 0x3003 // Device model-specific binary executable +#define MTP_FORMAT_TEXT 0x3004 // Text file +#define MTP_FORMAT_HTML 0x3005 // Hypertext Markup Language file (text) +#define MTP_FORMAT_DPOF 0x3006 // Digital Print Order Format file (text) +#define MTP_FORMAT_AIFF 0x3007 // Audio clip +#define MTP_FORMAT_WAV 0x3008 // Audio clip +#define MTP_FORMAT_MP3 0x3009 // Audio clip +#define MTP_FORMAT_AVI 0x300A // Video clip +#define MTP_FORMAT_MPEG 0x300B // Video clip +#define MTP_FORMAT_ASF 0x300C // Microsoft Advanced Streaming Format (video) +#define MTP_FORMAT_DEFINED 0x3800 // Unknown image object +#define MTP_FORMAT_EXIF_JPEG 0x3801 // Exchangeable File Format, JEIDA standard +#define MTP_FORMAT_TIFF_EP 0x3802 // Tag Image File Format for Electronic Photography +#define MTP_FORMAT_FLASHPIX 0x3803 // Structured Storage Image Format +#define MTP_FORMAT_BMP 0x3804 // Microsoft Windows Bitmap file +#define MTP_FORMAT_CIFF 0x3805 // Canon Camera Image File Format +#define MTP_FORMAT_GIF 0x3807 // Graphics Interchange Format +#define MTP_FORMAT_JFIF 0x3808 // JPEG File Interchange Format +#define MTP_FORMAT_CD 0x3809 // PhotoCD Image Pac +#define MTP_FORMAT_PICT 0x380A // Quickdraw Image Format +#define MTP_FORMAT_PNG 0x380B // Portable Network Graphics +#define MTP_FORMAT_TIFF 0x380D // Tag Image File Format +#define MTP_FORMAT_TIFF_IT 0x380E // Tag Image File Format for Information Technology (graphic arts) +#define MTP_FORMAT_JP2 0x380F // JPEG2000 Baseline File Format +#define MTP_FORMAT_JPX 0x3810 // JPEG2000 Extended File Format +#define MTP_FORMAT_UNDEFINED_FIRMWARE 0xB802 +#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881 +#define MTP_FORMAT_UNDEFINED_AUDIO 0xB900 +#define MTP_FORMAT_WMA 0xB901 +#define MTP_FORMAT_OGG 0xB902 +#define MTP_FORMAT_AAC 0xB903 +#define MTP_FORMAT_AUDIBLE 0xB904 +#define MTP_FORMAT_FLAC 0xB906 +#define MTP_FORMAT_UNDEFINED_VIDEO 0xB980 +#define MTP_FORMAT_WMV 0xB981 +#define MTP_FORMAT_MP4_CONTAINER 0xB982 // ISO 14496-1 +#define MTP_FORMAT_MP2 0xB983 +#define MTP_FORMAT_3GP_CONTAINER 0xB984 // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d). +#define MTP_FORMAT_UNDEFINED_COLLECTION 0xBA00 +#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01 +#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02 +#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03 +#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04 +#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05 +#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP 0xBA06 +#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER 0xBA07 +#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION 0xBA08 +#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09 +#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A +#define MTP_FORMAT_ABSTRACT_MEDIACAST 0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content +#define MTP_FORMAT_WPL_PLAYLIST 0xBA10 +#define MTP_FORMAT_M3U_PLAYLIST 0xBA11 +#define MTP_FORMAT_MPL_PLAYLIST 0xBA12 +#define MTP_FORMAT_ASX_PLAYLIST 0xBA13 +#define MTP_FORMAT_PLS_PLAYLIST 0xBA14 +#define MTP_FORMAT_UNDEFINED_DOCUMENT 0xBA80 +#define MTP_FORMAT_ABSTRACT_DOCUMENT 0xBA81 +#define MTP_FORMAT_XML_DOCUMENT 0xBA82 +#define MTP_FORMAT_MS_WORD_DOCUMENT 0xBA83 +#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT 0xBA84 +#define MTP_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85 +#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86 +#define MTP_FORMAT_UNDEFINED_MESSAGE 0xBB00 +#define MTP_FORMAT_ABSTRACT_MESSSAGE 0xBB01 +#define MTP_FORMAT_UNDEFINED_CONTACT 0xBB80 +#define MTP_FORMAT_ABSTRACT_CONTACT 0xBB81 +#define MTP_FORMAT_VCARD_2 0xBB82 + +// MTP Object Property Codes +#define MTP_PROPERTY_STORAGE_ID 0xDC01 +#define MTP_PROPERTY_OBJECT_FORMAT 0xDC02 +#define MTP_PROPERTY_PROTECTION_STATUS 0xDC03 +#define MTP_PROPERTY_OBJECT_SIZE 0xDC04 +#define MTP_PROPERTY_ASSOCIATION_TYPE 0xDC05 +#define MTP_PROPERTY_ASSOCIATION_DESC 0xDC06 +#define MTP_PROPERTY_OBJECT_FILE_NAME 0xDC07 +#define MTP_PROPERTY_DATE_CREATED 0xDC08 +#define MTP_PROPERTY_DATE_MODIFIED 0xDC09 +#define MTP_PROPERTY_KEYWORDS 0xDC0A +#define MTP_PROPERTY_PARENT_OBJECT 0xDC0B +#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS 0xDC0C +#define MTP_PROPERTY_HIDDEN 0xDC0D +#define MTP_PROPERTY_SYSTEM_OBJECT 0xDC0E +#define MTP_PROPERTY_PERSISTENT_UID 0xDC41 +#define MTP_PROPERTY_SYNC_ID 0xDC42 +#define MTP_PROPERTY_PROPERTY_BAG 0xDC43 +#define MTP_PROPERTY_NAME 0xDC44 +#define MTP_PROPERTY_CREATED_BY 0xDC45 +#define MTP_PROPERTY_ARTIST 0xDC46 +#define MTP_PROPERTY_DATE_AUTHORED 0xDC47 +#define MTP_PROPERTY_DESCRIPTION 0xDC48 +#define MTP_PROPERTY_URL_REFERENCE 0xDC49 +#define MTP_PROPERTY_LANGUAGE_LOCALE 0xDC4A +#define MTP_PROPERTY_COPYRIGHT_INFORMATION 0xDC4B +#define MTP_PROPERTY_SOURCE 0xDC4C +#define MTP_PROPERTY_ORIGIN_LOCATION 0xDC4D +#define MTP_PROPERTY_DATE_ADDED 0xDC4E +#define MTP_PROPERTY_NON_CONSUMABLE 0xDC4F +#define MTP_PROPERTY_CORRUPT_UNPLAYABLE 0xDC50 +#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER 0xDC51 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT 0xDC81 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE 0xDC82 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT 0xDC83 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH 0xDC84 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION 0xDC85 +#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA 0xDC86 +#define MTP_PROPERTY_WIDTH 0xDC87 +#define MTP_PROPERTY_HEIGHT 0xDC88 +#define MTP_PROPERTY_DURATION 0xDC89 +#define MTP_PROPERTY_RATING 0xDC8A +#define MTP_PROPERTY_TRACK 0xDC8B +#define MTP_PROPERTY_GENRE 0xDC8C +#define MTP_PROPERTY_CREDITS 0xDC8D +#define MTP_PROPERTY_LYRICS 0xDC8E +#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID 0xDC8F +#define MTP_PROPERTY_PRODUCED_BY 0xDC90 +#define MTP_PROPERTY_USE_COUNT 0xDC91 +#define MTP_PROPERTY_SKIP_COUNT 0xDC92 +#define MTP_PROPERTY_LAST_ACCESSED 0xDC93 +#define MTP_PROPERTY_PARENTAL_RATING 0xDC94 +#define MTP_PROPERTY_META_GENRE 0xDC95 +#define MTP_PROPERTY_COMPOSER 0xDC96 +#define MTP_PROPERTY_EFFECTIVE_RATING 0xDC97 +#define MTP_PROPERTY_SUBTITLE 0xDC98 +#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE 0xDC99 +#define MTP_PROPERTY_ALBUM_NAME 0xDC9A +#define MTP_PROPERTY_ALBUM_ARTIST 0xDC9B +#define MTP_PROPERTY_MOOD 0xDC9C +#define MTP_PROPERTY_DRM_STATUS 0xDC9D +#define MTP_PROPERTY_SUB_DESCRIPTION 0xDC9E +#define MTP_PROPERTY_IS_CROPPED 0xDCD1 +#define MTP_PROPERTY_IS_COLOUR_CORRECTED 0xDCD2 +#define MTP_PROPERTY_IMAGE_BIT_DEPTH 0xDCD3 +#define MTP_PROPERTY_F_NUMBER 0xDCD4 +#define MTP_PROPERTY_EXPOSURE_TIME 0xDCD5 +#define MTP_PROPERTY_EXPOSURE_INDEX 0xDCD6 +#define MTP_PROPERTY_TOTAL_BITRATE 0xDE91 +#define MTP_PROPERTY_BITRATE_TYPE 0xDE92 +#define MTP_PROPERTY_SAMPLE_RATE 0xDE93 +#define MTP_PROPERTY_NUMBER_OF_CHANNELS 0xDE94 +#define MTP_PROPERTY_AUDIO_BIT_DEPTH 0xDE95 +#define MTP_PROPERTY_SCAN_TYPE 0xDE97 +#define MTP_PROPERTY_AUDIO_WAVE_CODEC 0xDE99 +#define MTP_PROPERTY_AUDIO_BITRATE 0xDE9A +#define MTP_PROPERTY_VIDEO_FOURCC_CODEC 0xDE9B +#define MTP_PROPERTY_VIDEO_BITRATE 0xDE9C +#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS 0xDE9D +#define MTP_PROPERTY_KEYFRAME_DISTANCE 0xDE9E +#define MTP_PROPERTY_BUFFER_SIZE 0xDE9F +#define MTP_PROPERTY_ENCODING_QUALITY 0xDEA0 +#define MTP_PROPERTY_ENCODING_PROFILE 0xDEA1 +#define MTP_PROPERTY_DISPLAY_NAME 0xDCE0 +#define MTP_PROPERTY_BODY_TEXT 0xDCE1 +#define MTP_PROPERTY_SUBJECT 0xDCE2 +#define MTP_PROPERTY_PRIORITY 0xDCE3 +#define MTP_PROPERTY_GIVEN_NAME 0xDD00 +#define MTP_PROPERTY_MIDDLE_NAMES 0xDD01 +#define MTP_PROPERTY_FAMILY_NAME 0xDD02 +#define MTP_PROPERTY_PREFIX 0xDD03 +#define MTP_PROPERTY_SUFFIX 0xDD04 +#define MTP_PROPERTY_PHONETIC_GIVEN_NAME 0xDD05 +#define MTP_PROPERTY_PHONETIC_FAMILY_NAME 0xDD06 +#define MTP_PROPERTY_EMAIL_PRIMARY 0xDD07 +#define MTP_PROPERTY_EMAIL_PERSONAL_1 0xDD08 +#define MTP_PROPERTY_EMAIL_PERSONAL_2 0xDD09 +#define MTP_PROPERTY_EMAIL_BUSINESS_1 0xDD0A +#define MTP_PROPERTY_EMAIL_BUSINESS_2 0xDD0B +#define MTP_PROPERTY_EMAIL_OTHERS 0xDD0C +#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY 0xDD0D +#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL 0xDD0E +#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2 0xDD0F +#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS 0xDD10 +#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2 0xDD11 +#define MTP_PROPERTY_PHONE_NUMBER_MOBILE 0xDD12 +#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2 0xDD13 +#define MTP_PROPERTY_FAX_NUMBER_PRIMARY 0xDD14 +#define MTP_PROPERTY_FAX_NUMBER_PERSONAL 0xDD15 +#define MTP_PROPERTY_FAX_NUMBER_BUSINESS 0xDD16 +#define MTP_PROPERTY_PAGER_NUMBER 0xDD17 +#define MTP_PROPERTY_PHONE_NUMBER_OTHERS 0xDD18 +#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS 0xDD19 +#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS 0xDD1A +#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS 0xDD1B +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS 0xDD1C +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2 0xDD1D +#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3 0xDD1E +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL 0xDD1F +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 0xDD20 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 0xDD21 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY 0xDD22 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION 0xDD23 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE 0xDD24 +#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY 0xDD25 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL 0xDD26 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 0xDD27 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 0xDD28 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY 0xDD29 +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION 0xDD2A +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE 0xDD2B +#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY 0xDD2C +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL 0xDD2D +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 0xDD2E +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 0xDD2F +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY 0xDD30 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION 0xDD31 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE 0xDD32 +#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY 0xDD33 +#define MTP_PROPERTY_ORGANIZATION_NAME 0xDD34 +#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME 0xDD35 +#define MTP_PROPERTY_ROLE 0xDD36 +#define MTP_PROPERTY_BIRTHDATE 0xDD37 +#define MTP_PROPERTY_MESSAGE_TO 0xDD40 +#define MTP_PROPERTY_MESSAGE_CC 0xDD41 +#define MTP_PROPERTY_MESSAGE_BCC 0xDD42 +#define MTP_PROPERTY_MESSAGE_READ 0xDD43 +#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME 0xDD44 +#define MTP_PROPERTY_MESSAGE_SENDER 0xDD45 +#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME 0xDD50 +#define MTP_PROPERTY_ACTIVITY_END_TIME 0xDD51 +#define MTP_PROPERTY_ACTIVITY_LOCATION 0xDD52 +#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES 0xDD54 +#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES 0xDD55 +#define MTP_PROPERTY_ACTIVITY_RESOURCES 0xDD56 +#define MTP_PROPERTY_ACTIVITY_ACCEPTED 0xDD57 +#define MTP_PROPERTY_ACTIVITY_TENTATIVE 0xDD58 +#define MTP_PROPERTY_ACTIVITY_DECLINED 0xDD59 +#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME 0xDD5A +#define MTP_PROPERTY_ACTIVITY_OWNER 0xDD5B +#define MTP_PROPERTY_ACTIVITY_STATUS 0xDD5C +#define MTP_PROPERTY_OWNER 0xDD5D +#define MTP_PROPERTY_EDITOR 0xDD5E +#define MTP_PROPERTY_WEBMASTER 0xDD5F +#define MTP_PROPERTY_URL_SOURCE 0xDD60 +#define MTP_PROPERTY_URL_DESTINATION 0xDD61 +#define MTP_PROPERTY_TIME_BOOKMARK 0xDD62 +#define MTP_PROPERTY_OBJECT_BOOKMARK 0xDD63 +#define MTP_PROPERTY_BYTE_BOOKMARK 0xDD64 +#define MTP_PROPERTY_LAST_BUILD_DATE 0xDD70 +#define MTP_PROPERTY_TIME_TO_LIVE 0xDD71 +#define MTP_PROPERTY_MEDIA_GUID 0xDD72 + +// MTP Device Property Codes +#define MTP_DEVICE_PROPERTY_UNDEFINED 0x5000 +#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL 0x5001 +#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE 0x5002 +#define MTP_DEVICE_PROPERTY_IMAGE_SIZE 0x5003 +#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING 0x5004 +#define MTP_DEVICE_PROPERTY_WHITE_BALANCE 0x5005 +#define MTP_DEVICE_PROPERTY_RGB_GAIN 0x5006 +#define MTP_DEVICE_PROPERTY_F_NUMBER 0x5007 +#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH 0x5008 +#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE 0x5009 +#define MTP_DEVICE_PROPERTY_FOCUS_MODE 0x500A +#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE 0x500B +#define MTP_DEVICE_PROPERTY_FLASH_MODE 0x500C +#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME 0x500D +#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE 0x500E +#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX 0x500F +#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION 0x5010 +#define MTP_DEVICE_PROPERTY_DATETIME 0x5011 +#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY 0x5012 +#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE 0x5013 +#define MTP_DEVICE_PROPERTY_CONTRAST 0x5014 +#define MTP_DEVICE_PROPERTY_SHARPNESS 0x5015 +#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM 0x5016 +#define MTP_DEVICE_PROPERTY_EFFECT_MODE 0x5017 +#define MTP_DEVICE_PROPERTY_BURST_NUMBER 0x5018 +#define MTP_DEVICE_PROPERTY_BURST_INTERVAL 0x5019 +#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER 0x501A +#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL 0x501B +#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE 0x501C +#define MTP_DEVICE_PROPERTY_UPLOAD_URL 0x501D +#define MTP_DEVICE_PROPERTY_ARTIST 0x501E +#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO 0x501F +#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER 0xD401 +#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME 0xD402 +#define MTP_DEVICE_PROPERTY_VOLUME 0xD403 +#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED 0xD404 +#define MTP_DEVICE_PROPERTY_DEVICE_ICON 0xD405 +#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE 0xD410 +#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT 0xD411 +#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX 0xD412 +#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO 0xD406 +#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE 0xD407 + +// MTP Operation Codes +#define MTP_OPERATION_GET_DEVICE_INFO 0x1001 +#define MTP_OPERATION_OPEN_SESSION 0x1002 +#define MTP_OPERATION_CLOSE_SESSION 0x1003 +#define MTP_OPERATION_GET_STORAGE_IDS 0x1004 +#define MTP_OPERATION_GET_STORAGE_INFO 0x1005 +#define MTP_OPERATION_GET_NUM_OBJECTS 0x1006 +#define MTP_OPERATION_GET_OBJECT_HANDLES 0x1007 +#define MTP_OPERATION_GET_OBJECT_INFO 0x1008 +#define MTP_OPERATION_GET_OBJECT 0x1009 +#define MTP_OPERATION_GET_THUMB 0x100A +#define MTP_OPERATION_DELETE_OBJECT 0x100B +#define MTP_OPERATION_SEND_OBJECT_INFO 0x100C +#define MTP_OPERATION_SEND_OBJECT 0x100D +#define MTP_OPERATION_INITIATE_CAPTURE 0x100E +#define MTP_OPERATION_FORMAT_STORE 0x100F +#define MTP_OPERATION_RESET_DEVICE 0x1010 +#define MTP_OPERATION_SELF_TEST 0x1011 +#define MTP_OPERATION_SET_OBJECT_PROTECTION 0x1012 +#define MTP_OPERATION_POWER_DOWN 0x1013 +#define MTP_OPERATION_GET_DEVICE_PROP_DESC 0x1014 +#define MTP_OPERATION_GET_DEVICE_PROP_VALUE 0x1015 +#define MTP_OPERATION_SET_DEVICE_PROP_VALUE 0x1016 +#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE 0x1017 +#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE 0x1018 +#define MTP_OPERATION_MOVE_OBJECT 0x1019 +#define MTP_OPERATION_COPY_OBJECT 0x101A +#define MTP_OPERATION_GET_PARTIAL_OBJECT 0x101B +#define MTP_OPERATION_INITIATE_OPEN_CAPTURE 0x101C +#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED 0x9801 +#define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802 +#define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803 +#define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804 +#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805 +#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806 +#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807 +#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808 +#define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810 +#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 +#define MTP_OPERATION_SKIP 0x9820 + +// MTP Response Codes +#define MTP_RESPONSE_UNDEFINED 0x2000 +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_GENERAL_ERROR 0x2002 +#define MTP_RESPONSE_SESSION_NOT_OPEN 0x2003 +#define MTP_RESPONSE_INVALID_TRANSACTION_ID 0x2004 +#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED 0x2005 +#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED 0x2006 +#define MTP_RESPONSE_INCOMPLETE_TRANSFER 0x2007 +#define MTP_RESPONSE_INVALID_STORAGE_ID 0x2008 +#define MTP_RESPONSE_INVALID_OBJECT_HANDLE 0x2009 +#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED 0x200A +#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE 0x200B +#define MTP_RESPONSE_STORAGE_FULL 0x200C +#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED 0x200D +#define MTP_RESPONSE_STORE_READ_ONLY 0x200E +#define MTP_RESPONSE_ACCESS_DENIED 0x200F +#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT 0x2010 +#define MTP_RESPONSE_SELF_TEST_FAILED 0x2011 +#define MTP_RESPONSE_PARTIAL_DELETION 0x2012 +#define MTP_RESPONSE_STORE_NOT_AVAILABLE 0x2013 +#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED 0x2014 +#define MTP_RESPONSE_NO_VALID_OBJECT_INFO 0x2015 +#define MTP_RESPONSE_INVALID_CODE_FORMAT 0x2016 +#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE 0x2017 +#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED 0x2018 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define MTP_RESPONSE_INVALID_PARENT_OBJECT 0x201A +#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT 0x201B +#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE 0x201C +#define MTP_RESPONSE_INVALID_PARAMETER 0x201D +#define MTP_RESPONSE_SESSION_ALREADY_OPEN 0x201E +#define MTP_RESPONSE_TRANSACTION_CANCELLED 0x201F +#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED 0x2020 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE 0xA801 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT 0xA802 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE 0xA803 +#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE 0xA804 +#define MTP_RESPONSE_GROUP_NOT_SUPPORTED 0xA805 +#define MTP_RESPONSE_INVALID_DATASET 0xA806 +#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807 +#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808 +#define MTP_RESPONSE_OBJECT_TOO_LARGE 0xA809 +#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED 0xA80A + +// MTP Event Codes +#define MTP_EVENT_UNDEFINED 0x4000 +#define MTP_EVENT_CANCEL_TRANSACTION 0x4001 +#define MTP_EVENT_OBJECT_ADDED 0x4002 +#define MTP_EVENT_OBJECT_REMOVED 0x4003 +#define MTP_EVENT_STORE_ADDED 0x4004 +#define MTP_EVENT_STORE_REMOVED 0x4005 +#define MTP_EVENT_DEVICE_PROP_CHANGED 0x4006 +#define MTP_EVENT_OBJECT_INFO_CHANGED 0x4007 +#define MTP_EVENT_DEVICE_INFO_CHANGED 0x4008 +#define MTP_EVENT_REQUEST_OBJECT_TRANSFER 0x4009 +#define MTP_EVENT_STORE_FULL 0x400A +#define MTP_EVENT_DEVICE_RESET 0x400B +#define MTP_EVENT_STORAGE_INFO_CHANGED 0x400C +#define MTP_EVENT_CAPTURE_COMPLETE 0x400D +#define MTP_EVENT_UNREPORTED_STATUS 0x400E +#define MTP_EVENT_OBJECT_PROP_CHANGED 0xC801 +#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED 0xC802 +#define MTP_EVENT_OBJECT_REFERENCES_CHANGED 0xC803 + +// Storage Type +#define MTP_STORAGE_FIXED_ROM 0x0001 +#define MTP_STORAGE_REMOVABLE_ROM 0x0002 +#define MTP_STORAGE_FIXED_RAM 0x0003 +#define MTP_STORAGE_REMOVABLE_RAM 0x0004 + +// Storage File System +#define MTP_STORAGE_FILESYSTEM_FLAT 0x0001 +#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL 0x0002 +#define MTP_STORAGE_FILESYSTEM_DCF 0x0003 + +// Storage Access Capability +#define MTP_STORAGE_READ_WRITE 0x0000 +#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0001 +#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0002 + +// Association Type +#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000 +#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER 0x0001 + +#endif // _MTP_H |