blob: b84a24560307ed5836c839d5785aa81710a1dfaf [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);
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900433 mRequest.setParameter(2, parent);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400434
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900435 mData.reset();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400436 mData.putUInt32(info->mStorageID);
437 mData.putUInt16(info->mFormat);
438 mData.putUInt16(info->mProtectionStatus);
439 mData.putUInt32(info->mCompressedSize);
440 mData.putUInt16(info->mThumbFormat);
441 mData.putUInt32(info->mThumbCompressedSize);
442 mData.putUInt32(info->mThumbPixWidth);
443 mData.putUInt32(info->mThumbPixHeight);
444 mData.putUInt32(info->mImagePixWidth);
445 mData.putUInt32(info->mImagePixHeight);
446 mData.putUInt32(info->mImagePixDepth);
447 mData.putUInt32(info->mParent);
448 mData.putUInt16(info->mAssociationType);
449 mData.putUInt32(info->mAssociationDesc);
450 mData.putUInt32(info->mSequenceNumber);
451 mData.putString(info->mName);
452
453 char created[100], modified[100];
454 formatDateTime(info->mDateCreated, created, sizeof(created));
455 formatDateTime(info->mDateModified, modified, sizeof(modified));
456
457 mData.putString(created);
458 mData.putString(modified);
459 if (info->mKeywords)
460 mData.putString(info->mKeywords);
461 else
462 mData.putEmptyString();
463
464 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400465 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400466 if (ret == MTP_RESPONSE_OK) {
467 info->mStorageID = mResponse.getParameter(1);
468 info->mParent = mResponse.getParameter(2);
469 info->mHandle = mResponse.getParameter(3);
470 return info->mHandle;
471 }
472 }
473 return (MtpObjectHandle)-1;
474}
475
476bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
477 Mutex::Autolock autoLock(mMutex);
478
479 int remaining = info->mCompressedSize;
480 mRequest.reset();
481 mRequest.setParameter(1, info->mHandle);
482 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400483 // send data header
484 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
485
486 char buffer[65536];
487 while (remaining > 0) {
488 int count = read(srcFD, buffer, sizeof(buffer));
489 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500490 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400491 // FIXME check error
492 remaining -= count;
493 } else {
494 break;
495 }
496 }
497 }
498 MtpResponseCode ret = readResponse();
499 return (remaining == 0 && ret == MTP_RESPONSE_OK);
500}
501
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400502bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400503 Mutex::Autolock autoLock(mMutex);
504
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400505 mRequest.reset();
506 mRequest.setParameter(1, handle);
507 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
508 MtpResponseCode ret = readResponse();
509 if (ret == MTP_RESPONSE_OK)
510 return true;
511 }
512 return false;
513}
514
515MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
516 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800517 if (info) {
518 MtpObjectHandle parent = info->mParent;
519 delete info;
520 return parent;
521 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400522 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800523 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400524}
525
526MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
527 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800528 if (info) {
529 MtpObjectHandle storageId = info->mStorageID;
530 delete info;
531 return storageId;
532 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400533 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800534 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400535}
536
Mike Lockwood98693f62010-12-07 10:58:56 -0800537MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
538 Mutex::Autolock autoLock(mMutex);
539
540 mRequest.reset();
541 mRequest.setParameter(1, format);
542 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
543 return NULL;
544 if (!readData())
545 return NULL;
546 MtpResponseCode ret = readResponse();
547 if (ret == MTP_RESPONSE_OK) {
548 return mData.getAUInt16();
549 }
550 return NULL;
551
552}
553
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400554MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400555 Mutex::Autolock autoLock(mMutex);
556
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400557 mRequest.reset();
558 mRequest.setParameter(1, code);
559 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
560 return NULL;
561 if (!readData())
562 return NULL;
563 MtpResponseCode ret = readResponse();
564 if (ret == MTP_RESPONSE_OK) {
565 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800566 if (property->read(mData))
567 return property;
568 else
569 delete property;
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400570 }
571 return NULL;
572}
573
Mike Lockwood99e393a2010-12-07 18:53:04 -0800574MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800575 Mutex::Autolock autoLock(mMutex);
576
577 mRequest.reset();
578 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800579 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800580 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
581 return NULL;
582 if (!readData())
583 return NULL;
584 MtpResponseCode ret = readResponse();
585 if (ret == MTP_RESPONSE_OK) {
586 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800587 if (property->read(mData))
588 return property;
589 else
590 delete property;
Mike Lockwood98693f62010-12-07 10:58:56 -0800591 }
592 return NULL;
593}
594
Mike Lockwood23f1b332010-12-30 15:38:45 -0500595bool MtpDevice::readObject(MtpObjectHandle handle,
596 bool (* callback)(void* data, int offset, int length, void* clientData),
Mike Lockwoodab063842014-11-12 14:20:06 -0800597 size_t objectSize, void* clientData) {
Mike Lockwood23f1b332010-12-30 15:38:45 -0500598 Mutex::Autolock autoLock(mMutex);
599 bool result = false;
600
601 mRequest.reset();
602 mRequest.setParameter(1, handle);
603 if (sendRequest(MTP_OPERATION_GET_OBJECT)
604 && mData.readDataHeader(mRequestIn1)) {
605 uint32_t length = mData.getContainerLength();
606 if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
Steve Block29357bc2012-01-06 19:20:56 +0000607 ALOGE("readObject error objectSize: %d, length: %d",
Mike Lockwood23f1b332010-12-30 15:38:45 -0500608 objectSize, length);
609 goto fail;
610 }
611 length -= MTP_CONTAINER_HEADER_SIZE;
612 uint32_t remaining = length;
613 int offset = 0;
614
615 int initialDataLength = 0;
616 void* initialData = mData.getData(initialDataLength);
617 if (initialData) {
618 if (initialDataLength > 0) {
619 if (!callback(initialData, 0, initialDataLength, clientData))
620 goto fail;
621 remaining -= initialDataLength;
622 offset += initialDataLength;
623 }
624 free(initialData);
625 }
626
627 // USB reads greater than 16K don't work
628 char buffer1[16384], buffer2[16384];
629 mRequestIn1->buffer = buffer1;
630 mRequestIn2->buffer = buffer2;
631 struct usb_request* req = mRequestIn1;
632 void* writeBuffer = NULL;
633 int writeLength = 0;
634
635 while (remaining > 0 || writeBuffer) {
636 if (remaining > 0) {
637 // queue up a read request
638 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
639 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000640 ALOGE("readDataAsync failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500641 goto fail;
642 }
643 } else {
644 req = NULL;
645 }
646
647 if (writeBuffer) {
648 // write previous buffer
649 if (!callback(writeBuffer, offset, writeLength, clientData)) {
Steve Block29357bc2012-01-06 19:20:56 +0000650 ALOGE("write failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500651 // wait for pending read before failing
652 if (req)
653 mData.readDataWait(mDevice);
654 goto fail;
655 }
656 offset += writeLength;
657 writeBuffer = NULL;
658 }
659
660 // wait for read to complete
661 if (req) {
662 int read = mData.readDataWait(mDevice);
663 if (read < 0)
664 goto fail;
665
666 if (read > 0) {
667 writeBuffer = req->buffer;
668 writeLength = read;
669 remaining -= read;
670 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
671 } else {
672 writeBuffer = NULL;
673 }
674 }
675 }
676
677 MtpResponseCode response = readResponse();
678 if (response == MTP_RESPONSE_OK)
679 result = true;
680 }
681
682fail:
683 return result;
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);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400710
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500711 mRequest.reset();
712 mRequest.setParameter(1, handle);
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900713 if (sendRequest(MTP_OPERATION_GET_OBJECT) && mData.readDataHeader(mRequestIn1)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500714 uint32_t length = mData.getContainerLength();
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900715 if (length < MTP_CONTAINER_HEADER_SIZE) {
716 ALOGE("Invalid container length.");
717 return false;
718 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500719 length -= MTP_CONTAINER_HEADER_SIZE;
720 uint32_t remaining = length;
721
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900722 bool writingError = false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500723 int initialDataLength = 0;
724 void* initialData = mData.getData(initialDataLength);
725 if (initialData) {
726 if (initialDataLength > 0) {
Kenny Root31c52e72011-02-02 13:43:55 -0800727 if (write(fd, initialData, initialDataLength) != initialDataLength) {
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900728 ALOGE("Failed to write initial data.");
729 writingError = true;
Kenny Root31c52e72011-02-02 13:43:55 -0800730 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500731 remaining -= initialDataLength;
732 }
733 free(initialData);
734 }
735
736 // USB reads greater than 16K don't work
737 char buffer1[16384], buffer2[16384];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500738 mRequestIn1->buffer = buffer1;
739 mRequestIn2->buffer = buffer2;
740 struct usb_request* req = mRequestIn1;
741 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500742 int writeLength = 0;
743
744 while (remaining > 0 || writeBuffer) {
745 if (remaining > 0) {
746 // queue up a read request
Mike Lockwood42d0b792011-01-04 14:48:57 -0500747 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
748 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000749 ALOGE("readDataAsync failed");
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900750 return false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500751 }
752 } else {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500753 req = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500754 }
755
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900756 if (writeBuffer && !writingError) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500757 // write previous buffer
758 if (write(fd, writeBuffer, writeLength) != writeLength) {
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900759 writingError = true;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500760 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500761 }
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900762 writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500763
764 // wait for read to complete
Mike Lockwood42d0b792011-01-04 14:48:57 -0500765 if (req) {
766 int read = mData.readDataWait(mDevice);
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900767 if (read < 0) {
768 ALOGE("readDataWait failed.");
769 return false;
770 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500771
Mike Lockwood23f1b332010-12-30 15:38:45 -0500772 if (read > 0) {
773 writeBuffer = req->buffer;
774 writeLength = read;
775 remaining -= read;
776 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
777 } else {
778 writeBuffer = NULL;
779 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500780 }
781 }
782
783 MtpResponseCode response = readResponse();
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900784 return response == MTP_RESPONSE_OK && !writingError;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400785 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500786
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900787 return false;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400788}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400789
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400790bool MtpDevice::sendRequest(MtpOperationCode operation) {
Steve Block3856b092011-10-20 11:56:00 +0100791 ALOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800792 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400793 mRequest.setOperationCode(operation);
794 if (mTransactionID > 0)
795 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500796 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400797 mRequest.dump();
798 return (ret > 0);
799}
800
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400801bool MtpDevice::sendData() {
Steve Block3856b092011-10-20 11:56:00 +0100802 ALOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400803 mData.setOperationCode(mRequest.getOperationCode());
804 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500805 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400806 mData.dump();
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900807 return (ret >= 0);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400808}
809
810bool MtpDevice::readData() {
811 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500812 int ret = mData.read(mRequestIn1);
Steve Block3856b092011-10-20 11:56:00 +0100813 ALOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400814 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800815 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
Steve Blockb8a80522011-12-20 16:23:08 +0000816 ALOGD("got response packet instead of data packet");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800817 // we got a response packet rather than data
818 // copy it to mResponse
819 mResponse.copyFrom(mData);
820 mReceivedResponse = true;
821 return false;
822 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400823 mData.dump();
824 return true;
825 }
826 else {
Steve Block3856b092011-10-20 11:56:00 +0100827 ALOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400828 return false;
829 }
830}
831
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400832bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
833 mData.setOperationCode(operation);
834 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500835 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400836}
837
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400838MtpResponseCode MtpDevice::readResponse() {
Steve Block3856b092011-10-20 11:56:00 +0100839 ALOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800840 if (mReceivedResponse) {
841 mReceivedResponse = false;
842 return mResponse.getResponseCode();
843 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500844 int ret = mResponse.read(mRequestIn1);
Mike Lockwood3d744572011-03-14 10:33:22 -0400845 // handle zero length packets, which might occur if the data transfer
846 // ends on a packet boundary
847 if (ret == 0)
848 ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400849 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
850 mResponse.dump();
851 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800852 } else {
Steve Blockb8a80522011-12-20 16:23:08 +0000853 ALOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400854 return -1;
855 }
856}
857
858} // namespace android