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