MtpDevice.cpp revision 0cf89f2e622aa53f31fa5762ca4bc805bb509ed3
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        printf("MTP_OPERATION_SEND_OBJECT_INFO sent\n");
268        MtpResponseCode ret = readResponse();
269        printf("sendObjectInfo response: %04X\n", ret);
270        if (ret == MTP_RESPONSE_OK) {
271            info->mStorageID = mResponse.getParameter(1);
272            info->mParent = mResponse.getParameter(2);
273            info->mHandle = mResponse.getParameter(3);
274            return info->mHandle;
275        }
276    }
277    return (MtpObjectHandle)-1;
278}
279
280bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
281    Mutex::Autolock autoLock(mMutex);
282
283    int remaining = info->mCompressedSize;
284    mRequest.reset();
285    mRequest.setParameter(1, info->mHandle);
286    if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
287        printf("MTP_OPERATION_SEND_OBJECT sent\n");
288        // send data header
289        writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
290
291        char buffer[65536];
292        while (remaining > 0) {
293            int count = read(srcFD, buffer, sizeof(buffer));
294            if (count > 0) {
295                int written = mData.write(mEndpointOut, buffer, count);
296                printf("wrote %d\n", written);
297                // FIXME check error
298                remaining -= count;
299            } else {
300                break;
301            }
302        }
303    }
304    MtpResponseCode ret = readResponse();
305    return (remaining == 0 && ret == MTP_RESPONSE_OK);
306}
307
308bool MtpDevice::deleteObject(MtpObjectHandle handle) {
309    Mutex::Autolock autoLock(mMutex);
310
311    mRequest.reset();
312    mRequest.setParameter(1, handle);
313    if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
314        MtpResponseCode ret = readResponse();
315        if (ret == MTP_RESPONSE_OK)
316            return true;
317    }
318    return false;
319}
320
321MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
322    MtpObjectInfo* info = getObjectInfo(handle);
323    if (info)
324        return info->mParent;
325    else
326        return -1;
327}
328
329MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
330    MtpObjectInfo* info = getObjectInfo(handle);
331    if (info)
332        return info->mStorageID;
333    else
334        return -1;
335}
336
337MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
338    Mutex::Autolock autoLock(mMutex);
339
340    mRequest.reset();
341    mRequest.setParameter(1, code);
342    if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
343        return NULL;
344    if (!readData())
345        return NULL;
346    MtpResponseCode ret = readResponse();
347    if (ret == MTP_RESPONSE_OK) {
348        MtpProperty* property = new MtpProperty;
349        property->read(mData, true);
350        return property;
351    }
352    return NULL;
353}
354
355class ReadObjectThread : public Thread {
356private:
357    MtpDevice*          mDevice;
358    MtpObjectHandle     mHandle;
359    int                 mObjectSize;
360    void*               mInitialData;
361    int                 mInitialDataLength;
362    int                 mFD;
363
364public:
365    ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
366        : mDevice(device),
367          mHandle(handle),
368          mObjectSize(objectSize),
369          mInitialData(NULL),
370          mInitialDataLength(0)
371    {
372    }
373
374    virtual ~ReadObjectThread() {
375        if (mFD >= 0)
376            close(mFD);
377        free(mInitialData);
378    }
379
380    // returns file descriptor
381    int init() {
382        mDevice->mRequest.reset();
383        mDevice->mRequest.setParameter(1, mHandle);
384        if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
385                && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
386
387            // mData will contain header and possibly the beginning of the object data
388            mInitialData = mDevice->mData.getData(mInitialDataLength);
389
390            // create a pipe for the client to read from
391            int pipefd[2];
392            if (pipe(pipefd) < 0) {
393                LOGE("pipe failed (%s)", strerror(errno));
394                return -1;
395            }
396
397            mFD = pipefd[1];
398            return pipefd[0];
399        } else {
400           return -1;
401        }
402    }
403
404    virtual bool threadLoop() {
405        int remaining = mObjectSize;
406        if (mInitialData) {
407            write(mFD, mInitialData, mInitialDataLength);
408            remaining -= mInitialDataLength;
409            free(mInitialData);
410            mInitialData = NULL;
411        }
412
413        char buffer[65536];
414        while (remaining > 0) {
415            int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
416            int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
417            int written;
418            if (count >= 0) {
419                int written = write(mFD, buffer, count);
420                // FIXME check error
421                remaining -= count;
422            } else {
423                break;
424            }
425        }
426
427        MtpResponseCode ret = mDevice->readResponse();
428        mDevice->mMutex.unlock();
429        return false;
430    }
431};
432
433    // returns the file descriptor for a pipe to read the object's data
434int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
435    mMutex.lock();
436
437    ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
438    int fd = thread->init();
439    if (fd < 0) {
440        delete thread;
441        mMutex.unlock();
442    } else {
443        thread->run("ReadObjectThread");
444    }
445    return fd;
446}
447
448bool MtpDevice::sendRequest(MtpOperationCode operation) {
449    LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
450    mRequest.setOperationCode(operation);
451    if (mTransactionID > 0)
452        mRequest.setTransactionID(mTransactionID++);
453    int ret = mRequest.write(mEndpointOut);
454    mRequest.dump();
455    return (ret > 0);
456}
457
458bool MtpDevice::sendData() {
459    LOGD("sendData\n");
460    mData.setOperationCode(mRequest.getOperationCode());
461    mData.setTransactionID(mRequest.getTransactionID());
462    int ret = mData.write(mEndpointOut);
463    mData.dump();
464    return (ret > 0);
465}
466
467bool MtpDevice::readData() {
468    mData.reset();
469    int ret = mData.read(mEndpointIn);
470    LOGD("readData returned %d\n", ret);
471    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
472        mData.dump();
473        return true;
474    }
475    else {
476        LOGD("readResponse failed\n");
477        return false;
478    }
479}
480
481bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
482    mData.setOperationCode(operation);
483    mData.setTransactionID(mRequest.getTransactionID());
484    return (!mData.writeDataHeader(mEndpointOut, dataLength));
485}
486
487MtpResponseCode MtpDevice::readResponse() {
488    LOGD("readResponse\n");
489    int ret = mResponse.read(mEndpointIn);
490    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
491        mResponse.dump();
492        return mResponse.getResponseCode();
493    }
494    else {
495        LOGD("readResponse failed\n");
496        return -1;
497    }
498}
499
500}  // namespace android
501