blob: 9878f9039f150020075331853006fc714e284ed6 [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"
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +090022#include "MtpEventPacket.h"
Mike Lockwoodb14e5882010-06-29 18:11:52 -040023#include "MtpObjectInfo.h"
24#include "MtpProperty.h"
25#include "MtpStorageInfo.h"
26#include "MtpStringBuffer.h"
Mike Lockwood0cf89f22010-07-26 20:40:45 -040027#include "MtpUtils.h"
Mike Lockwooda6c490b2010-06-05 22:45:01 -040028
Mike Lockwood5ed68d22010-05-25 19:08:48 -040029#include <stdio.h>
30#include <stdlib.h>
31#include <sys/types.h>
32#include <sys/ioctl.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <errno.h>
Mike Lockwood0cf89f22010-07-26 20:40:45 -040036#include <endian.h>
Mike Lockwood5ed68d22010-05-25 19:08:48 -040037
38#include <usbhost/usbhost.h>
39
Mike Lockwood5ed68d22010-05-25 19:08:48 -040040namespace android {
41
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -050042#if 0
Mike Lockwood23f1b332010-12-30 15:38:45 -050043static bool isMtpDevice(uint16_t vendor, uint16_t product) {
44 // Sandisk Sansa Fuze
45 if (vendor == 0x0781 && product == 0x74c2)
46 return true;
47 // Samsung YP-Z5
48 if (vendor == 0x04e8 && product == 0x503c)
49 return true;
50 return false;
51}
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -050052#endif
Mike Lockwood23f1b332010-12-30 15:38:45 -050053
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +090054namespace {
55
56bool writeToFd(void* data, int /* unused_offset */, int length, void* clientData) {
57 const int fd = *static_cast<int*>(clientData);
58 return write(fd, data, length) == length;
59}
60
61}
62
Mike Lockwood23f1b332010-12-30 15:38:45 -050063MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
64 struct usb_device *device = usb_device_new(deviceName, fd);
65 if (!device) {
Steve Block29357bc2012-01-06 19:20:56 +000066 ALOGE("usb_device_new failed for %s", deviceName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050067 return NULL;
68 }
69
70 struct usb_descriptor_header* desc;
71 struct usb_descriptor_iter iter;
72
73 usb_descriptor_iter_init(device, &iter);
74
75 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
76 if (desc->bDescriptorType == USB_DT_INTERFACE) {
77 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
78
79 if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
80 interface->bInterfaceSubClass == 1 && // Still Image Capture
81 interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
82 {
Kenny Root31c52e72011-02-02 13:43:55 -080083 char* manufacturerName = usb_device_get_manufacturer_name(device);
84 char* productName = usb_device_get_product_name(device);
Steve Blockb8a80522011-12-20 16:23:08 +000085 ALOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
Kenny Root31c52e72011-02-02 13:43:55 -080086 free(manufacturerName);
87 free(productName);
Mike Lockwood23f1b332010-12-30 15:38:45 -050088 } else if (interface->bInterfaceClass == 0xFF &&
89 interface->bInterfaceSubClass == 0xFF &&
90 interface->bInterfaceProtocol == 0) {
91 char* interfaceName = usb_device_get_string(device, interface->iInterface);
Kenny Root31c52e72011-02-02 13:43:55 -080092 if (!interfaceName) {
Mike Lockwood23f1b332010-12-30 15:38:45 -050093 continue;
Kenny Root31c52e72011-02-02 13:43:55 -080094 } else if (strcmp(interfaceName, "MTP")) {
95 free(interfaceName);
96 continue;
97 }
98 free(interfaceName);
99
Mike Lockwood23f1b332010-12-30 15:38:45 -0500100 // Looks like an android style MTP device
Kenny Root31c52e72011-02-02 13:43:55 -0800101 char* manufacturerName = usb_device_get_manufacturer_name(device);
102 char* productName = usb_device_get_product_name(device);
Steve Blockb8a80522011-12-20 16:23:08 +0000103 ALOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
Kenny Root31c52e72011-02-02 13:43:55 -0800104 free(manufacturerName);
105 free(productName);
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -0500106 }
107#if 0
108 else {
Mike Lockwood23f1b332010-12-30 15:38:45 -0500109 // look for special cased devices based on vendor/product ID
110 // we are doing this mainly for testing purposes
111 uint16_t vendor = usb_device_get_vendor_id(device);
112 uint16_t product = usb_device_get_product_id(device);
113 if (!isMtpDevice(vendor, product)) {
114 // not an MTP or PTP device
115 continue;
116 }
117 // request MTP OS string and descriptor
118 // some music players need to see this before entering MTP mode.
119 char buffer[256];
120 memset(buffer, 0, sizeof(buffer));
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800121 int ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500122 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
123 USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800124 0, buffer, sizeof(buffer), 0);
125 printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500126 if (ret > 0) {
127 printf("got MTP string %s\n", buffer);
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800128 ret = usb_device_control_transfer(device,
Mike Lockwood23f1b332010-12-30 15:38:45 -0500129 USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
Mike Lockwoodf41ef0e2011-01-27 10:47:40 -0800130 0, 4, buffer, sizeof(buffer), 0);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500131 printf("OS descriptor got %d\n", ret);
132 } else {
133 printf("no MTP string\n");
134 }
135 }
Mike Lockwoodd4fb52e2011-02-15 14:55:51 -0500136#endif
Mike Lockwood23f1b332010-12-30 15:38:45 -0500137 // if we got here, then we have a likely MTP or PTP device
138
139 // interface should be followed by three endpoints
140 struct usb_endpoint_descriptor *ep;
141 struct usb_endpoint_descriptor *ep_in_desc = NULL;
142 struct usb_endpoint_descriptor *ep_out_desc = NULL;
143 struct usb_endpoint_descriptor *ep_intr_desc = NULL;
Bo Huang8023f3a2013-06-09 08:53:21 +0800144 //USB3 add USB_DT_SS_ENDPOINT_COMP as companion descriptor;
145 struct usb_ss_ep_comp_descriptor *ep_ss_ep_comp_desc = NULL;
Mike Lockwood23f1b332010-12-30 15:38:45 -0500146 for (int i = 0; i < 3; i++) {
147 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
Bo Huang8023f3a2013-06-09 08:53:21 +0800148 if (ep && ep->bDescriptorType == USB_DT_SS_ENDPOINT_COMP) {
149 ALOGD("Descriptor type is USB_DT_SS_ENDPOINT_COMP for USB3 \n");
150 ep_ss_ep_comp_desc = (usb_ss_ep_comp_descriptor*)ep;
151 ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
152 }
153
Mike Lockwood23f1b332010-12-30 15:38:45 -0500154 if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
Steve Block29357bc2012-01-06 19:20:56 +0000155 ALOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800156 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500157 return NULL;
158 }
Bo Huang8023f3a2013-06-09 08:53:21 +0800159
Mike Lockwood23f1b332010-12-30 15:38:45 -0500160 if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
161 if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
162 ep_in_desc = ep;
163 else
164 ep_out_desc = ep;
165 } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
166 ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
167 ep_intr_desc = ep;
168 }
169 }
170 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
Steve Block29357bc2012-01-06 19:20:56 +0000171 ALOGE("endpoints not found\n");
Kenny Root31c52e72011-02-02 13:43:55 -0800172 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500173 return NULL;
174 }
175
176 if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
Steve Block29357bc2012-01-06 19:20:56 +0000177 ALOGE("usb_device_claim_interface failed errno: %d\n", errno);
Kenny Root31c52e72011-02-02 13:43:55 -0800178 usb_device_close(device);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500179 return NULL;
180 }
181
182 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
183 ep_in_desc, ep_out_desc, ep_intr_desc);
184 mtpDevice->initialize();
185 return mtpDevice;
186 }
187 }
188
189 usb_device_close(device);
Steve Block29357bc2012-01-06 19:20:56 +0000190 ALOGE("device not found");
Mike Lockwood23f1b332010-12-30 15:38:45 -0500191 return NULL;
192}
193
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400194MtpDevice::MtpDevice(struct usb_device* device, int interface,
Mike Lockwood42d0b792011-01-04 14:48:57 -0500195 const struct usb_endpoint_descriptor *ep_in,
196 const struct usb_endpoint_descriptor *ep_out,
197 const struct usb_endpoint_descriptor *ep_intr)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400198 : mDevice(device),
199 mInterface(interface),
Mike Lockwood42d0b792011-01-04 14:48:57 -0500200 mRequestIn1(NULL),
201 mRequestIn2(NULL),
202 mRequestOut(NULL),
203 mRequestIntr(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400204 mDeviceInfo(NULL),
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400205 mSessionID(0),
Mike Lockwoodf7454622010-12-09 18:34:18 -0800206 mTransactionID(0),
207 mReceivedResponse(false)
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400208{
Mike Lockwood42d0b792011-01-04 14:48:57 -0500209 mRequestIn1 = usb_request_new(device, ep_in);
210 mRequestIn2 = usb_request_new(device, ep_in);
211 mRequestOut = usb_request_new(device, ep_out);
212 mRequestIntr = usb_request_new(device, ep_intr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400213}
214
215MtpDevice::~MtpDevice() {
216 close();
Mark Salyzyn3ab368e2014-04-15 14:55:53 -0700217 for (size_t i = 0; i < mDeviceProperties.size(); i++)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400218 delete mDeviceProperties[i];
Mike Lockwood42d0b792011-01-04 14:48:57 -0500219 usb_request_free(mRequestIn1);
220 usb_request_free(mRequestIn2);
221 usb_request_free(mRequestOut);
222 usb_request_free(mRequestIntr);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400223}
224
225void MtpDevice::initialize() {
226 openSession();
227 mDeviceInfo = getDeviceInfo();
228 if (mDeviceInfo) {
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400229 if (mDeviceInfo->mDeviceProperties) {
230 int count = mDeviceInfo->mDeviceProperties->size();
231 for (int i = 0; i < count; i++) {
232 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
233 MtpProperty* property = getDevicePropDesc(propCode);
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800234 if (property)
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400235 mDeviceProperties.push(property);
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400236 }
237 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400238 }
239}
240
241void MtpDevice::close() {
242 if (mDevice) {
243 usb_device_release_interface(mDevice, mInterface);
244 usb_device_close(mDevice);
245 mDevice = NULL;
246 }
247}
248
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800249void MtpDevice::print() {
250 if (mDeviceInfo) {
251 mDeviceInfo->print();
252
253 if (mDeviceInfo->mDeviceProperties) {
Steve Blockdf64d152012-01-04 20:05:49 +0000254 ALOGI("***** DEVICE PROPERTIES *****\n");
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800255 int count = mDeviceInfo->mDeviceProperties->size();
256 for (int i = 0; i < count; i++) {
257 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
258 MtpProperty* property = getDevicePropDesc(propCode);
259 if (property) {
260 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800261 delete property;
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800262 }
263 }
264 }
265 }
266
267 if (mDeviceInfo->mPlaybackFormats) {
Steve Blockdf64d152012-01-04 20:05:49 +0000268 ALOGI("***** OBJECT PROPERTIES *****\n");
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800269 int count = mDeviceInfo->mPlaybackFormats->size();
270 for (int i = 0; i < count; i++) {
271 MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
Steve Blockdf64d152012-01-04 20:05:49 +0000272 ALOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800273 MtpObjectPropertyList* props = getObjectPropsSupported(format);
274 if (props) {
Mark Salyzyn3ab368e2014-04-15 14:55:53 -0700275 for (size_t j = 0; j < props->size(); j++) {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800276 MtpObjectProperty prop = (*props)[j];
Mike Lockwood99e393a2010-12-07 18:53:04 -0800277 MtpProperty* property = getObjectPropDesc(prop, format);
Kenny Root31c52e72011-02-02 13:43:55 -0800278 if (property) {
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800279 property->print();
Kenny Root31c52e72011-02-02 13:43:55 -0800280 delete property;
281 } else {
Steve Block29357bc2012-01-06 19:20:56 +0000282 ALOGE("could not fetch property: %s",
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800283 MtpDebug::getObjectPropCodeName(prop));
Kenny Root31c52e72011-02-02 13:43:55 -0800284 }
Mike Lockwood0c7c7c72010-12-07 11:24:28 -0800285 }
286 }
287 }
288 }
289}
290
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400291const char* MtpDevice::getDeviceName() {
292 if (mDevice)
293 return usb_device_get_name(mDevice);
294 else
295 return "???";
296}
297
298bool MtpDevice::openSession() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400299 Mutex::Autolock autoLock(mMutex);
300
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400301 mSessionID = 0;
302 mTransactionID = 0;
303 MtpSessionID newSession = 1;
304 mRequest.reset();
305 mRequest.setParameter(1, newSession);
306 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
307 return false;
308 MtpResponseCode ret = readResponse();
309 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
310 newSession = mResponse.getParameter(1);
311 else if (ret != MTP_RESPONSE_OK)
312 return false;
313
314 mSessionID = newSession;
315 mTransactionID = 1;
316 return true;
317}
318
319bool MtpDevice::closeSession() {
320 // FIXME
321 return true;
322}
323
324MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400325 Mutex::Autolock autoLock(mMutex);
326
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400327 mRequest.reset();
328 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
329 return NULL;
330 if (!readData())
331 return NULL;
332 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400333 if (ret == MTP_RESPONSE_OK) {
334 MtpDeviceInfo* info = new MtpDeviceInfo;
Mike Lockwoodab063842014-11-12 14:20:06 -0800335 if (info->read(mData))
336 return info;
337 else
338 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400339 }
340 return NULL;
341}
342
343MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400344 Mutex::Autolock autoLock(mMutex);
345
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400346 mRequest.reset();
347 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
348 return NULL;
349 if (!readData())
350 return NULL;
351 MtpResponseCode ret = readResponse();
352 if (ret == MTP_RESPONSE_OK) {
353 return mData.getAUInt32();
354 }
355 return NULL;
356}
357
358MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400359 Mutex::Autolock autoLock(mMutex);
360
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400361 mRequest.reset();
362 mRequest.setParameter(1, storageID);
363 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
364 return NULL;
365 if (!readData())
366 return NULL;
367 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400368 if (ret == MTP_RESPONSE_OK) {
369 MtpStorageInfo* info = new MtpStorageInfo(storageID);
Mike Lockwoodab063842014-11-12 14:20:06 -0800370 if (info->read(mData))
371 return info;
372 else
373 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400374 }
375 return NULL;
376}
377
378MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
379 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400380 Mutex::Autolock autoLock(mMutex);
381
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400382 mRequest.reset();
383 mRequest.setParameter(1, storageID);
384 mRequest.setParameter(2, format);
385 mRequest.setParameter(3, parent);
386 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
387 return NULL;
388 if (!readData())
389 return NULL;
390 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400391 if (ret == MTP_RESPONSE_OK) {
392 return mData.getAUInt32();
393 }
394 return NULL;
395}
396
397MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400398 Mutex::Autolock autoLock(mMutex);
399
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400400 // FIXME - we might want to add some caching here
401
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400402 mRequest.reset();
403 mRequest.setParameter(1, handle);
404 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
405 return NULL;
406 if (!readData())
407 return NULL;
408 MtpResponseCode ret = readResponse();
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400409 if (ret == MTP_RESPONSE_OK) {
410 MtpObjectInfo* info = new MtpObjectInfo(handle);
Mike Lockwoodab063842014-11-12 14:20:06 -0800411 if (info->read(mData))
412 return info;
413 else
414 delete info;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400415 }
416 return NULL;
417}
418
Mike Lockwood3e072b32010-06-10 16:34:20 -0400419void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400420 Mutex::Autolock autoLock(mMutex);
421
Mike Lockwood3e072b32010-06-10 16:34:20 -0400422 mRequest.reset();
423 mRequest.setParameter(1, handle);
424 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
425 MtpResponseCode ret = readResponse();
426 if (ret == MTP_RESPONSE_OK) {
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900427 return mData.getData(&outLength);
Mike Lockwood3e072b32010-06-10 16:34:20 -0400428 }
429 }
430 outLength = 0;
431 return NULL;
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400432}
Mike Lockwood3e072b32010-06-10 16:34:20 -0400433
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400434MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
435 Mutex::Autolock autoLock(mMutex);
436
437 mRequest.reset();
438 MtpObjectHandle parent = info->mParent;
439 if (parent == 0)
440 parent = MTP_PARENT_ROOT;
441
442 mRequest.setParameter(1, info->mStorageID);
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900443 mRequest.setParameter(2, parent);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400444
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900445 mData.reset();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400446 mData.putUInt32(info->mStorageID);
447 mData.putUInt16(info->mFormat);
448 mData.putUInt16(info->mProtectionStatus);
449 mData.putUInt32(info->mCompressedSize);
450 mData.putUInt16(info->mThumbFormat);
451 mData.putUInt32(info->mThumbCompressedSize);
452 mData.putUInt32(info->mThumbPixWidth);
453 mData.putUInt32(info->mThumbPixHeight);
454 mData.putUInt32(info->mImagePixWidth);
455 mData.putUInt32(info->mImagePixHeight);
456 mData.putUInt32(info->mImagePixDepth);
457 mData.putUInt32(info->mParent);
458 mData.putUInt16(info->mAssociationType);
459 mData.putUInt32(info->mAssociationDesc);
460 mData.putUInt32(info->mSequenceNumber);
461 mData.putString(info->mName);
462
463 char created[100], modified[100];
464 formatDateTime(info->mDateCreated, created, sizeof(created));
465 formatDateTime(info->mDateModified, modified, sizeof(modified));
466
467 mData.putString(created);
468 mData.putString(modified);
469 if (info->mKeywords)
470 mData.putString(info->mKeywords);
471 else
472 mData.putEmptyString();
473
474 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400475 MtpResponseCode ret = readResponse();
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400476 if (ret == MTP_RESPONSE_OK) {
477 info->mStorageID = mResponse.getParameter(1);
478 info->mParent = mResponse.getParameter(2);
479 info->mHandle = mResponse.getParameter(3);
480 return info->mHandle;
481 }
482 }
483 return (MtpObjectHandle)-1;
484}
485
Tomasz Mikolajewski532b4f22015-08-25 14:14:36 +0900486bool MtpDevice::sendObject(MtpObjectHandle handle, int size, int srcFD) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400487 Mutex::Autolock autoLock(mMutex);
488
Tomasz Mikolajewski532b4f22015-08-25 14:14:36 +0900489 int remaining = size;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400490 mRequest.reset();
Tomasz Mikolajewski532b4f22015-08-25 14:14:36 +0900491 mRequest.setParameter(1, handle);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400492 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400493 // send data header
494 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
495
Tomasz Mikolajewski532b4f22015-08-25 14:14:36 +0900496 // USB writes greater than 16K don't work
497 char buffer[MTP_BUFFER_SIZE];
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400498 while (remaining > 0) {
499 int count = read(srcFD, buffer, sizeof(buffer));
500 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500501 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400502 // FIXME check error
503 remaining -= count;
504 } else {
505 break;
506 }
507 }
508 }
509 MtpResponseCode ret = readResponse();
510 return (remaining == 0 && ret == MTP_RESPONSE_OK);
511}
512
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400513bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400514 Mutex::Autolock autoLock(mMutex);
515
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400516 mRequest.reset();
517 mRequest.setParameter(1, handle);
518 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
519 MtpResponseCode ret = readResponse();
520 if (ret == MTP_RESPONSE_OK)
521 return true;
522 }
523 return false;
524}
525
526MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
527 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800528 if (info) {
529 MtpObjectHandle parent = info->mParent;
530 delete info;
531 return parent;
532 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400533 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800534 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400535}
536
537MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
538 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800539 if (info) {
540 MtpObjectHandle storageId = info->mStorageID;
541 delete info;
542 return storageId;
543 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400544 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800545 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400546}
547
Mike Lockwood98693f62010-12-07 10:58:56 -0800548MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
549 Mutex::Autolock autoLock(mMutex);
550
551 mRequest.reset();
552 mRequest.setParameter(1, format);
553 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
554 return NULL;
555 if (!readData())
556 return NULL;
557 MtpResponseCode ret = readResponse();
558 if (ret == MTP_RESPONSE_OK) {
559 return mData.getAUInt16();
560 }
561 return NULL;
562
563}
564
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400565MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400566 Mutex::Autolock autoLock(mMutex);
567
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400568 mRequest.reset();
569 mRequest.setParameter(1, code);
570 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
571 return NULL;
572 if (!readData())
573 return NULL;
574 MtpResponseCode ret = readResponse();
575 if (ret == MTP_RESPONSE_OK) {
576 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800577 if (property->read(mData))
578 return property;
579 else
580 delete property;
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400581 }
582 return NULL;
583}
584
Mike Lockwood99e393a2010-12-07 18:53:04 -0800585MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800586 Mutex::Autolock autoLock(mMutex);
587
588 mRequest.reset();
589 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800590 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800591 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
592 return NULL;
593 if (!readData())
594 return NULL;
595 MtpResponseCode ret = readResponse();
596 if (ret == MTP_RESPONSE_OK) {
597 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800598 if (property->read(mData))
599 return property;
600 else
601 delete property;
Mike Lockwood98693f62010-12-07 10:58:56 -0800602 }
603 return NULL;
604}
605
Mike Lockwood23f1b332010-12-30 15:38:45 -0500606bool MtpDevice::readObject(MtpObjectHandle handle,
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900607 ReadObjectCallback callback,
608 size_t expectedLength,
609 void* clientData) {
610 return readObjectInternal(handle, callback, &expectedLength, clientData);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500611}
612
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500613// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500614bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Steve Blockb8a80522011-12-20 16:23:08 +0000615 ALOGD("readObject: %s", destPath);
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700616 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500617 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000618 ALOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400619 return false;
620 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400621
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500622 fchown(fd, getuid(), group);
623 // set permissions
624 int mask = umask(0);
625 fchmod(fd, perm);
626 umask(mask);
627
Tomasz Mikolajewski025ffd92015-08-04 18:38:31 +0900628 bool result = readObject(handle, fd);
629 ::close(fd);
630 return result;
631}
632
633bool MtpDevice::readObject(MtpObjectHandle handle, int fd) {
634 ALOGD("readObject: %d", fd);
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900635 return readObjectInternal(handle, writeToFd, NULL /* expected size */, &fd);
636}
Tomasz Mikolajewski025ffd92015-08-04 18:38:31 +0900637
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900638bool MtpDevice::readObjectInternal(MtpObjectHandle handle,
639 ReadObjectCallback callback,
640 const size_t* expectedLength,
641 void* clientData) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500642 Mutex::Autolock autoLock(mMutex);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400643
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500644 mRequest.reset();
645 mRequest.setParameter(1, handle);
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900646 if (!sendRequest(MTP_OPERATION_GET_OBJECT)) {
647 ALOGE("Failed to send a read request.");
648 return false;
649 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500650
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900651 if (!mData.readDataHeader(mRequestIn1)) {
652 ALOGE("Failed to read header.");
653 return false;
654 }
655
656 const uint32_t fullLength = mData.getContainerLength();
657 if ((!expectedLength && fullLength < MTP_CONTAINER_HEADER_SIZE) ||
658 (expectedLength && *expectedLength + MTP_CONTAINER_HEADER_SIZE != fullLength)) {
659 ALOGE("readObject error length: %d", fullLength);
660 return false;
661 }
662
663 const uint32_t length = fullLength - MTP_CONTAINER_HEADER_SIZE;
664 uint32_t offset = 0;
665 bool writingError = false;
666
667 {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500668 int initialDataLength = 0;
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900669 void* const initialData = mData.getData(&initialDataLength);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500670 if (initialData) {
671 if (initialDataLength > 0) {
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900672 if (!callback(initialData, offset, initialDataLength, clientData)) {
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900673 ALOGE("Failed to write initial data.");
674 writingError = true;
Kenny Root31c52e72011-02-02 13:43:55 -0800675 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900676 offset += initialDataLength;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500677 }
678 free(initialData);
679 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900680 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500681
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900682 // USB reads greater than 16K don't work.
683 char buffer1[MTP_BUFFER_SIZE], buffer2[MTP_BUFFER_SIZE];
684 mRequestIn1->buffer = buffer1;
685 mRequestIn2->buffer = buffer2;
686 struct usb_request* req = NULL;
687
688 while (offset < length) {
689 // Wait for previous read to complete.
Mike Lockwood42d0b792011-01-04 14:48:57 -0500690 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500691 int writeLength = 0;
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900692 if (req) {
693 const int read = mData.readDataWait(mDevice);
694 if (read < 0) {
695 ALOGE("readDataWait failed.");
696 return false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500697 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900698 writeBuffer = req->buffer;
699 writeLength = read;
700 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500701
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900702 // Request to read next chunk.
703 const uint32_t nextOffset = offset + writeLength;
704 if (nextOffset < length) {
705 // Queue up a read request.
706 const size_t remaining = length - nextOffset;
707 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
708 req->buffer_length =
709 remaining > MTP_BUFFER_SIZE ? static_cast<size_t>(MTP_BUFFER_SIZE) : remaining;
710 if (mData.readDataAsync(req) != 0) {
711 ALOGE("readDataAsync failed");
712 return false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500713 }
714 }
715
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900716 // Write previous buffer.
717 if (writeBuffer && !writingError) {
718 if (!callback(writeBuffer, offset, writeLength, clientData)) {
719 ALOGE("write failed");
720 writingError = true;
721 }
722 }
723 offset = nextOffset;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400724 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500725
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900726 return readResponse() == MTP_RESPONSE_OK && !writingError;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400727}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400728
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400729bool MtpDevice::sendRequest(MtpOperationCode operation) {
Steve Block3856b092011-10-20 11:56:00 +0100730 ALOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800731 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400732 mRequest.setOperationCode(operation);
733 if (mTransactionID > 0)
734 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500735 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400736 mRequest.dump();
737 return (ret > 0);
738}
739
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400740bool MtpDevice::sendData() {
Steve Block3856b092011-10-20 11:56:00 +0100741 ALOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400742 mData.setOperationCode(mRequest.getOperationCode());
743 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500744 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400745 mData.dump();
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900746 return (ret >= 0);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400747}
748
749bool MtpDevice::readData() {
750 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500751 int ret = mData.read(mRequestIn1);
Steve Block3856b092011-10-20 11:56:00 +0100752 ALOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400753 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800754 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
Steve Blockb8a80522011-12-20 16:23:08 +0000755 ALOGD("got response packet instead of data packet");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800756 // we got a response packet rather than data
757 // copy it to mResponse
758 mResponse.copyFrom(mData);
759 mReceivedResponse = true;
760 return false;
761 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400762 mData.dump();
763 return true;
764 }
765 else {
Steve Block3856b092011-10-20 11:56:00 +0100766 ALOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400767 return false;
768 }
769}
770
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400771bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
772 mData.setOperationCode(operation);
773 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500774 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400775}
776
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400777MtpResponseCode MtpDevice::readResponse() {
Steve Block3856b092011-10-20 11:56:00 +0100778 ALOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800779 if (mReceivedResponse) {
780 mReceivedResponse = false;
781 return mResponse.getResponseCode();
782 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500783 int ret = mResponse.read(mRequestIn1);
Mike Lockwood3d744572011-03-14 10:33:22 -0400784 // handle zero length packets, which might occur if the data transfer
785 // ends on a packet boundary
786 if (ret == 0)
787 ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400788 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
789 mResponse.dump();
790 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800791 } else {
Steve Blockb8a80522011-12-20 16:23:08 +0000792 ALOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400793 return -1;
794 }
795}
796
797} // namespace android