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