MtpDevice.cpp revision da90dff17b6ce79d0a7d8ab2dbe2aa69cd1c11aa
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, true);
346        return property;
347    }
348    return NULL;
349}
350
351class ReadObjectThread : public Thread {
352private:
353    MtpDevice*          mDevice;
354    MtpObjectHandle     mHandle;
355    int                 mObjectSize;
356    void*               mInitialData;
357    int                 mInitialDataLength;
358    int                 mFD;
359
360public:
361    ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
362        : mDevice(device),
363          mHandle(handle),
364          mObjectSize(objectSize),
365          mInitialData(NULL),
366          mInitialDataLength(0)
367    {
368    }
369
370    virtual ~ReadObjectThread() {
371        if (mFD >= 0)
372            close(mFD);
373        free(mInitialData);
374    }
375
376    // returns file descriptor
377    int init() {
378        mDevice->mRequest.reset();
379        mDevice->mRequest.setParameter(1, mHandle);
380        if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
381                && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
382
383            // mData will contain header and possibly the beginning of the object data
384            mInitialData = mDevice->mData.getData(mInitialDataLength);
385
386            // create a pipe for the client to read from
387            int pipefd[2];
388            if (pipe(pipefd) < 0) {
389                LOGE("pipe failed (%s)", strerror(errno));
390                return -1;
391            }
392
393            mFD = pipefd[1];
394            return pipefd[0];
395        } else {
396           return -1;
397        }
398    }
399
400    virtual bool threadLoop() {
401        int remaining = mObjectSize;
402        if (mInitialData) {
403            write(mFD, mInitialData, mInitialDataLength);
404            remaining -= mInitialDataLength;
405            free(mInitialData);
406            mInitialData = NULL;
407        }
408
409        char buffer[16384];
410        while (remaining > 0) {
411            int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
412            int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
413            int written;
414            if (count >= 0) {
415                int written = write(mFD, buffer, count);
416                // FIXME check error
417                remaining -= count;
418            } else {
419                break;
420            }
421        }
422
423        MtpResponseCode ret = mDevice->readResponse();
424        mDevice->mMutex.unlock();
425        return false;
426    }
427};
428
429    // returns the file descriptor for a pipe to read the object's data
430int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
431    mMutex.lock();
432
433    ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
434    int fd = thread->init();
435    if (fd < 0) {
436        delete thread;
437        mMutex.unlock();
438    } else {
439        thread->run("ReadObjectThread");
440    }
441    return fd;
442}
443
444bool MtpDevice::sendRequest(MtpOperationCode operation) {
445    LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
446    mRequest.setOperationCode(operation);
447    if (mTransactionID > 0)
448        mRequest.setTransactionID(mTransactionID++);
449    int ret = mRequest.write(mEndpointOut);
450    mRequest.dump();
451    return (ret > 0);
452}
453
454bool MtpDevice::sendData() {
455    LOGV("sendData\n");
456    mData.setOperationCode(mRequest.getOperationCode());
457    mData.setTransactionID(mRequest.getTransactionID());
458    int ret = mData.write(mEndpointOut);
459    mData.dump();
460    return (ret > 0);
461}
462
463bool MtpDevice::readData() {
464    mData.reset();
465    int ret = mData.read(mEndpointIn);
466    LOGV("readData returned %d\n", ret);
467    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
468        mData.dump();
469        return true;
470    }
471    else {
472        LOGV("readResponse failed\n");
473        return false;
474    }
475}
476
477bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
478    mData.setOperationCode(operation);
479    mData.setTransactionID(mRequest.getTransactionID());
480    return (!mData.writeDataHeader(mEndpointOut, dataLength));
481}
482
483MtpResponseCode MtpDevice::readResponse() {
484    LOGV("readResponse\n");
485    int ret = mResponse.read(mEndpointIn);
486    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
487        mResponse.dump();
488        return mResponse.getResponseCode();
489    }
490    else {
491        LOGD("readResponse failed\n");
492        return -1;
493    }
494}
495
496}  // namespace android
497