MtpDevice.cpp revision 27afe3a1cc9c07ac4884a7720087a597aea3c76f
1/*
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
17#define LOG_TAG "MtpDevice"
18
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"
26#include "MtpUtils.h"
27
28#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>
35#include <endian.h>
36
37#include <usbhost/usbhost.h>
38
39namespace 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();
58    for (int i = 0; i < mDeviceProperties.size(); i++)
59        delete mDeviceProperties[i];
60}
61
62void MtpDevice::initialize() {
63    openSession();
64    mDeviceInfo = getDeviceInfo();
65    if (mDeviceInfo) {
66        mDeviceInfo->print();
67
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        }
79    }
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() {
98    Mutex::Autolock autoLock(mMutex);
99
100    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() {
124    Mutex::Autolock autoLock(mMutex);
125
126    mRequest.reset();
127    if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
128        return NULL;
129    if (!readData())
130        return NULL;
131    MtpResponseCode ret = readResponse();
132    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() {
141    Mutex::Autolock autoLock(mMutex);
142
143    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) {
156    Mutex::Autolock autoLock(mMutex);
157
158    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();
165    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) {
175    Mutex::Autolock autoLock(mMutex);
176
177    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();
186    if (ret == MTP_RESPONSE_OK) {
187        return mData.getAUInt32();
188    }
189    return NULL;
190}
191
192MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
193    Mutex::Autolock autoLock(mMutex);
194
195    // FIXME - we might want to add some caching here
196
197    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();
204    if (ret == MTP_RESPONSE_OK) {
205        MtpObjectInfo* info = new MtpObjectInfo(handle);
206        info->read(mData);
207        return info;
208    }
209    return NULL;
210}
211
212void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
213    Mutex::Autolock autoLock(mMutex);
214
215    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;
225}
226
227MtpObjectHandle 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()) {
267        MtpResponseCode ret = readResponse();
268        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)) {
285        // 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);
293                // 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
304bool MtpDevice::deleteObject(MtpObjectHandle handle) {
305    Mutex::Autolock autoLock(mMutex);
306
307    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;
331}
332
333MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
334    Mutex::Autolock autoLock(mMutex);
335
336    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;
345        property->read(mData);
346        return property;
347    }
348    return NULL;
349}
350
351// reads the object's data and writes it to the specified file path
352bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
353    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);
357        return false;
358    }
359
360    fchown(fd, getuid(), group);
361    // set permissions
362    int mask = umask(0);
363    fchmod(fd, perm);
364    umask(mask);
365
366    Mutex::Autolock autoLock(mMutex);
367    bool result = false;
368
369    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;
436    }
437
438fail:
439    ::close(fd);
440    return result;
441}
442
443bool MtpDevice::sendRequest(MtpOperationCode operation) {
444    LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
445    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
453bool MtpDevice::sendData() {
454    LOGV("sendData\n");
455    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);
465    LOGV("readData returned %d\n", ret);
466    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
467        mData.dump();
468        return true;
469    }
470    else {
471        LOGV("readResponse failed\n");
472        return false;
473    }
474}
475
476bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
477    mData.setOperationCode(operation);
478    mData.setTransactionID(mRequest.getTransactionID());
479    return (!mData.writeDataHeader(mEndpointOut, dataLength));
480}
481
482MtpResponseCode MtpDevice::readResponse() {
483    LOGV("readResponse\n");
484    int ret = mResponse.read(mEndpointIn);
485    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
486        mResponse.dump();
487        return mResponse.getResponseCode();
488    }
489    else {
490        LOGD("readResponse failed\n");
491        return -1;
492    }
493}
494
495}  // namespace android
496