blob: 96331b5934bddc7b6022b10f27f3856d32abe378 [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;
325 info->read(mData);
326 return info;
327 }
328 return NULL;
329}
330
331MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400332 Mutex::Autolock autoLock(mMutex);
333
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400334 mRequest.reset();
335 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
336 return NULL;
337 if (!readData())
338 return NULL;
339 MtpResponseCode ret = readResponse();
340 if (ret == MTP_RESPONSE_OK) {
341 return mData.getAUInt32();
342 }
343 return NULL;
344}
345
346MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400347 Mutex::Autolock autoLock(mMutex);
348
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400349 mRequest.reset();
350 mRequest.setParameter(1, storageID);
351 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
352 return NULL;
353 if (!readData())
354 return NULL;
355 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400356 if (ret == MTP_RESPONSE_OK) {
357 MtpStorageInfo* info = new MtpStorageInfo(storageID);
358 info->read(mData);
359 return info;
360 }
361 return NULL;
362}
363
364MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
365 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400366 Mutex::Autolock autoLock(mMutex);
367
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400368 mRequest.reset();
369 mRequest.setParameter(1, storageID);
370 mRequest.setParameter(2, format);
371 mRequest.setParameter(3, parent);
372 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
373 return NULL;
374 if (!readData())
375 return NULL;
376 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400377 if (ret == MTP_RESPONSE_OK) {
378 return mData.getAUInt32();
379 }
380 return NULL;
381}
382
383MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400384 Mutex::Autolock autoLock(mMutex);
385
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400386 // FIXME - we might want to add some caching here
387
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400388 mRequest.reset();
389 mRequest.setParameter(1, handle);
390 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
391 return NULL;
392 if (!readData())
393 return NULL;
394 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400395 if (ret == MTP_RESPONSE_OK) {
396 MtpObjectInfo* info = new MtpObjectInfo(handle);
397 info->read(mData);
398 return info;
399 }
400 return NULL;
401}
402
Mike Lockwood3e072b32010-06-10 16:34:20 -0400403void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400404 Mutex::Autolock autoLock(mMutex);
405
Mike Lockwood3e072b32010-06-10 16:34:20 -0400406 mRequest.reset();
407 mRequest.setParameter(1, handle);
408 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
409 MtpResponseCode ret = readResponse();
410 if (ret == MTP_RESPONSE_OK) {
411 return mData.getData(outLength);
412 }
413 }
414 outLength = 0;
415 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400416}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400417
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400418MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
419 Mutex::Autolock autoLock(mMutex);
420
421 mRequest.reset();
422 MtpObjectHandle parent = info->mParent;
423 if (parent == 0)
424 parent = MTP_PARENT_ROOT;
425
426 mRequest.setParameter(1, info->mStorageID);
427 mRequest.setParameter(2, info->mParent);
428
429 mData.putUInt32(info->mStorageID);
430 mData.putUInt16(info->mFormat);
431 mData.putUInt16(info->mProtectionStatus);
432 mData.putUInt32(info->mCompressedSize);
433 mData.putUInt16(info->mThumbFormat);
434 mData.putUInt32(info->mThumbCompressedSize);
435 mData.putUInt32(info->mThumbPixWidth);
436 mData.putUInt32(info->mThumbPixHeight);
437 mData.putUInt32(info->mImagePixWidth);
438 mData.putUInt32(info->mImagePixHeight);
439 mData.putUInt32(info->mImagePixDepth);
440 mData.putUInt32(info->mParent);
441 mData.putUInt16(info->mAssociationType);
442 mData.putUInt32(info->mAssociationDesc);
443 mData.putUInt32(info->mSequenceNumber);
444 mData.putString(info->mName);
445
446 char created[100], modified[100];
447 formatDateTime(info->mDateCreated, created, sizeof(created));
448 formatDateTime(info->mDateModified, modified, sizeof(modified));
449
450 mData.putString(created);
451 mData.putString(modified);
452 if (info->mKeywords)
453 mData.putString(info->mKeywords);
454 else
455 mData.putEmptyString();
456
457 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400458 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400459 if (ret == MTP_RESPONSE_OK) {
460 info->mStorageID = mResponse.getParameter(1);
461 info->mParent = mResponse.getParameter(2);
462 info->mHandle = mResponse.getParameter(3);
463 return info->mHandle;
464 }
465 }
466 return (MtpObjectHandle)-1;
467}
468
469bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
470 Mutex::Autolock autoLock(mMutex);
471
472 int remaining = info->mCompressedSize;
473 mRequest.reset();
474 mRequest.setParameter(1, info->mHandle);
475 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400476 // send data header
477 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
478
479 char buffer[65536];
480 while (remaining > 0) {
481 int count = read(srcFD, buffer, sizeof(buffer));
482 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500483 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400484 // FIXME check error
485 remaining -= count;
486 } else {
487 break;
488 }
489 }
490 }
491 MtpResponseCode ret = readResponse();
492 return (remaining == 0 && ret == MTP_RESPONSE_OK);
493}
494
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400495bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400496 Mutex::Autolock autoLock(mMutex);
497
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400498 mRequest.reset();
499 mRequest.setParameter(1, handle);
500 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
501 MtpResponseCode ret = readResponse();
502 if (ret == MTP_RESPONSE_OK)
503 return true;
504 }
505 return false;
506}
507
508MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
509 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800510 if (info) {
511 MtpObjectHandle parent = info->mParent;
512 delete info;
513 return parent;
514 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400515 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800516 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400517}
518
519MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
520 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800521 if (info) {
522 MtpObjectHandle storageId = info->mStorageID;
523 delete info;
524 return storageId;
525 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400526 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800527 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400528}
529
Mike Lockwood98693f62010-12-07 10:58:56 -0800530MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
531 Mutex::Autolock autoLock(mMutex);
532
533 mRequest.reset();
534 mRequest.setParameter(1, format);
535 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
536 return NULL;
537 if (!readData())
538 return NULL;
539 MtpResponseCode ret = readResponse();
540 if (ret == MTP_RESPONSE_OK) {
541 return mData.getAUInt16();
542 }
543 return NULL;
544
545}
546
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400547MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400548 Mutex::Autolock autoLock(mMutex);
549
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400550 mRequest.reset();
551 mRequest.setParameter(1, code);
552 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
553 return NULL;
554 if (!readData())
555 return NULL;
556 MtpResponseCode ret = readResponse();
557 if (ret == MTP_RESPONSE_OK) {
558 MtpProperty* property = new MtpProperty;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400559 property->read(mData);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400560 return property;
561 }
562 return NULL;
563}
564
Mike Lockwood99e393a2010-12-07 18:53:04 -0800565MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800566 Mutex::Autolock autoLock(mMutex);
567
568 mRequest.reset();
569 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800570 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800571 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
572 return NULL;
573 if (!readData())
574 return NULL;
575 MtpResponseCode ret = readResponse();
576 if (ret == MTP_RESPONSE_OK) {
577 MtpProperty* property = new MtpProperty;
578 property->read(mData);
579 return property;
580 }
581 return NULL;
582}
583
Mike Lockwood23f1b332010-12-30 15:38:45 -0500584bool MtpDevice::readObject(MtpObjectHandle handle,
585 bool (* callback)(void* data, int offset, int length, void* clientData),
586 int objectSize, void* clientData) {
587 Mutex::Autolock autoLock(mMutex);
588 bool result = false;
589
590 mRequest.reset();
591 mRequest.setParameter(1, handle);
592 if (sendRequest(MTP_OPERATION_GET_OBJECT)
593 && mData.readDataHeader(mRequestIn1)) {
594 uint32_t length = mData.getContainerLength();
595 if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
Steve Block29357bc2012-01-06 19:20:56 +0000596 ALOGE("readObject error objectSize: %d, length: %d",
Mike Lockwood23f1b332010-12-30 15:38:45 -0500597 objectSize, length);
598 goto fail;
599 }
600 length -= MTP_CONTAINER_HEADER_SIZE;
601 uint32_t remaining = length;
602 int offset = 0;
603
604 int initialDataLength = 0;
605 void* initialData = mData.getData(initialDataLength);
606 if (initialData) {
607 if (initialDataLength > 0) {
608 if (!callback(initialData, 0, initialDataLength, clientData))
609 goto fail;
610 remaining -= initialDataLength;
611 offset += initialDataLength;
612 }
613 free(initialData);
614 }
615
616 // USB reads greater than 16K don't work
617 char buffer1[16384], buffer2[16384];
618 mRequestIn1->buffer = buffer1;
619 mRequestIn2->buffer = buffer2;
620 struct usb_request* req = mRequestIn1;
621 void* writeBuffer = NULL;
622 int writeLength = 0;
623
624 while (remaining > 0 || writeBuffer) {
625 if (remaining > 0) {
626 // queue up a read request
627 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
628 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000629 ALOGE("readDataAsync failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500630 goto fail;
631 }
632 } else {
633 req = NULL;
634 }
635
636 if (writeBuffer) {
637 // write previous buffer
638 if (!callback(writeBuffer, offset, writeLength, clientData)) {
Steve Block29357bc2012-01-06 19:20:56 +0000639 ALOGE("write failed");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500640 // wait for pending read before failing
641 if (req)
642 mData.readDataWait(mDevice);
643 goto fail;
644 }
645 offset += writeLength;
646 writeBuffer = NULL;
647 }
648
649 // wait for read to complete
650 if (req) {
651 int read = mData.readDataWait(mDevice);
652 if (read < 0)
653 goto fail;
654
655 if (read > 0) {
656 writeBuffer = req->buffer;
657 writeLength = read;
658 remaining -= read;
659 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
660 } else {
661 writeBuffer = NULL;
662 }
663 }
664 }
665
666 MtpResponseCode response = readResponse();
667 if (response == MTP_RESPONSE_OK)
668 result = true;
669 }
670
671fail:
672 return result;
673}
674
675
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500676// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500677bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Steve Blockb8a80522011-12-20 16:23:08 +0000678 ALOGD("readObject: %s", destPath);
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700679 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500680 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000681 ALOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400682 return false;
683 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400684
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500685 fchown(fd, getuid(), group);
686 // set permissions
687 int mask = umask(0);
688 fchmod(fd, perm);
689 umask(mask);
690
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500691 Mutex::Autolock autoLock(mMutex);
692 bool result = false;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400693
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500694 mRequest.reset();
695 mRequest.setParameter(1, handle);
696 if (sendRequest(MTP_OPERATION_GET_OBJECT)
Mike Lockwood42d0b792011-01-04 14:48:57 -0500697 && mData.readDataHeader(mRequestIn1)) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500698 uint32_t length = mData.getContainerLength();
699 if (length < MTP_CONTAINER_HEADER_SIZE)
700 goto fail;
701 length -= MTP_CONTAINER_HEADER_SIZE;
702 uint32_t remaining = length;
703
704 int initialDataLength = 0;
705 void* initialData = mData.getData(initialDataLength);
706 if (initialData) {
707 if (initialDataLength > 0) {
Kenny Root31c52e72011-02-02 13:43:55 -0800708 if (write(fd, initialData, initialDataLength) != initialDataLength) {
709 free(initialData);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500710 goto fail;
Kenny Root31c52e72011-02-02 13:43:55 -0800711 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500712 remaining -= initialDataLength;
713 }
714 free(initialData);
715 }
716
717 // USB reads greater than 16K don't work
718 char buffer1[16384], buffer2[16384];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500719 mRequestIn1->buffer = buffer1;
720 mRequestIn2->buffer = buffer2;
721 struct usb_request* req = mRequestIn1;
722 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500723 int writeLength = 0;
724
725 while (remaining > 0 || writeBuffer) {
726 if (remaining > 0) {
727 // queue up a read request
Mike Lockwood42d0b792011-01-04 14:48:57 -0500728 req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
729 if (mData.readDataAsync(req)) {
Steve Block29357bc2012-01-06 19:20:56 +0000730 ALOGE("readDataAsync failed");
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500731 goto fail;
732 }
733 } else {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500734 req = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500735 }
736
737 if (writeBuffer) {
738 // write previous buffer
739 if (write(fd, writeBuffer, writeLength) != writeLength) {
Steve Block29357bc2012-01-06 19:20:56 +0000740 ALOGE("write failed");
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500741 // wait for pending read before failing
Mike Lockwood42d0b792011-01-04 14:48:57 -0500742 if (req)
743 mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500744 goto fail;
745 }
746 writeBuffer = NULL;
747 }
748
749 // wait for read to complete
Mike Lockwood42d0b792011-01-04 14:48:57 -0500750 if (req) {
751 int read = mData.readDataWait(mDevice);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500752 if (read < 0)
753 goto fail;
754
Mike Lockwood23f1b332010-12-30 15:38:45 -0500755 if (read > 0) {
756 writeBuffer = req->buffer;
757 writeLength = read;
758 remaining -= read;
759 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
760 } else {
761 writeBuffer = NULL;
762 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500763 }
764 }
765
766 MtpResponseCode response = readResponse();
767 if (response == MTP_RESPONSE_OK)
768 result = true;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400769 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500770
771fail:
772 ::close(fd);
773 return result;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400774}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400775
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400776bool MtpDevice::sendRequest(MtpOperationCode operation) {
Steve Block3856b092011-10-20 11:56:00 +0100777 ALOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800778 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400779 mRequest.setOperationCode(operation);
780 if (mTransactionID > 0)
781 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500782 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400783 mRequest.dump();
784 return (ret > 0);
785}
786
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400787bool MtpDevice::sendData() {
Steve Block3856b092011-10-20 11:56:00 +0100788 ALOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400789 mData.setOperationCode(mRequest.getOperationCode());
790 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500791 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400792 mData.dump();
793 return (ret > 0);
794}
795
796bool MtpDevice::readData() {
797 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500798 int ret = mData.read(mRequestIn1);
Steve Block3856b092011-10-20 11:56:00 +0100799 ALOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400800 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800801 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
Steve Blockb8a80522011-12-20 16:23:08 +0000802 ALOGD("got response packet instead of data packet");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800803 // we got a response packet rather than data
804 // copy it to mResponse
805 mResponse.copyFrom(mData);
806 mReceivedResponse = true;
807 return false;
808 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400809 mData.dump();
810 return true;
811 }
812 else {
Steve Block3856b092011-10-20 11:56:00 +0100813 ALOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400814 return false;
815 }
816}
817
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400818bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
819 mData.setOperationCode(operation);
820 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500821 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400822}
823
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400824MtpResponseCode MtpDevice::readResponse() {
Steve Block3856b092011-10-20 11:56:00 +0100825 ALOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800826 if (mReceivedResponse) {
827 mReceivedResponse = false;
828 return mResponse.getResponseCode();
829 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500830 int ret = mResponse.read(mRequestIn1);
Mike Lockwood3d744572011-03-14 10:33:22 -0400831 // handle zero length packets, which might occur if the data transfer
832 // ends on a packet boundary
833 if (ret == 0)
834 ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400835 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
836 mResponse.dump();
837 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800838 } else {
Steve Blockb8a80522011-12-20 16:23:08 +0000839 ALOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400840 return -1;
841 }
842}
843
844} // namespace android