MtpDevice.cpp revision b9ff444a7eaf7ffd43970c0477110c6808bd4a7c
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) {
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    Mutex::Autolock autoLock(mMutex);
361    bool result = false;
362
363    mRequest.reset();
364    mRequest.setParameter(1, handle);
365    if (sendRequest(MTP_OPERATION_GET_OBJECT)
366            && mData.readDataHeader(mEndpointIn)) {
367        uint32_t length = mData.getContainerLength();
368        if (length < MTP_CONTAINER_HEADER_SIZE)
369            goto fail;
370        length -= MTP_CONTAINER_HEADER_SIZE;
371        uint32_t remaining = length;
372
373        int initialDataLength = 0;
374        void* initialData = mData.getData(initialDataLength);
375        if (initialData) {
376            if (initialDataLength > 0) {
377                if (write(fd, initialData, initialDataLength) != initialDataLength)
378                    goto fail;
379                remaining -= initialDataLength;
380            }
381            free(initialData);
382        }
383
384        // USB reads greater than 16K don't work
385        char buffer1[16384], buffer2[16384];
386        char* readBuffer = buffer1;
387        char* writeBuffer = NULL;
388        int writeLength = 0;
389
390        while (remaining > 0 || writeBuffer) {
391            if (remaining > 0) {
392                // queue up a read request
393                int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
394                if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) {
395                    LOGE("readDataAsync failed");
396                    goto fail;
397                }
398            } else {
399                readBuffer = NULL;
400            }
401
402            if (writeBuffer) {
403                // write previous buffer
404                if (write(fd, writeBuffer, writeLength) != writeLength) {
405                    LOGE("write failed");
406                    // wait for pending read before failing
407                    if (readBuffer)
408                        mData.readDataWait(mEndpointIn);
409                    goto fail;
410                }
411                writeBuffer = NULL;
412            }
413
414            // wait for read to complete
415            if (readBuffer) {
416                int read = mData.readDataWait(mEndpointIn);
417                if (read < 0)
418                    goto fail;
419
420                writeBuffer = readBuffer;
421                writeLength = read;
422                remaining -= read;
423                readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1);
424            }
425        }
426
427        MtpResponseCode response = readResponse();
428        if (response == MTP_RESPONSE_OK)
429            result = true;
430    }
431
432fail:
433    ::close(fd);
434    return result;
435}
436
437bool MtpDevice::sendRequest(MtpOperationCode operation) {
438    LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
439    mRequest.setOperationCode(operation);
440    if (mTransactionID > 0)
441        mRequest.setTransactionID(mTransactionID++);
442    int ret = mRequest.write(mEndpointOut);
443    mRequest.dump();
444    return (ret > 0);
445}
446
447bool MtpDevice::sendData() {
448    LOGV("sendData\n");
449    mData.setOperationCode(mRequest.getOperationCode());
450    mData.setTransactionID(mRequest.getTransactionID());
451    int ret = mData.write(mEndpointOut);
452    mData.dump();
453    return (ret > 0);
454}
455
456bool MtpDevice::readData() {
457    mData.reset();
458    int ret = mData.read(mEndpointIn);
459    LOGV("readData returned %d\n", ret);
460    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
461        mData.dump();
462        return true;
463    }
464    else {
465        LOGV("readResponse failed\n");
466        return false;
467    }
468}
469
470bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
471    mData.setOperationCode(operation);
472    mData.setTransactionID(mRequest.getTransactionID());
473    return (!mData.writeDataHeader(mEndpointOut, dataLength));
474}
475
476MtpResponseCode MtpDevice::readResponse() {
477    LOGV("readResponse\n");
478    int ret = mResponse.read(mEndpointIn);
479    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
480        mResponse.dump();
481        return mResponse.getResponseCode();
482    }
483    else {
484        LOGD("readResponse failed\n");
485        return -1;
486    }
487}
488
489}  // namespace android
490