MtpDevice.cpp revision 99e393a39a31bfbdeb435462939519e2d0279433
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        if (mDeviceInfo->mDeviceProperties) {
67            int count = mDeviceInfo->mDeviceProperties->size();
68            for (int i = 0; i < count; i++) {
69                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
70                MtpProperty* property = getDevicePropDesc(propCode);
71                if (property)
72                    mDeviceProperties.push(property);
73            }
74        }
75    }
76}
77
78void MtpDevice::close() {
79    if (mDevice) {
80        usb_device_release_interface(mDevice, mInterface);
81        usb_device_close(mDevice);
82        mDevice = NULL;
83    }
84}
85
86void MtpDevice::print() {
87    if (mDeviceInfo) {
88        mDeviceInfo->print();
89
90        if (mDeviceInfo->mDeviceProperties) {
91            LOGI("***** DEVICE PROPERTIES *****\n");
92            int count = mDeviceInfo->mDeviceProperties->size();
93            for (int i = 0; i < count; i++) {
94                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
95                MtpProperty* property = getDevicePropDesc(propCode);
96                if (property) {
97                    property->print();
98                }
99            }
100        }
101    }
102
103    if (mDeviceInfo->mPlaybackFormats) {
104            LOGI("***** OBJECT PROPERTIES *****\n");
105        int count = mDeviceInfo->mPlaybackFormats->size();
106        for (int i = 0; i < count; i++) {
107            MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
108            LOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
109            MtpObjectPropertyList* props = getObjectPropsSupported(format);
110            if (props) {
111                for (int j = 0; j < props->size(); j++) {
112                    MtpObjectProperty prop = (*props)[j];
113                    MtpProperty* property = getObjectPropDesc(prop, format);
114                    if (property)
115                        property->print();
116                    else
117                        LOGE("could not fetch property: %s",
118                                MtpDebug::getObjectPropCodeName(prop));
119                }
120            }
121        }
122    }
123}
124
125const char* MtpDevice::getDeviceName() {
126    if (mDevice)
127        return usb_device_get_name(mDevice);
128    else
129        return "???";
130}
131
132bool MtpDevice::openSession() {
133    Mutex::Autolock autoLock(mMutex);
134
135    mSessionID = 0;
136    mTransactionID = 0;
137    MtpSessionID newSession = 1;
138    mRequest.reset();
139    mRequest.setParameter(1, newSession);
140    if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
141        return false;
142    MtpResponseCode ret = readResponse();
143    if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
144        newSession = mResponse.getParameter(1);
145    else if (ret != MTP_RESPONSE_OK)
146        return false;
147
148    mSessionID = newSession;
149    mTransactionID = 1;
150    return true;
151}
152
153bool MtpDevice::closeSession() {
154    // FIXME
155    return true;
156}
157
158MtpDeviceInfo* MtpDevice::getDeviceInfo() {
159    Mutex::Autolock autoLock(mMutex);
160
161    mRequest.reset();
162    if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
163        return NULL;
164    if (!readData())
165        return NULL;
166    MtpResponseCode ret = readResponse();
167    if (ret == MTP_RESPONSE_OK) {
168        MtpDeviceInfo* info = new MtpDeviceInfo;
169        info->read(mData);
170        return info;
171    }
172    return NULL;
173}
174
175MtpStorageIDList* MtpDevice::getStorageIDs() {
176    Mutex::Autolock autoLock(mMutex);
177
178    mRequest.reset();
179    if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
180        return NULL;
181    if (!readData())
182        return NULL;
183    MtpResponseCode ret = readResponse();
184    if (ret == MTP_RESPONSE_OK) {
185        return mData.getAUInt32();
186    }
187    return NULL;
188}
189
190MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
191    Mutex::Autolock autoLock(mMutex);
192
193    mRequest.reset();
194    mRequest.setParameter(1, storageID);
195    if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
196        return NULL;
197    if (!readData())
198        return NULL;
199    MtpResponseCode ret = readResponse();
200    if (ret == MTP_RESPONSE_OK) {
201        MtpStorageInfo* info = new MtpStorageInfo(storageID);
202        info->read(mData);
203        return info;
204    }
205    return NULL;
206}
207
208MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
209            MtpObjectFormat format, MtpObjectHandle parent) {
210    Mutex::Autolock autoLock(mMutex);
211
212    mRequest.reset();
213    mRequest.setParameter(1, storageID);
214    mRequest.setParameter(2, format);
215    mRequest.setParameter(3, parent);
216    if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
217        return NULL;
218    if (!readData())
219        return NULL;
220    MtpResponseCode ret = readResponse();
221    if (ret == MTP_RESPONSE_OK) {
222        return mData.getAUInt32();
223    }
224    return NULL;
225}
226
227MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
228    Mutex::Autolock autoLock(mMutex);
229
230    // FIXME - we might want to add some caching here
231
232    mRequest.reset();
233    mRequest.setParameter(1, handle);
234    if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
235        return NULL;
236    if (!readData())
237        return NULL;
238    MtpResponseCode ret = readResponse();
239    if (ret == MTP_RESPONSE_OK) {
240        MtpObjectInfo* info = new MtpObjectInfo(handle);
241        info->read(mData);
242        return info;
243    }
244    return NULL;
245}
246
247void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
248    Mutex::Autolock autoLock(mMutex);
249
250    mRequest.reset();
251    mRequest.setParameter(1, handle);
252    if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
253        MtpResponseCode ret = readResponse();
254        if (ret == MTP_RESPONSE_OK) {
255            return mData.getData(outLength);
256        }
257    }
258    outLength = 0;
259    return NULL;
260}
261
262MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
263    Mutex::Autolock autoLock(mMutex);
264
265    mRequest.reset();
266    MtpObjectHandle parent = info->mParent;
267    if (parent == 0)
268        parent = MTP_PARENT_ROOT;
269
270    mRequest.setParameter(1, info->mStorageID);
271    mRequest.setParameter(2, info->mParent);
272
273    mData.putUInt32(info->mStorageID);
274    mData.putUInt16(info->mFormat);
275    mData.putUInt16(info->mProtectionStatus);
276    mData.putUInt32(info->mCompressedSize);
277    mData.putUInt16(info->mThumbFormat);
278    mData.putUInt32(info->mThumbCompressedSize);
279    mData.putUInt32(info->mThumbPixWidth);
280    mData.putUInt32(info->mThumbPixHeight);
281    mData.putUInt32(info->mImagePixWidth);
282    mData.putUInt32(info->mImagePixHeight);
283    mData.putUInt32(info->mImagePixDepth);
284    mData.putUInt32(info->mParent);
285    mData.putUInt16(info->mAssociationType);
286    mData.putUInt32(info->mAssociationDesc);
287    mData.putUInt32(info->mSequenceNumber);
288    mData.putString(info->mName);
289
290    char created[100], modified[100];
291    formatDateTime(info->mDateCreated, created, sizeof(created));
292    formatDateTime(info->mDateModified, modified, sizeof(modified));
293
294    mData.putString(created);
295    mData.putString(modified);
296    if (info->mKeywords)
297        mData.putString(info->mKeywords);
298    else
299        mData.putEmptyString();
300
301   if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
302        MtpResponseCode ret = readResponse();
303        if (ret == MTP_RESPONSE_OK) {
304            info->mStorageID = mResponse.getParameter(1);
305            info->mParent = mResponse.getParameter(2);
306            info->mHandle = mResponse.getParameter(3);
307            return info->mHandle;
308        }
309    }
310    return (MtpObjectHandle)-1;
311}
312
313bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
314    Mutex::Autolock autoLock(mMutex);
315
316    int remaining = info->mCompressedSize;
317    mRequest.reset();
318    mRequest.setParameter(1, info->mHandle);
319    if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
320        // send data header
321        writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
322
323        char buffer[65536];
324        while (remaining > 0) {
325            int count = read(srcFD, buffer, sizeof(buffer));
326            if (count > 0) {
327                int written = mData.write(mEndpointOut, buffer, count);
328                // FIXME check error
329                remaining -= count;
330            } else {
331                break;
332            }
333        }
334    }
335    MtpResponseCode ret = readResponse();
336    return (remaining == 0 && ret == MTP_RESPONSE_OK);
337}
338
339bool MtpDevice::deleteObject(MtpObjectHandle handle) {
340    Mutex::Autolock autoLock(mMutex);
341
342    mRequest.reset();
343    mRequest.setParameter(1, handle);
344    if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
345        MtpResponseCode ret = readResponse();
346        if (ret == MTP_RESPONSE_OK)
347            return true;
348    }
349    return false;
350}
351
352MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
353    MtpObjectInfo* info = getObjectInfo(handle);
354    if (info)
355        return info->mParent;
356    else
357        return -1;
358}
359
360MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
361    MtpObjectInfo* info = getObjectInfo(handle);
362    if (info)
363        return info->mStorageID;
364    else
365        return -1;
366}
367
368MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
369    Mutex::Autolock autoLock(mMutex);
370
371    mRequest.reset();
372    mRequest.setParameter(1, format);
373    if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
374        return NULL;
375    if (!readData())
376        return NULL;
377    MtpResponseCode ret = readResponse();
378    if (ret == MTP_RESPONSE_OK) {
379        return mData.getAUInt16();
380    }
381    return NULL;
382
383}
384
385MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
386    Mutex::Autolock autoLock(mMutex);
387
388    mRequest.reset();
389    mRequest.setParameter(1, code);
390    if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
391        return NULL;
392    if (!readData())
393        return NULL;
394    MtpResponseCode ret = readResponse();
395    if (ret == MTP_RESPONSE_OK) {
396        MtpProperty* property = new MtpProperty;
397        property->read(mData);
398        return property;
399    }
400    return NULL;
401}
402
403MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
404    Mutex::Autolock autoLock(mMutex);
405
406    mRequest.reset();
407    mRequest.setParameter(1, code);
408    mRequest.setParameter(2, format);
409    if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
410        return NULL;
411    if (!readData())
412        return NULL;
413    MtpResponseCode ret = readResponse();
414    if (ret == MTP_RESPONSE_OK) {
415        MtpProperty* property = new MtpProperty;
416        property->read(mData);
417        return property;
418    }
419    return NULL;
420}
421
422// reads the object's data and writes it to the specified file path
423bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
424    LOGD("readObject: %s", destPath);
425    int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
426    if (fd < 0) {
427        LOGE("open failed for %s", destPath);
428        return false;
429    }
430
431    fchown(fd, getuid(), group);
432    // set permissions
433    int mask = umask(0);
434    fchmod(fd, perm);
435    umask(mask);
436
437    Mutex::Autolock autoLock(mMutex);
438    bool result = false;
439
440    mRequest.reset();
441    mRequest.setParameter(1, handle);
442    if (sendRequest(MTP_OPERATION_GET_OBJECT)
443            && mData.readDataHeader(mEndpointIn)) {
444        uint32_t length = mData.getContainerLength();
445        if (length < MTP_CONTAINER_HEADER_SIZE)
446            goto fail;
447        length -= MTP_CONTAINER_HEADER_SIZE;
448        uint32_t remaining = length;
449
450        int initialDataLength = 0;
451        void* initialData = mData.getData(initialDataLength);
452        if (initialData) {
453            if (initialDataLength > 0) {
454                if (write(fd, initialData, initialDataLength) != initialDataLength)
455                    goto fail;
456                remaining -= initialDataLength;
457            }
458            free(initialData);
459        }
460
461        // USB reads greater than 16K don't work
462        char buffer1[16384], buffer2[16384];
463        char* readBuffer = buffer1;
464        char* writeBuffer = NULL;
465        int writeLength = 0;
466
467        while (remaining > 0 || writeBuffer) {
468            if (remaining > 0) {
469                // queue up a read request
470                int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
471                if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) {
472                    LOGE("readDataAsync failed");
473                    goto fail;
474                }
475            } else {
476                readBuffer = NULL;
477            }
478
479            if (writeBuffer) {
480                // write previous buffer
481                if (write(fd, writeBuffer, writeLength) != writeLength) {
482                    LOGE("write failed");
483                    // wait for pending read before failing
484                    if (readBuffer)
485                        mData.readDataWait(mEndpointIn);
486                    goto fail;
487                }
488                writeBuffer = NULL;
489            }
490
491            // wait for read to complete
492            if (readBuffer) {
493                int read = mData.readDataWait(mEndpointIn);
494                if (read < 0)
495                    goto fail;
496
497                writeBuffer = readBuffer;
498                writeLength = read;
499                remaining -= read;
500                readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1);
501            }
502        }
503
504        MtpResponseCode response = readResponse();
505        if (response == MTP_RESPONSE_OK)
506            result = true;
507    }
508
509fail:
510    ::close(fd);
511    return result;
512}
513
514bool MtpDevice::sendRequest(MtpOperationCode operation) {
515    LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
516    mRequest.setOperationCode(operation);
517    if (mTransactionID > 0)
518        mRequest.setTransactionID(mTransactionID++);
519    int ret = mRequest.write(mEndpointOut);
520    mRequest.dump();
521    return (ret > 0);
522}
523
524bool MtpDevice::sendData() {
525    LOGV("sendData\n");
526    mData.setOperationCode(mRequest.getOperationCode());
527    mData.setTransactionID(mRequest.getTransactionID());
528    int ret = mData.write(mEndpointOut);
529    mData.dump();
530    return (ret > 0);
531}
532
533bool MtpDevice::readData() {
534    mData.reset();
535    int ret = mData.read(mEndpointIn);
536    LOGV("readData returned %d\n", ret);
537    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
538        mData.dump();
539        return true;
540    }
541    else {
542        LOGV("readResponse failed\n");
543        return false;
544    }
545}
546
547bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
548    mData.setOperationCode(operation);
549    mData.setTransactionID(mRequest.getTransactionID());
550    return (!mData.writeDataHeader(mEndpointOut, dataLength));
551}
552
553MtpResponseCode MtpDevice::readResponse() {
554    LOGV("readResponse\n");
555    int ret = mResponse.read(mEndpointIn);
556    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
557        mResponse.dump();
558        return mResponse.getResponseCode();
559    }
560    else {
561        LOGD("readResponse failed\n");
562        return -1;
563    }
564}
565
566}  // namespace android
567