blob: 887f0ecbb7391f262a0246fe42ec3a60c6ae880f [file] [log] [blame]
Mike Lockwood5ed68d22010-05-25 19:08:48 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Mike Lockwooda6c490b2010-06-05 22:45:01 -040017#define LOG_TAG "MtpDevice"
Mike Lockwoodb14e5882010-06-29 18:11:52 -040018
19#include "MtpDebug.h"
20#include "MtpDevice.h"
21#include "MtpDeviceInfo.h"
22#include "MtpObjectInfo.h"
23#include "MtpProperty.h"
24#include "MtpStorageInfo.h"
25#include "MtpStringBuffer.h"
Mike Lockwood0cf89f22010-07-26 20:40:45 -040026#include "MtpUtils.h"
Mike Lockwooda6c490b2010-06-05 22:45:01 -040027
Mike Lockwood5ed68d22010-05-25 19:08:48 -040028#include <stdio.h>
29#include <stdlib.h>
30#include <sys/types.h>
31#include <sys/ioctl.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <errno.h>
Mike Lockwood0cf89f22010-07-26 20:40:45 -040035#include <endian.h>
Mike Lockwood5ed68d22010-05-25 19:08:48 -040036
37#include <usbhost/usbhost.h>
38
Mike Lockwood5ed68d22010-05-25 19:08:48 -040039namespace android {
40
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -050041#if 0
Mike Lockwood23f1b332010-12-30 15:38:45 -050042static bool isMtpDevice(uint16_t vendor, uint16_t product) {
43 // Sandisk Sansa Fuze
44 if (vendor == 0x0781 && product == 0x74c2)
45 return true;
46 // Samsung YP-Z5
47 if (vendor == 0x04e8 && product == 0x503c)
48 return true;
49 return false;
50}
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -050051#endif
Mike Lockwood23f1b332010-12-30 15:38:45 -050052
53MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
54 struct usb_device *device = usb_device_new(deviceName, fd);
55 if (!device) {
Steve Block29357bc2012-01-06 19:20:56 +000056 ALOGE("usb_device_new failed for %s", deviceName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050057 return NULL;
58 }
59
60 struct usb_descriptor_header* desc;
61 struct usb_descriptor_iter iter;
62
63 usb_descriptor_iter_init(device, &iter);
64
65 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
66 if (desc->bDescriptorType == USB_DT_INTERFACE) {
67 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
68
69 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
70 interface->bInterfaceSubClass == 1 && // Still Image Capture
71 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
72 {
Kenny Root31c52e72011-02-02 13:43:55 -080073 char* manufacturerName = usb_device_get_manufacturer_name(device);
74 char* productName = usb_device_get_product_name(device);
Steve Blockb8a80522011-12-20 16:23:08 +000075 ALOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
Kenny Root31c52e72011-02-02 13:43:55 -080076 free(manufacturerName);
77 free(productName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050078 } else if (interface->bInterfaceClass == 0xFF &&
79 interface->bInterfaceSubClass == 0xFF &&
80 interface->bInterfaceProtocol == 0) {
81 char* interfaceName = usb_device_get_string(device, interface->iInterface);
Kenny Root31c52e72011-02-02 13:43:55 -080082 if (!interfaceName) {
Mike Lockwood23f1b332010-12-30 15:38:45 -050083 continue;
Kenny Root31c52e72011-02-02 13:43:55 -080084 } else if (strcmp(interfaceName, "MTP")) {
85 free(interfaceName);
86 continue;
87 }
88 free(interfaceName);
89
Mike Lockwood23f1b332010-12-30 15:38:45 -050090 // Looks like an android style MTP device
Kenny Root31c52e72011-02-02 13:43:55 -080091 char* manufacturerName = usb_device_get_manufacturer_name(device);
92 char* productName = usb_device_get_product_name(device);
Steve Blockb8a80522011-12-20 16:23:08 +000093 ALOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
Kenny Root31c52e72011-02-02 13:43:55 -080094 free(manufacturerName);
95 free(productName);
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -050096 }
97#if 0
98 else {
Mike Lockwood23f1b332010-12-30 15:38:45 -050099 // look for special cased devices based on vendor/product ID
100 // we are doing this mainly for testing purposes
101 uint16_t vendor = usb_device_get_vendor_id(device);
102 uint16_t product = usb_device_get_product_id(device);
103 if (!isMtpDevice(vendor, product)) {
104 // not an MTP or PTP device
105 continue;
106 }
107 // request MTP OS string and descriptor
108 // some music players need to see this before entering MTP mode.
109 char buffer[256];
110 memset(buffer, 0, sizeof(buffer));
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800111 int ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500112 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
113 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800114 0, buffer, sizeof(buffer), 0);
115 printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500116 if (ret > 0) {
117 printf("got MTP string %s\n", buffer);
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800118 ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500119 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800120 0, 4, buffer, sizeof(buffer), 0);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500121 printf("OS descriptor got %d\n", ret);
122 } else {
123 printf("no MTP string\n");
124 }
125 }
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -0500126#endif
Mike Lockwood23f1b332010-12-30 15:38:45 -0500127 // if we got here, then we have a likely MTP or PTP device
128
129 // interface should be followed by three endpoints
130 struct usb_endpoint_descriptor *ep;
131 struct usb_endpoint_descriptor *ep_in_desc = NULL;
132 struct usb_endpoint_descriptor *ep_out_desc = NULL;
133 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
Bo Huang8023f3a2013-06-09 08:53:21 +0800134 //USB3 add USB_DT_SS_ENDPOINT_COMP as companion descriptor;
135 struct usb_ss_ep_comp_descriptor *ep_ss_ep_comp_desc = NULL;
Mike Lockwood23f1b332010-12-30 15:38:45 -0500136 for (int i = 0; i < 3; i++) {
137 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
Bo Huang8023f3a2013-06-09 08:53:21 +0800138 if (ep && ep->bDescriptorType == USB_DT_SS_ENDPOINT_COMP) {
139 ALOGD("Descriptor type is USB_DT_SS_ENDPOINT_COMP for USB3 \n");
140 ep_ss_ep_comp_desc = (usb_ss_ep_comp_descriptor*)ep;
141 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
142 }
143
Mike Lockwood23f1b332010-12-30 15:38:45 -0500144 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
Steve Block29357bc2012-01-06 19:20:56 +0000145 ALOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800146 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500147 return NULL;
148 }
Bo Huang8023f3a2013-06-09 08:53:21 +0800149
Mike Lockwood23f1b332010-12-30 15:38:45 -0500150 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
151 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
152 ep_in_desc = ep;
153 else
154 ep_out_desc = ep;
155 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
156 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
157 ep_intr_desc = ep;
158 }
159 }
160 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
Steve Block29357bc2012-01-06 19:20:56 +0000161 ALOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800162 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500163 return NULL;
164 }
165
166 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
Steve Block29357bc2012-01-06 19:20:56 +0000167 ALOGE("usb_device_claim_interface failed errno: %d\n", errno);
Kenny Root31c52e72011-02-02 13:43:55 -0800168 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500169 return NULL;
170 }
171
172 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
173 ep_in_desc, ep_out_desc, ep_intr_desc);
174 mtpDevice->initialize();
175 return mtpDevice;
176 }
177 }
178
179 usb_device_close(device);
Steve Block29357bc2012-01-06 19:20:56 +0000180 ALOGE("device not found");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500181 return NULL;
182}
183
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400184MtpDevice::MtpDevice(struct usb_device* device, int interface,
Mike Lockwood42d0b792011-01-04 14:48:57 -0500185 const struct usb_endpoint_descriptor *ep_in,
186 const struct usb_endpoint_descriptor *ep_out,
187 const struct usb_endpoint_descriptor *ep_intr)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400188 : mDevice(device),
189 mInterface(interface),
Mike Lockwood42d0b792011-01-04 14:48:57 -0500190 mRequestIn1(NULL),
191 mRequestIn2(NULL),
192 mRequestOut(NULL),
193 mRequestIntr(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400194 mDeviceInfo(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400195 mSessionID(0),
Mike Lockwoodf7454622010-12-09 18:34:18 -0800196 mTransactionID(0),
197 mReceivedResponse(false)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400198{
Mike Lockwood42d0b792011-01-04 14:48:57 -0500199 mRequestIn1 = usb_request_new(device, ep_in);
200 mRequestIn2 = usb_request_new(device, ep_in);
201 mRequestOut = usb_request_new(device, ep_out);
202 mRequestIntr = usb_request_new(device, ep_intr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400203}
204
205MtpDevice::~MtpDevice() {
206 close();
Mark Salyzyn3ab368e2014-04-15 14:55:53 -0700207 for (size_t i = 0; i < mDeviceProperties.size(); i++)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400208 delete mDeviceProperties[i];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500209 usb_request_free(mRequestIn1);
210 usb_request_free(mRequestIn2);
211 usb_request_free(mRequestOut);
212 usb_request_free(mRequestIntr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400213}
214
215void MtpDevice::initialize() {
216 openSession();
217 mDeviceInfo = getDeviceInfo();
218 if (mDeviceInfo) {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400219 if (mDeviceInfo->mDeviceProperties) {
220 int count = mDeviceInfo->mDeviceProperties->size();
221 for (int i = 0; i < count; i++) {
222 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
223 MtpProperty* property = getDevicePropDesc(propCode);
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800224 if (property)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400225 mDeviceProperties.push(property);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400226 }
227 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400228 }
229}
230
231void MtpDevice::close() {
232 if (mDevice) {
233 usb_device_release_interface(mDevice, mInterface);
234 usb_device_close(mDevice);
235 mDevice = NULL;
236 }
237}
238
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800239void MtpDevice::print() {
240 if (mDeviceInfo) {
241 mDeviceInfo->print();
242
243 if (mDeviceInfo->mDeviceProperties) {
Steve Blockdf64d152012-01-04 20:05:49 +0000244 ALOGI("***** DEVICE PROPERTIES *****\n");
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800245 int count = mDeviceInfo->mDeviceProperties->size();
246 for (int i = 0; i < count; i++) {
247 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
248 MtpProperty* property = getDevicePropDesc(propCode);
249 if (property) {
250 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800251 delete property;
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800252 }
253 }
254 }
255 }
256
257 if (mDeviceInfo->mPlaybackFormats) {
Steve Blockdf64d152012-01-04 20:05:49 +0000258 ALOGI("***** OBJECT PROPERTIES *****\n");
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800259 int count = mDeviceInfo->mPlaybackFormats->size();
260 for (int i = 0; i < count; i++) {
261 MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
Steve Blockdf64d152012-01-04 20:05:49 +0000262 ALOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800263 MtpObjectPropertyList* props = getObjectPropsSupported(format);
264 if (props) {
Mark Salyzyn3ab368e2014-04-15 14:55:53 -0700265 for (size_t j = 0; j < props->size(); j++) {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800266 MtpObjectProperty prop = (*props)[j];
Mike Lockwood99e393a2010-12-07 18:53:04 -0800267 MtpProperty* property = getObjectPropDesc(prop, format);
Kenny Root31c52e72011-02-02 13:43:55 -0800268 if (property) {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800269 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800270 delete property;
271 } else {
Steve Block29357bc2012-01-06 19:20:56 +0000272 ALOGE("could not fetch property: %s",
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800273 MtpDebug::getObjectPropCodeName(prop));
Kenny Root31c52e72011-02-02 13:43:55 -0800274 }
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800275 }
276 }
277 }
278 }
279}
280
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400281const char* MtpDevice::getDeviceName() {
282 if (mDevice)
283 return usb_device_get_name(mDevice);
284 else
285 return "???";
286}
287
288bool MtpDevice::openSession() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400289 Mutex::Autolock autoLock(mMutex);
290
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400291 mSessionID = 0;
292 mTransactionID = 0;
293 MtpSessionID newSession = 1;
294 mRequest.reset();
295 mRequest.setParameter(1, newSession);
296 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
297 return false;
298 MtpResponseCode ret = readResponse();
299 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
300 newSession = mResponse.getParameter(1);
301 else if (ret != MTP_RESPONSE_OK)
302 return false;
303
304 mSessionID = newSession;
305 mTransactionID = 1;
306 return true;
307}
308
309bool MtpDevice::closeSession() {
310 // FIXME
311 return true;
312}
313
314MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400315 Mutex::Autolock autoLock(mMutex);
316
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400317 mRequest.reset();
318 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
319 return NULL;
320 if (!readData())
321 return NULL;
322 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400323 if (ret == MTP_RESPONSE_OK) {
324 MtpDeviceInfo* info = new MtpDeviceInfo;
Mike Lockwoodab063842014-11-12 14:20:06 -0800325 if (info->read(mData))
326 return info;
327 else
328 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400329 }
330 return NULL;
331}
332
333MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400334 Mutex::Autolock autoLock(mMutex);
335
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400336 mRequest.reset();
337 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
338 return NULL;
339 if (!readData())
340 return NULL;
341 MtpResponseCode ret = readResponse();
342 if (ret == MTP_RESPONSE_OK) {
343 return mData.getAUInt32();
344 }
345 return NULL;
346}
347
348MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400349 Mutex::Autolock autoLock(mMutex);
350
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400351 mRequest.reset();
352 mRequest.setParameter(1, storageID);
353 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
354 return NULL;
355 if (!readData())
356 return NULL;
357 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400358 if (ret == MTP_RESPONSE_OK) {
359 MtpStorageInfo* info = new MtpStorageInfo(storageID);
Mike Lockwoodab063842014-11-12 14:20:06 -0800360 if (info->read(mData))
361 return info;
362 else
363 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400364 }
365 return NULL;
366}
367
368MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
369 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400370 Mutex::Autolock autoLock(mMutex);
371
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400372 mRequest.reset();
373 mRequest.setParameter(1, storageID);
374 mRequest.setParameter(2, format);
375 mRequest.setParameter(3, parent);
376 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
377 return NULL;
378 if (!readData())
379 return NULL;
380 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400381 if (ret == MTP_RESPONSE_OK) {
382 return mData.getAUInt32();
383 }
384 return NULL;
385}
386
387MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400388 Mutex::Autolock autoLock(mMutex);
389
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400390 // FIXME - we might want to add some caching here
391
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400392 mRequest.reset();
393 mRequest.setParameter(1, handle);
394 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
395 return NULL;
396 if (!readData())
397 return NULL;
398 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400399 if (ret == MTP_RESPONSE_OK) {
400 MtpObjectInfo* info = new MtpObjectInfo(handle);
Mike Lockwoodab063842014-11-12 14:20:06 -0800401 if (info->read(mData))
402 return info;
403 else
404 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400405 }
406 return NULL;
407}
408
Mike Lockwood3e072b32010-06-10 16:34:20 -0400409void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400410 Mutex::Autolock autoLock(mMutex);
411
Mike Lockwood3e072b32010-06-10 16:34:20 -0400412 mRequest.reset();
413 mRequest.setParameter(1, handle);
414 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
415 MtpResponseCode ret = readResponse();
416 if (ret == MTP_RESPONSE_OK) {
417 return mData.getData(outLength);
418 }
419 }
420 outLength = 0;
421 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400422}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400423
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400424MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
425 Mutex::Autolock autoLock(mMutex);
426
427 mRequest.reset();
428 MtpObjectHandle parent = info->mParent;
429 if (parent == 0)
430 parent = MTP_PARENT_ROOT;
431
432 mRequest.setParameter(1, info->mStorageID);
433 mRequest.setParameter(2, info->mParent);
434
435 mData.putUInt32(info->mStorageID);
436 mData.putUInt16(info->mFormat);
437 mData.putUInt16(info->mProtectionStatus);
438 mData.putUInt32(info->mCompressedSize);
439 mData.putUInt16(info->mThumbFormat);
440 mData.putUInt32(info->mThumbCompressedSize);
441 mData.putUInt32(info->mThumbPixWidth);
442 mData.putUInt32(info->mThumbPixHeight);
443 mData.putUInt32(info->mImagePixWidth);
444 mData.putUInt32(info->mImagePixHeight);
445 mData.putUInt32(info->mImagePixDepth);
446 mData.putUInt32(info->mParent);
447 mData.putUInt16(info->mAssociationType);
448 mData.putUInt32(info->mAssociationDesc);
449 mData.putUInt32(info->mSequenceNumber);
450 mData.putString(info->mName);
451
452 char created[100], modified[100];
453 formatDateTime(info->mDateCreated, created, sizeof(created));
454 formatDateTime(info->mDateModified, modified, sizeof(modified));
455
456 mData.putString(created);
457 mData.putString(modified);
458 if (info->mKeywords)
459 mData.putString(info->mKeywords);
460 else
461 mData.putEmptyString();
462
463 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400464 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400465 if (ret == MTP_RESPONSE_OK) {
466 info->mStorageID = mResponse.getParameter(1);
467 info->mParent = mResponse.getParameter(2);
468 info->mHandle = mResponse.getParameter(3);
469 return info->mHandle;
470 }
471 }
472 return (MtpObjectHandle)-1;
473}
474
475bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
476 Mutex::Autolock autoLock(mMutex);
477
478 int remaining = info->mCompressedSize;
479 mRequest.reset();
480 mRequest.setParameter(1, info->mHandle);
481 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400482 // send data header
483 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
484
485 char buffer[65536];
486 while (remaining > 0) {
487 int count = read(srcFD, buffer, sizeof(buffer));
488 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500489 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400490 // FIXME check error
491 remaining -= count;
492 } else {
493 break;
494 }
495 }
496 }
497 MtpResponseCode ret = readResponse();
498 return (remaining == 0 && ret == MTP_RESPONSE_OK);
499}
500
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400501bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400502 Mutex::Autolock autoLock(mMutex);
503
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400504 mRequest.reset();
505 mRequest.setParameter(1, handle);
506 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
507 MtpResponseCode ret = readResponse();
508 if (ret == MTP_RESPONSE_OK)
509 return true;
510 }
511 return false;
512}
513
514MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
515 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800516 if (info) {
517 MtpObjectHandle parent = info->mParent;
518 delete info;
519 return parent;
520 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400521 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800522 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400523}
524
525MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
526 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800527 if (info) {
528 MtpObjectHandle storageId = info->mStorageID;
529 delete info;
530 return storageId;
531 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400532 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800533 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400534}
535
Mike Lockwood98693f62010-12-07 10:58:56 -0800536MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
537 Mutex::Autolock autoLock(mMutex);
538
539 mRequest.reset();
540 mRequest.setParameter(1, format);
541 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
542 return NULL;
543 if (!readData())
544 return NULL;
545 MtpResponseCode ret = readResponse();
546 if (ret == MTP_RESPONSE_OK) {
547 return mData.getAUInt16();
548 }
549 return NULL;
550
551}
552
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400553MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400554 Mutex::Autolock autoLock(mMutex);
555
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400556 mRequest.reset();
557 mRequest.setParameter(1, code);
558 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
559 return NULL;
560 if (!readData())
561 return NULL;
562 MtpResponseCode ret = readResponse();
563 if (ret == MTP_RESPONSE_OK) {
564 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800565 if (property->read(mData))
566 return property;
567 else
568 delete property;
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400569 }
570 return NULL;
571}
572
Mike Lockwood99e393a2010-12-07 18:53:04 -0800573MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800574 Mutex::Autolock autoLock(mMutex);
575
576 mRequest.reset();
577 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800578 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800579 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
580 return NULL;
581 if (!readData())
582 return NULL;
583 MtpResponseCode ret = readResponse();
584 if (ret == MTP_RESPONSE_OK) {
585 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800586 if (property->read(mData))
587 return property;
588 else
589 delete property;
Mike Lockwood98693f62010-12-07 10:58:56 -0800590 }
591 return NULL;
592}
593
Mike Lockwood23f1b332010-12-30 15:38:45 -0500594bool MtpDevice::readObject(MtpObjectHandle handle,
595 bool (* callback)(void* data, int offset, int length, void* clientData),
Mike Lockwoodab063842014-11-12 14:20:06 -0800596 size_t objectSize, void* clientData) {
Mike Lockwood23f1b332010-12-30 15:38:45 -0500597 Mutex::Autolock autoLock(mMutex);
598 bool result = false;
599
600 mRequest.reset();
601 mRequest.setParameter(1, handle);
602 if (sendRequest(MTP_OPERATION_GET_OBJECT)
603 && mData.readDataHeader(mRequestIn1)) {
604 uint32_t length = mData.getContainerLength();
605 if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
Steve Block29357bc2012-01-06 19:20:56 +0000606 ALOGE("readObject error objectSize: %d, length: %d",
Mike Lockwood23f1b332010-12-30 15:38:45 -0500607 objectSize, length);
608 goto fail;
609 }
610 length -= MTP_CONTAINER_HEADER_SIZE;
611 uint32_t remaining = length;
612 int offset = 0;
613
614 int initialDataLength = 0;
615 void* initialData = mData.getData(initialDataLength);
616 if (initialData) {
617 if (initialDataLength > 0) {
618 if (!callback(initialData, 0, initialDataLength, clientData))
619 goto fail;
620 remaining -= initialDataLength;
621 offset += initialDataLength;
622 }
623 free(initialData);
624 }
625
626 // USB reads greater than 16K don't work
627 char buffer1[16384], buffer2[16384];
628 mRequestIn1->buffer = buffer1;
629 mRequestIn2->buffer = buffer2;
630 struct usb_request* req = mRequestIn1;
631 void* writeBuffer = NULL;
632 int writeLength = 0;
633
634 while (remaining > 0 || writeBuffer) {
635 if (remaining > 0) {
636 // queue up a read request
637 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
638 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000639 ALOGE("readDataAsync failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500640 goto fail;
641 }
642 } else {
643 req = NULL;
644 }
645
646 if (writeBuffer) {
647 // write previous buffer
648 if (!callback(writeBuffer, offset, writeLength, clientData)) {
Steve Block29357bc2012-01-06 19:20:56 +0000649 ALOGE("write failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500650 // wait for pending read before failing
651 if (req)
652 mData.readDataWait(mDevice);
653 goto fail;
654 }
655 offset += writeLength;
656 writeBuffer = NULL;
657 }
658
659 // wait for read to complete
660 if (req) {
661 int read = mData.readDataWait(mDevice);
662 if (read < 0)
663 goto fail;
664
665 if (read > 0) {
666 writeBuffer = req->buffer;
667 writeLength = read;
668 remaining -= read;
669 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
670 } else {
671 writeBuffer = NULL;
672 }
673 }
674 }
675
676 MtpResponseCode response = readResponse();
677 if (response == MTP_RESPONSE_OK)
678 result = true;
679 }
680
681fail:
682 return result;
683}
684
685
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500686// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500687bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Steve Blockb8a80522011-12-20 16:23:08 +0000688 ALOGD("readObject: %s", destPath);
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700689 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500690 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000691 ALOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400692 return false;
693 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400694
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500695 fchown(fd, getuid(), group);
696 // set permissions
697 int mask = umask(0);
698 fchmod(fd, perm);
699 umask(mask);
700
Tomasz Mikolajewski025ffd92015-08-04 18:38:31 +0900701 bool result = readObject(handle, fd);
702 ::close(fd);
703 return result;
704}
705
706bool MtpDevice::readObject(MtpObjectHandle handle, int fd) {
707 ALOGD("readObject: %d", fd);
708
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500709 Mutex::Autolock autoLock(mMutex);
710 bool result = false;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400711
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500712 mRequest.reset();
713 mRequest.setParameter(1, handle);
714 if (sendRequest(MTP_OPERATION_GET_OBJECT)
Mike Lockwood42d0b792011-01-04 14:48:57 -0500715 && mData.readDataHeader(mRequestIn1)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500716 uint32_t length = mData.getContainerLength();
717 if (length < MTP_CONTAINER_HEADER_SIZE)
718 goto fail;
719 length -= MTP_CONTAINER_HEADER_SIZE;
720 uint32_t remaining = length;
721
722 int initialDataLength = 0;
723 void* initialData = mData.getData(initialDataLength);
724 if (initialData) {
725 if (initialDataLength > 0) {
Kenny Root31c52e72011-02-02 13:43:55 -0800726 if (write(fd, initialData, initialDataLength) != initialDataLength) {
727 free(initialData);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500728 goto fail;
Kenny Root31c52e72011-02-02 13:43:55 -0800729 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500730 remaining -= initialDataLength;
731 }
732 free(initialData);
733 }
734
735 // USB reads greater than 16K don't work
736 char buffer1[16384], buffer2[16384];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500737 mRequestIn1->buffer = buffer1;
738 mRequestIn2->buffer = buffer2;
739 struct usb_request* req = mRequestIn1;
740 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500741 int writeLength = 0;
742
743 while (remaining > 0 || writeBuffer) {
744 if (remaining > 0) {
745 // queue up a read request
Mike Lockwood42d0b792011-01-04 14:48:57 -0500746 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
747 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000748 ALOGE("readDataAsync failed");
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500749 goto fail;
750 }
751 } else {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500752 req = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500753 }
754
755 if (writeBuffer) {
756 // write previous buffer
757 if (write(fd, writeBuffer, writeLength) != writeLength) {
Steve Block29357bc2012-01-06 19:20:56 +0000758 ALOGE("write failed");
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500759 // wait for pending read before failing
Mike Lockwood42d0b792011-01-04 14:48:57 -0500760 if (req)
761 mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500762 goto fail;
763 }
764 writeBuffer = NULL;
765 }
766
767 // wait for read to complete
Mike Lockwood42d0b792011-01-04 14:48:57 -0500768 if (req) {
769 int read = mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500770 if (read < 0)
771 goto fail;
772
Mike Lockwood23f1b332010-12-30 15:38:45 -0500773 if (read > 0) {
774 writeBuffer = req->buffer;
775 writeLength = read;
776 remaining -= read;
777 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
778 } else {
779 writeBuffer = NULL;
780 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500781 }
782 }
783
784 MtpResponseCode response = readResponse();
785 if (response == MTP_RESPONSE_OK)
786 result = true;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400787 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500788
789fail:
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500790 return result;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400791}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400792
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400793bool MtpDevice::sendRequest(MtpOperationCode operation) {
Steve Block3856b092011-10-20 11:56:00 +0100794 ALOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800795 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400796 mRequest.setOperationCode(operation);
797 if (mTransactionID > 0)
798 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500799 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400800 mRequest.dump();
801 return (ret > 0);
802}
803
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400804bool MtpDevice::sendData() {
Steve Block3856b092011-10-20 11:56:00 +0100805 ALOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400806 mData.setOperationCode(mRequest.getOperationCode());
807 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500808 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400809 mData.dump();
810 return (ret > 0);
811}
812
813bool MtpDevice::readData() {
814 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500815 int ret = mData.read(mRequestIn1);
Steve Block3856b092011-10-20 11:56:00 +0100816 ALOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400817 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800818 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
Steve Blockb8a80522011-12-20 16:23:08 +0000819 ALOGD("got response packet instead of data packet");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800820 // we got a response packet rather than data
821 // copy it to mResponse
822 mResponse.copyFrom(mData);
823 mReceivedResponse = true;
824 return false;
825 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400826 mData.dump();
827 return true;
828 }
829 else {
Steve Block3856b092011-10-20 11:56:00 +0100830 ALOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400831 return false;
832 }
833}
834
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400835bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
836 mData.setOperationCode(operation);
837 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500838 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400839}
840
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400841MtpResponseCode MtpDevice::readResponse() {
Steve Block3856b092011-10-20 11:56:00 +0100842 ALOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800843 if (mReceivedResponse) {
844 mReceivedResponse = false;
845 return mResponse.getResponseCode();
846 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500847 int ret = mResponse.read(mRequestIn1);
Mike Lockwood3d744572011-03-14 10:33:22 -0400848 // handle zero length packets, which might occur if the data transfer
849 // ends on a packet boundary
850 if (ret == 0)
851 ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400852 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
853 mResponse.dump();
854 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800855 } else {
Steve Blockb8a80522011-12-20 16:23:08 +0000856 ALOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400857 return -1;
858 }
859}
860
861} // namespace android