blob: 416ebfe059b05db6e2cfe182c5be13b561f66edf [file] [log] [blame]
Mike Lockwood755fd612010-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 Lockwood90f48732010-06-05 22:45:01 -040017#define LOG_TAG "MtpDevice"
Mike Lockwood3e6616d2010-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 Lockwoodbc4cb0b2010-07-26 20:40:45 -040026#include "MtpUtils.h"
Mike Lockwood90f48732010-06-05 22:45:01 -040027
Mike Lockwood755fd612010-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 Lockwoodbc4cb0b2010-07-26 20:40:45 -040035#include <endian.h>
Mike Lockwood755fd612010-05-25 19:08:48 -040036
37#include <usbhost/usbhost.h>
38
Mike Lockwood755fd612010-05-25 19:08:48 -040039namespace android {
40
41MtpDevice::MtpDevice(struct usb_device* device, int interface,
42 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
43 struct usb_endpoint *ep_intr)
44 : mDevice(device),
45 mInterface(interface),
46 mEndpointIn(ep_in),
47 mEndpointOut(ep_out),
48 mEndpointIntr(ep_intr),
49 mDeviceInfo(NULL),
50 mID(usb_device_get_unique_id(device)),
51 mSessionID(0),
52 mTransactionID(0)
53{
54}
55
56MtpDevice::~MtpDevice() {
57 close();
Mike Lockwood90f48732010-06-05 22:45:01 -040058 for (int i = 0; i < mDeviceProperties.size(); i++)
59 delete mDeviceProperties[i];
Mike Lockwood755fd612010-05-25 19:08:48 -040060}
61
62void MtpDevice::initialize() {
63 openSession();
64 mDeviceInfo = getDeviceInfo();
65 if (mDeviceInfo) {
66 mDeviceInfo->print();
Mike Lockwood90f48732010-06-05 22:45:01 -040067
68 if (mDeviceInfo->mDeviceProperties) {
69 int count = mDeviceInfo->mDeviceProperties->size();
70 for (int i = 0; i < count; i++) {
71 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
72 MtpProperty* property = getDevicePropDesc(propCode);
73 if (property) {
74 property->print();
75 mDeviceProperties.push(property);
76 }
77 }
78 }
Mike Lockwood755fd612010-05-25 19:08:48 -040079 }
80}
81
82void MtpDevice::close() {
83 if (mDevice) {
84 usb_device_release_interface(mDevice, mInterface);
85 usb_device_close(mDevice);
86 mDevice = NULL;
87 }
88}
89
90const char* MtpDevice::getDeviceName() {
91 if (mDevice)
92 return usb_device_get_name(mDevice);
93 else
94 return "???";
95}
96
97bool MtpDevice::openSession() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -040098 Mutex::Autolock autoLock(mMutex);
99
Mike Lockwood755fd612010-05-25 19:08:48 -0400100 mSessionID = 0;
101 mTransactionID = 0;
102 MtpSessionID newSession = 1;
103 mRequest.reset();
104 mRequest.setParameter(1, newSession);
105 if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
106 return false;
107 MtpResponseCode ret = readResponse();
108 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
109 newSession = mResponse.getParameter(1);
110 else if (ret != MTP_RESPONSE_OK)
111 return false;
112
113 mSessionID = newSession;
114 mTransactionID = 1;
115 return true;
116}
117
118bool MtpDevice::closeSession() {
119 // FIXME
120 return true;
121}
122
123MtpDeviceInfo* MtpDevice::getDeviceInfo() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400124 Mutex::Autolock autoLock(mMutex);
125
Mike Lockwood755fd612010-05-25 19:08:48 -0400126 mRequest.reset();
127 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
128 return NULL;
129 if (!readData())
130 return NULL;
131 MtpResponseCode ret = readResponse();
Mike Lockwood755fd612010-05-25 19:08:48 -0400132 if (ret == MTP_RESPONSE_OK) {
133 MtpDeviceInfo* info = new MtpDeviceInfo;
134 info->read(mData);
135 return info;
136 }
137 return NULL;
138}
139
140MtpStorageIDList* MtpDevice::getStorageIDs() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400141 Mutex::Autolock autoLock(mMutex);
142
Mike Lockwood755fd612010-05-25 19:08:48 -0400143 mRequest.reset();
144 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
145 return NULL;
146 if (!readData())
147 return NULL;
148 MtpResponseCode ret = readResponse();
149 if (ret == MTP_RESPONSE_OK) {
150 return mData.getAUInt32();
151 }
152 return NULL;
153}
154
155MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400156 Mutex::Autolock autoLock(mMutex);
157
Mike Lockwood755fd612010-05-25 19:08:48 -0400158 mRequest.reset();
159 mRequest.setParameter(1, storageID);
160 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
161 return NULL;
162 if (!readData())
163 return NULL;
164 MtpResponseCode ret = readResponse();
Mike Lockwood755fd612010-05-25 19:08:48 -0400165 if (ret == MTP_RESPONSE_OK) {
166 MtpStorageInfo* info = new MtpStorageInfo(storageID);
167 info->read(mData);
168 return info;
169 }
170 return NULL;
171}
172
173MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
174 MtpObjectFormat format, MtpObjectHandle parent) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400175 Mutex::Autolock autoLock(mMutex);
176
Mike Lockwood755fd612010-05-25 19:08:48 -0400177 mRequest.reset();
178 mRequest.setParameter(1, storageID);
179 mRequest.setParameter(2, format);
180 mRequest.setParameter(3, parent);
181 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
182 return NULL;
183 if (!readData())
184 return NULL;
185 MtpResponseCode ret = readResponse();
Mike Lockwood755fd612010-05-25 19:08:48 -0400186 if (ret == MTP_RESPONSE_OK) {
187 return mData.getAUInt32();
188 }
189 return NULL;
190}
191
192MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400193 Mutex::Autolock autoLock(mMutex);
194
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400195 // FIXME - we might want to add some caching here
196
Mike Lockwood755fd612010-05-25 19:08:48 -0400197 mRequest.reset();
198 mRequest.setParameter(1, handle);
199 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
200 return NULL;
201 if (!readData())
202 return NULL;
203 MtpResponseCode ret = readResponse();
Mike Lockwood755fd612010-05-25 19:08:48 -0400204 if (ret == MTP_RESPONSE_OK) {
205 MtpObjectInfo* info = new MtpObjectInfo(handle);
206 info->read(mData);
207 return info;
208 }
209 return NULL;
210}
211
Mike Lockwooddda56862010-06-10 16:34:20 -0400212void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400213 Mutex::Autolock autoLock(mMutex);
214
Mike Lockwooddda56862010-06-10 16:34:20 -0400215 mRequest.reset();
216 mRequest.setParameter(1, handle);
217 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
218 MtpResponseCode ret = readResponse();
219 if (ret == MTP_RESPONSE_OK) {
220 return mData.getData(outLength);
221 }
222 }
223 outLength = 0;
224 return NULL;
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400225}
Mike Lockwooddda56862010-06-10 16:34:20 -0400226
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400227MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
228 Mutex::Autolock autoLock(mMutex);
229
230 mRequest.reset();
231 MtpObjectHandle parent = info->mParent;
232 if (parent == 0)
233 parent = MTP_PARENT_ROOT;
234
235 mRequest.setParameter(1, info->mStorageID);
236 mRequest.setParameter(2, info->mParent);
237
238 mData.putUInt32(info->mStorageID);
239 mData.putUInt16(info->mFormat);
240 mData.putUInt16(info->mProtectionStatus);
241 mData.putUInt32(info->mCompressedSize);
242 mData.putUInt16(info->mThumbFormat);
243 mData.putUInt32(info->mThumbCompressedSize);
244 mData.putUInt32(info->mThumbPixWidth);
245 mData.putUInt32(info->mThumbPixHeight);
246 mData.putUInt32(info->mImagePixWidth);
247 mData.putUInt32(info->mImagePixHeight);
248 mData.putUInt32(info->mImagePixDepth);
249 mData.putUInt32(info->mParent);
250 mData.putUInt16(info->mAssociationType);
251 mData.putUInt32(info->mAssociationDesc);
252 mData.putUInt32(info->mSequenceNumber);
253 mData.putString(info->mName);
254
255 char created[100], modified[100];
256 formatDateTime(info->mDateCreated, created, sizeof(created));
257 formatDateTime(info->mDateModified, modified, sizeof(modified));
258
259 mData.putString(created);
260 mData.putString(modified);
261 if (info->mKeywords)
262 mData.putString(info->mKeywords);
263 else
264 mData.putEmptyString();
265
266 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400267 MtpResponseCode ret = readResponse();
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400268 if (ret == MTP_RESPONSE_OK) {
269 info->mStorageID = mResponse.getParameter(1);
270 info->mParent = mResponse.getParameter(2);
271 info->mHandle = mResponse.getParameter(3);
272 return info->mHandle;
273 }
274 }
275 return (MtpObjectHandle)-1;
276}
277
278bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
279 Mutex::Autolock autoLock(mMutex);
280
281 int remaining = info->mCompressedSize;
282 mRequest.reset();
283 mRequest.setParameter(1, info->mHandle);
284 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400285 // send data header
286 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
287
288 char buffer[65536];
289 while (remaining > 0) {
290 int count = read(srcFD, buffer, sizeof(buffer));
291 if (count > 0) {
292 int written = mData.write(mEndpointOut, buffer, count);
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400293 // FIXME check error
294 remaining -= count;
295 } else {
296 break;
297 }
298 }
299 }
300 MtpResponseCode ret = readResponse();
301 return (remaining == 0 && ret == MTP_RESPONSE_OK);
302}
303
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400304bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400305 Mutex::Autolock autoLock(mMutex);
306
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400307 mRequest.reset();
308 mRequest.setParameter(1, handle);
309 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
310 MtpResponseCode ret = readResponse();
311 if (ret == MTP_RESPONSE_OK)
312 return true;
313 }
314 return false;
315}
316
317MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
318 MtpObjectInfo* info = getObjectInfo(handle);
319 if (info)
320 return info->mParent;
321 else
322 return -1;
323}
324
325MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
326 MtpObjectInfo* info = getObjectInfo(handle);
327 if (info)
328 return info->mStorageID;
329 else
330 return -1;
Mike Lockwooddda56862010-06-10 16:34:20 -0400331}
332
Mike Lockwood90f48732010-06-05 22:45:01 -0400333MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400334 Mutex::Autolock autoLock(mMutex);
335
Mike Lockwood90f48732010-06-05 22:45:01 -0400336 mRequest.reset();
337 mRequest.setParameter(1, code);
338 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
339 return NULL;
340 if (!readData())
341 return NULL;
342 MtpResponseCode ret = readResponse();
343 if (ret == MTP_RESPONSE_OK) {
344 MtpProperty* property = new MtpProperty;
Mike Lockwood59e3f0d2010-09-02 14:57:30 -0400345 property->read(mData);
Mike Lockwood90f48732010-06-05 22:45:01 -0400346 return property;
347 }
348 return NULL;
349}
350
Mike Lockwood954c2672010-11-19 11:20:19 -0500351// reads the object's data and writes it to the specified file path
Mike Lockwood929b3da2010-11-19 13:52:20 -0500352bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
Mike Lockwood954c2672010-11-19 11:20:19 -0500353 LOGD("readObject: %s", destPath);
354 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
355 if (fd < 0) {
356 LOGE("open failed for %s", destPath);
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400357 return false;
358 }
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400359
Mike Lockwood929b3da2010-11-19 13:52:20 -0500360 fchown(fd, getuid(), group);
361 // set permissions
362 int mask = umask(0);
363 fchmod(fd, perm);
364 umask(mask);
365
Mike Lockwood954c2672010-11-19 11:20:19 -0500366 Mutex::Autolock autoLock(mMutex);
367 bool result = false;
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400368
Mike Lockwood954c2672010-11-19 11:20:19 -0500369 mRequest.reset();
370 mRequest.setParameter(1, handle);
371 if (sendRequest(MTP_OPERATION_GET_OBJECT)
372 && mData.readDataHeader(mEndpointIn)) {
373 uint32_t length = mData.getContainerLength();
374 if (length < MTP_CONTAINER_HEADER_SIZE)
375 goto fail;
376 length -= MTP_CONTAINER_HEADER_SIZE;
377 uint32_t remaining = length;
378
379 int initialDataLength = 0;
380 void* initialData = mData.getData(initialDataLength);
381 if (initialData) {
382 if (initialDataLength > 0) {
383 if (write(fd, initialData, initialDataLength) != initialDataLength)
384 goto fail;
385 remaining -= initialDataLength;
386 }
387 free(initialData);
388 }
389
390 // USB reads greater than 16K don't work
391 char buffer1[16384], buffer2[16384];
392 char* readBuffer = buffer1;
393 char* writeBuffer = NULL;
394 int writeLength = 0;
395
396 while (remaining > 0 || writeBuffer) {
397 if (remaining > 0) {
398 // queue up a read request
399 int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
400 if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) {
401 LOGE("readDataAsync failed");
402 goto fail;
403 }
404 } else {
405 readBuffer = NULL;
406 }
407
408 if (writeBuffer) {
409 // write previous buffer
410 if (write(fd, writeBuffer, writeLength) != writeLength) {
411 LOGE("write failed");
412 // wait for pending read before failing
413 if (readBuffer)
414 mData.readDataWait(mEndpointIn);
415 goto fail;
416 }
417 writeBuffer = NULL;
418 }
419
420 // wait for read to complete
421 if (readBuffer) {
422 int read = mData.readDataWait(mEndpointIn);
423 if (read < 0)
424 goto fail;
425
426 writeBuffer = readBuffer;
427 writeLength = read;
428 remaining -= read;
429 readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1);
430 }
431 }
432
433 MtpResponseCode response = readResponse();
434 if (response == MTP_RESPONSE_OK)
435 result = true;
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400436 }
Mike Lockwood954c2672010-11-19 11:20:19 -0500437
438fail:
439 ::close(fd);
440 return result;
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400441}
Mike Lockwood90f48732010-06-05 22:45:01 -0400442
Mike Lockwood755fd612010-05-25 19:08:48 -0400443bool MtpDevice::sendRequest(MtpOperationCode operation) {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400444 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwood755fd612010-05-25 19:08:48 -0400445 mRequest.setOperationCode(operation);
446 if (mTransactionID > 0)
447 mRequest.setTransactionID(mTransactionID++);
448 int ret = mRequest.write(mEndpointOut);
449 mRequest.dump();
450 return (ret > 0);
451}
452
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400453bool MtpDevice::sendData() {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400454 LOGV("sendData\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400455 mData.setOperationCode(mRequest.getOperationCode());
456 mData.setTransactionID(mRequest.getTransactionID());
457 int ret = mData.write(mEndpointOut);
458 mData.dump();
459 return (ret > 0);
460}
461
462bool MtpDevice::readData() {
463 mData.reset();
464 int ret = mData.read(mEndpointIn);
Mike Lockwood456d8e62010-07-27 11:50:34 -0400465 LOGV("readData returned %d\n", ret);
Mike Lockwood755fd612010-05-25 19:08:48 -0400466 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
467 mData.dump();
468 return true;
469 }
470 else {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400471 LOGV("readResponse failed\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400472 return false;
473 }
474}
475
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400476bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
477 mData.setOperationCode(operation);
478 mData.setTransactionID(mRequest.getTransactionID());
479 return (!mData.writeDataHeader(mEndpointOut, dataLength));
480}
481
Mike Lockwood755fd612010-05-25 19:08:48 -0400482MtpResponseCode MtpDevice::readResponse() {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400483 LOGV("readResponse\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400484 int ret = mResponse.read(mEndpointIn);
485 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
486 mResponse.dump();
487 return mResponse.getResponseCode();
488 }
489 else {
Mike Lockwood90f48732010-06-05 22:45:01 -0400490 LOGD("readResponse failed\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400491 return -1;
492 }
493}
494
495} // namespace android