blob: c70d3a171bd86bb321ff8af0bc7c2987bebb61e1 [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
486bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
487 Mutex::Autolock autoLock(mMutex);
488
489 int remaining = info->mCompressedSize;
490 mRequest.reset();
491 mRequest.setParameter(1, info->mHandle);
492 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
496 char buffer[65536];
497 while (remaining > 0) {
498 int count = read(srcFD, buffer, sizeof(buffer));
499 if (count > 0) {
Mike Lockwood42d0b792011-01-04 14:48:57 -0500500 int written = mData.write(mRequestOut, buffer, count);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400501 // FIXME check error
502 remaining -= count;
503 } else {
504 break;
505 }
506 }
507 }
508 MtpResponseCode ret = readResponse();
509 return (remaining == 0 && ret == MTP_RESPONSE_OK);
510}
511
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400512bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400513 Mutex::Autolock autoLock(mMutex);
514
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400515 mRequest.reset();
516 mRequest.setParameter(1, handle);
517 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
518 MtpResponseCode ret = readResponse();
519 if (ret == MTP_RESPONSE_OK)
520 return true;
521 }
522 return false;
523}
524
525MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
526 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800527 if (info) {
528 MtpObjectHandle parent = info->mParent;
529 delete info;
530 return parent;
531 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400532 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800533 }
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400534}
535
536MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
537 MtpObjectInfo* info = getObjectInfo(handle);
Kenny Root31c52e72011-02-02 13:43:55 -0800538 if (info) {
539 MtpObjectHandle storageId = info->mStorageID;
540 delete info;
541 return storageId;
542 } else {
Mike Lockwood6afc41d2010-06-11 16:34:52 -0400543 return -1;
Kenny Root31c52e72011-02-02 13:43:55 -0800544 }
Mike Lockwood3e072b32010-06-10 16:34:20 -0400545}
546
Mike Lockwood98693f62010-12-07 10:58:56 -0800547MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
548 Mutex::Autolock autoLock(mMutex);
549
550 mRequest.reset();
551 mRequest.setParameter(1, format);
552 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
553 return NULL;
554 if (!readData())
555 return NULL;
556 MtpResponseCode ret = readResponse();
557 if (ret == MTP_RESPONSE_OK) {
558 return mData.getAUInt16();
559 }
560 return NULL;
561
562}
563
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400564MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400565 Mutex::Autolock autoLock(mMutex);
566
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400567 mRequest.reset();
568 mRequest.setParameter(1, code);
569 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
570 return NULL;
571 if (!readData())
572 return NULL;
573 MtpResponseCode ret = readResponse();
574 if (ret == MTP_RESPONSE_OK) {
575 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800576 if (property->read(mData))
577 return property;
578 else
579 delete property;
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400580 }
581 return NULL;
582}
583
Mike Lockwood99e393a2010-12-07 18:53:04 -0800584MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
Mike Lockwood98693f62010-12-07 10:58:56 -0800585 Mutex::Autolock autoLock(mMutex);
586
587 mRequest.reset();
588 mRequest.setParameter(1, code);
Mike Lockwood99e393a2010-12-07 18:53:04 -0800589 mRequest.setParameter(2, format);
Mike Lockwood98693f62010-12-07 10:58:56 -0800590 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
591 return NULL;
592 if (!readData())
593 return NULL;
594 MtpResponseCode ret = readResponse();
595 if (ret == MTP_RESPONSE_OK) {
596 MtpProperty* property = new MtpProperty;
Mike Lockwoodab063842014-11-12 14:20:06 -0800597 if (property->read(mData))
598 return property;
599 else
600 delete property;
Mike Lockwood98693f62010-12-07 10:58:56 -0800601 }
602 return NULL;
603}
604
Mike Lockwood23f1b332010-12-30 15:38:45 -0500605bool MtpDevice::readObject(MtpObjectHandle handle,
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900606 ReadObjectCallback callback,
607 size_t expectedLength,
608 void* clientData) {
609 return readObjectInternal(handle, callback, &expectedLength, clientData);
Mike Lockwood23f1b332010-12-30 15:38:45 -0500610}
611
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500612// reads the object's data and writes it to the specified file path
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500613bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Steve Blockb8a80522011-12-20 16:23:08 +0000614 ALOGD("readObject: %s", destPath);
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700615 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500616 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000617 ALOGE("open failed for %s", destPath);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400618 return false;
619 }
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400620
Mike Lockwood27afe3a2010-11-19 13:52:20 -0500621 fchown(fd, getuid(), group);
622 // set permissions
623 int mask = umask(0);
624 fchmod(fd, perm);
625 umask(mask);
626
Tomasz Mikolajewski025ffd92015-08-04 18:38:31 +0900627 bool result = readObject(handle, fd);
628 ::close(fd);
629 return result;
630}
631
632bool MtpDevice::readObject(MtpObjectHandle handle, int fd) {
633 ALOGD("readObject: %d", fd);
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900634 return readObjectInternal(handle, writeToFd, NULL /* expected size */, &fd);
635}
Tomasz Mikolajewski025ffd92015-08-04 18:38:31 +0900636
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900637bool MtpDevice::readObjectInternal(MtpObjectHandle handle,
638 ReadObjectCallback callback,
639 const size_t* expectedLength,
640 void* clientData) {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500641 Mutex::Autolock autoLock(mMutex);
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400642
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500643 mRequest.reset();
644 mRequest.setParameter(1, handle);
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900645 if (!sendRequest(MTP_OPERATION_GET_OBJECT)) {
646 ALOGE("Failed to send a read request.");
647 return false;
648 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500649
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900650 if (!mData.readDataHeader(mRequestIn1)) {
651 ALOGE("Failed to read header.");
652 return false;
653 }
654
655 const uint32_t fullLength = mData.getContainerLength();
656 if ((!expectedLength && fullLength < MTP_CONTAINER_HEADER_SIZE) ||
657 (expectedLength && *expectedLength + MTP_CONTAINER_HEADER_SIZE != fullLength)) {
658 ALOGE("readObject error length: %d", fullLength);
659 return false;
660 }
661
662 const uint32_t length = fullLength - MTP_CONTAINER_HEADER_SIZE;
663 uint32_t offset = 0;
664 bool writingError = false;
665
666 {
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500667 int initialDataLength = 0;
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900668 void* const initialData = mData.getData(&initialDataLength);
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500669 if (initialData) {
670 if (initialDataLength > 0) {
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900671 if (!callback(initialData, offset, initialDataLength, clientData)) {
Daichi Hirono81ca5ad2015-08-18 21:13:40 +0900672 ALOGE("Failed to write initial data.");
673 writingError = true;
Kenny Root31c52e72011-02-02 13:43:55 -0800674 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900675 offset += initialDataLength;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500676 }
677 free(initialData);
678 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900679 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500680
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900681 // USB reads greater than 16K don't work.
682 char buffer1[MTP_BUFFER_SIZE], buffer2[MTP_BUFFER_SIZE];
683 mRequestIn1->buffer = buffer1;
684 mRequestIn2->buffer = buffer2;
685 struct usb_request* req = NULL;
686
687 while (offset < length) {
688 // Wait for previous read to complete.
Mike Lockwood42d0b792011-01-04 14:48:57 -0500689 void* writeBuffer = NULL;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500690 int writeLength = 0;
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900691 if (req) {
692 const int read = mData.readDataWait(mDevice);
693 if (read < 0) {
694 ALOGE("readDataWait failed.");
695 return false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500696 }
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900697 writeBuffer = req->buffer;
698 writeLength = read;
699 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500700
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900701 // Request to read next chunk.
702 const uint32_t nextOffset = offset + writeLength;
703 if (nextOffset < length) {
704 // Queue up a read request.
705 const size_t remaining = length - nextOffset;
706 req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
707 req->buffer_length =
708 remaining > MTP_BUFFER_SIZE ? static_cast<size_t>(MTP_BUFFER_SIZE) : remaining;
709 if (mData.readDataAsync(req) != 0) {
710 ALOGE("readDataAsync failed");
711 return false;
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500712 }
713 }
714
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900715 // Write previous buffer.
716 if (writeBuffer && !writingError) {
717 if (!callback(writeBuffer, offset, writeLength, clientData)) {
718 ALOGE("write failed");
719 writingError = true;
720 }
721 }
722 offset = nextOffset;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400723 }
Mike Lockwoodb9ff4442010-11-19 11:20:19 -0500724
Daichi Hirono4fd9a8b2015-08-20 15:13:40 +0900725 return readResponse() == MTP_RESPONSE_OK && !writingError;
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400726}
Mike Lockwooda6c490b2010-06-05 22:45:01 -0400727
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400728bool MtpDevice::sendRequest(MtpOperationCode operation) {
Steve Block3856b092011-10-20 11:56:00 +0100729 ALOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwoodf7454622010-12-09 18:34:18 -0800730 mReceivedResponse = false;
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400731 mRequest.setOperationCode(operation);
732 if (mTransactionID > 0)
733 mRequest.setTransactionID(mTransactionID++);
Mike Lockwood42d0b792011-01-04 14:48:57 -0500734 int ret = mRequest.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400735 mRequest.dump();
736 return (ret > 0);
737}
738
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400739bool MtpDevice::sendData() {
Steve Block3856b092011-10-20 11:56:00 +0100740 ALOGV("sendData\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400741 mData.setOperationCode(mRequest.getOperationCode());
742 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500743 int ret = mData.write(mRequestOut);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400744 mData.dump();
Tomasz Mikolajewski64c948b2015-08-13 15:31:02 +0900745 return (ret >= 0);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400746}
747
748bool MtpDevice::readData() {
749 mData.reset();
Mike Lockwood42d0b792011-01-04 14:48:57 -0500750 int ret = mData.read(mRequestIn1);
Steve Block3856b092011-10-20 11:56:00 +0100751 ALOGV("readData returned %d\n", ret);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400752 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
Mike Lockwoodf7454622010-12-09 18:34:18 -0800753 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
Steve Blockb8a80522011-12-20 16:23:08 +0000754 ALOGD("got response packet instead of data packet");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800755 // we got a response packet rather than data
756 // copy it to mResponse
757 mResponse.copyFrom(mData);
758 mReceivedResponse = true;
759 return false;
760 }
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400761 mData.dump();
762 return true;
763 }
764 else {
Steve Block3856b092011-10-20 11:56:00 +0100765 ALOGV("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400766 return false;
767 }
768}
769
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400770bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
771 mData.setOperationCode(operation);
772 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood42d0b792011-01-04 14:48:57 -0500773 return (!mData.writeDataHeader(mRequestOut, dataLength));
Mike Lockwood0cf89f22010-07-26 20:40:45 -0400774}
775
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400776MtpResponseCode MtpDevice::readResponse() {
Steve Block3856b092011-10-20 11:56:00 +0100777 ALOGV("readResponse\n");
Mike Lockwoodf7454622010-12-09 18:34:18 -0800778 if (mReceivedResponse) {
779 mReceivedResponse = false;
780 return mResponse.getResponseCode();
781 }
Mike Lockwood42d0b792011-01-04 14:48:57 -0500782 int ret = mResponse.read(mRequestIn1);
Mike Lockwood3d744572011-03-14 10:33:22 -0400783 // handle zero length packets, which might occur if the data transfer
784 // ends on a packet boundary
785 if (ret == 0)
786 ret = mResponse.read(mRequestIn1);
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400787 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
788 mResponse.dump();
789 return mResponse.getResponseCode();
Mike Lockwoodf7454622010-12-09 18:34:18 -0800790 } else {
Steve Blockb8a80522011-12-20 16:23:08 +0000791 ALOGD("readResponse failed\n");
Mike Lockwood5ed68d22010-05-25 19:08:48 -0400792 return -1;
793 }
794}
795
796} // namespace android