MtpServer.cpp revision 40ce1f262cc4edbc8b7c470830325466263acaec
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#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <sys/stat.h>
25#include <dirent.h>
26
27#include <cutils/properties.h>
28
29#define LOG_TAG "MtpServer"
30
31#include "MtpDebug.h"
32#include "MtpDatabase.h"
33#include "MtpProperty.h"
34#include "MtpServer.h"
35#include "MtpStorage.h"
36#include "MtpStringBuffer.h"
37
38#include <linux/usb/f_mtp.h>
39
40namespace android {
41
42static const MtpOperationCode kSupportedOperationCodes[] = {
43    MTP_OPERATION_GET_DEVICE_INFO,
44    MTP_OPERATION_OPEN_SESSION,
45    MTP_OPERATION_CLOSE_SESSION,
46    MTP_OPERATION_GET_STORAGE_IDS,
47    MTP_OPERATION_GET_STORAGE_INFO,
48    MTP_OPERATION_GET_NUM_OBJECTS,
49    MTP_OPERATION_GET_OBJECT_HANDLES,
50    MTP_OPERATION_GET_OBJECT_INFO,
51    MTP_OPERATION_GET_OBJECT,
52//    MTP_OPERATION_GET_THUMB,
53    MTP_OPERATION_DELETE_OBJECT,
54    MTP_OPERATION_SEND_OBJECT_INFO,
55    MTP_OPERATION_SEND_OBJECT,
56//    MTP_OPERATION_INITIATE_CAPTURE,
57//    MTP_OPERATION_FORMAT_STORE,
58//    MTP_OPERATION_RESET_DEVICE,
59//    MTP_OPERATION_SELF_TEST,
60//    MTP_OPERATION_SET_OBJECT_PROTECTION,
61//    MTP_OPERATION_POWER_DOWN,
62    MTP_OPERATION_GET_DEVICE_PROP_DESC,
63    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
64    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
65    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
66//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
67//    MTP_OPERATION_MOVE_OBJECT,
68//    MTP_OPERATION_COPY_OBJECT,
69    MTP_OPERATION_GET_PARTIAL_OBJECT,
70//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
71    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
72    MTP_OPERATION_GET_OBJECT_PROP_DESC,
73    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
74    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
75    MTP_OPERATION_GET_OBJECT_PROP_LIST,
76//    MTP_OPERATION_SET_OBJECT_PROP_LIST,
77//    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
78//    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
79    MTP_OPERATION_GET_OBJECT_REFERENCES,
80    MTP_OPERATION_SET_OBJECT_REFERENCES,
81//    MTP_OPERATION_SKIP,
82};
83
84static const MtpEventCode kSupportedEventCodes[] = {
85    MTP_EVENT_OBJECT_ADDED,
86    MTP_EVENT_OBJECT_REMOVED,
87};
88
89MtpServer::MtpServer(int fd, MtpDatabase* database,
90                    int fileGroup, int filePerm, int directoryPerm)
91    :   mFD(fd),
92        mDatabase(database),
93        mFileGroup(fileGroup),
94        mFilePermission(filePerm),
95        mDirectoryPermission(directoryPerm),
96        mSessionID(0),
97        mSessionOpen(false),
98        mSendObjectHandle(kInvalidObjectHandle),
99        mSendObjectFormat(0),
100        mSendObjectFileSize(0)
101{
102}
103
104MtpServer::~MtpServer() {
105}
106
107void MtpServer::addStorage(const char* filePath) {
108    int index = mStorages.size() + 1;
109    index |= index << 16;   // set high and low part to our index
110    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
111    addStorage(storage);
112}
113
114MtpStorage* MtpServer::getStorage(MtpStorageID id) {
115    for (int i = 0; i < mStorages.size(); i++) {
116        MtpStorage* storage =  mStorages[i];
117        if (storage->getStorageID() == id)
118            return storage;
119    }
120    return NULL;
121}
122
123void MtpServer::run() {
124    int fd = mFD;
125
126    LOGV("MtpServer::run fd: %d\n", fd);
127
128    while (1) {
129        int ret = mRequest.read(fd);
130        if (ret < 0) {
131            LOGE("request read returned %d, errno: %d", ret, errno);
132            if (errno == ECANCELED) {
133                // return to top of loop and wait for next command
134                continue;
135            }
136            break;
137        }
138        MtpOperationCode operation = mRequest.getOperationCode();
139        MtpTransactionID transaction = mRequest.getTransactionID();
140
141        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
142        mRequest.dump();
143
144        // FIXME need to generalize this
145        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
146                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
147                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
148                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
149        if (dataIn) {
150            int ret = mData.read(fd);
151            if (ret < 0) {
152                LOGE("data read returned %d, errno: %d", ret, errno);
153                if (errno == ECANCELED) {
154                    // return to top of loop and wait for next command
155                    continue;
156                }
157                break;
158            }
159            LOGV("received data:");
160            mData.dump();
161        } else {
162            mData.reset();
163        }
164
165        if (handleRequest()) {
166            if (!dataIn && mData.hasData()) {
167                mData.setOperationCode(operation);
168                mData.setTransactionID(transaction);
169                LOGV("sending data:");
170                mData.dump();
171                ret = mData.write(fd);
172                if (ret < 0) {
173                    LOGE("request write returned %d, errno: %d", ret, errno);
174                    if (errno == ECANCELED) {
175                        // return to top of loop and wait for next command
176                        continue;
177                    }
178                    break;
179                }
180            }
181
182            mResponse.setTransactionID(transaction);
183            LOGV("sending response %04X", mResponse.getResponseCode());
184            ret = mResponse.write(fd);
185            mResponse.dump();
186            if (ret < 0) {
187                LOGE("request write returned %d, errno: %d", ret, errno);
188                if (errno == ECANCELED) {
189                    // return to top of loop and wait for next command
190                    continue;
191                }
192                break;
193            }
194        } else {
195            LOGV("skipping response\n");
196        }
197    }
198
199    if (mSessionOpen)
200        mDatabase->sessionEnded();
201}
202
203void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
204    if (mSessionOpen) {
205        LOGD("sendObjectAdded %d\n", handle);
206        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
207        mEvent.setTransactionID(mRequest.getTransactionID());
208        mEvent.setParameter(1, handle);
209        int ret = mEvent.write(mFD);
210        LOGD("mEvent.write returned %d\n", ret);
211    }
212}
213
214void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
215    if (mSessionOpen) {
216        LOGD("sendObjectRemoved %d\n", handle);
217        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
218        mEvent.setTransactionID(mRequest.getTransactionID());
219        mEvent.setParameter(1, handle);
220        int ret = mEvent.write(mFD);
221        LOGD("mEvent.write returned %d\n", ret);
222    }
223}
224
225bool MtpServer::handleRequest() {
226    MtpOperationCode operation = mRequest.getOperationCode();
227    MtpResponseCode response;
228
229    mResponse.reset();
230
231    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
232        // FIXME - need to delete mSendObjectHandle from the database
233        LOGE("expected SendObject after SendObjectInfo");
234        mSendObjectHandle = kInvalidObjectHandle;
235    }
236
237    switch (operation) {
238        case MTP_OPERATION_GET_DEVICE_INFO:
239            response = doGetDeviceInfo();
240            break;
241        case MTP_OPERATION_OPEN_SESSION:
242            response = doOpenSession();
243            break;
244        case MTP_OPERATION_CLOSE_SESSION:
245            response = doCloseSession();
246            break;
247        case MTP_OPERATION_GET_STORAGE_IDS:
248            response = doGetStorageIDs();
249            break;
250         case MTP_OPERATION_GET_STORAGE_INFO:
251            response = doGetStorageInfo();
252            break;
253        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
254            response = doGetObjectPropsSupported();
255            break;
256        case MTP_OPERATION_GET_OBJECT_HANDLES:
257            response = doGetObjectHandles();
258            break;
259        case MTP_OPERATION_GET_NUM_OBJECTS:
260            response = doGetNumObjects();
261            break;
262        case MTP_OPERATION_GET_OBJECT_REFERENCES:
263            response = doGetObjectReferences();
264            break;
265        case MTP_OPERATION_SET_OBJECT_REFERENCES:
266            response = doSetObjectReferences();
267            break;
268        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
269            response = doGetObjectPropValue();
270            break;
271        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
272            response = doSetObjectPropValue();
273            break;
274        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
275            response = doGetDevicePropValue();
276            break;
277        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
278            response = doSetDevicePropValue();
279            break;
280        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
281            response = doResetDevicePropValue();
282            break;
283        case MTP_OPERATION_GET_OBJECT_PROP_LIST:
284            response = doGetObjectPropList();
285            break;
286        case MTP_OPERATION_GET_OBJECT_INFO:
287            response = doGetObjectInfo();
288            break;
289        case MTP_OPERATION_GET_OBJECT:
290            response = doGetObject();
291            break;
292        case MTP_OPERATION_GET_PARTIAL_OBJECT:
293            response = doGetPartialObject();
294            break;
295        case MTP_OPERATION_SEND_OBJECT_INFO:
296            response = doSendObjectInfo();
297            break;
298        case MTP_OPERATION_SEND_OBJECT:
299            response = doSendObject();
300            break;
301        case MTP_OPERATION_DELETE_OBJECT:
302            response = doDeleteObject();
303            break;
304        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
305            response = doGetObjectPropDesc();
306            break;
307        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
308            response = doGetDevicePropDesc();
309            break;
310        default:
311            LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
312            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
313            break;
314    }
315
316    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
317        return false;
318    mResponse.setResponseCode(response);
319    return true;
320}
321
322MtpResponseCode MtpServer::doGetDeviceInfo() {
323    MtpStringBuffer   string;
324    char prop_value[PROPERTY_VALUE_MAX];
325
326    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
327    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
328    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
329
330    // fill in device info
331    mData.putUInt16(MTP_STANDARD_VERSION);
332    mData.putUInt32(6); // MTP Vendor Extension ID
333    mData.putUInt16(MTP_STANDARD_VERSION);
334    string.set("microsoft.com: 1.0;");
335    mData.putString(string); // MTP Extensions
336    mData.putUInt16(0); //Functional Mode
337    mData.putAUInt16(kSupportedOperationCodes,
338            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
339    mData.putAUInt16(kSupportedEventCodes,
340            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
341    mData.putAUInt16(deviceProperties); // Device Properties Supported
342    mData.putAUInt16(captureFormats); // Capture Formats
343    mData.putAUInt16(playbackFormats);  // Playback Formats
344    // FIXME
345    string.set("Google, Inc.");
346    mData.putString(string);   // Manufacturer
347
348    property_get("ro.product.model", prop_value, "MTP Device");
349    string.set(prop_value);
350    mData.putString(string);   // Model
351    string.set("1.0");
352    mData.putString(string);   // Device Version
353
354    property_get("ro.serialno", prop_value, "????????");
355    string.set(prop_value);
356    mData.putString(string);   // Serial Number
357
358    delete playbackFormats;
359    delete captureFormats;
360    delete deviceProperties;
361
362    return MTP_RESPONSE_OK;
363}
364
365MtpResponseCode MtpServer::doOpenSession() {
366    if (mSessionOpen) {
367        mResponse.setParameter(1, mSessionID);
368        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
369    }
370    mSessionID = mRequest.getParameter(1);
371    mSessionOpen = true;
372
373    mDatabase->sessionStarted();
374
375    return MTP_RESPONSE_OK;
376}
377
378MtpResponseCode MtpServer::doCloseSession() {
379    if (!mSessionOpen)
380        return MTP_RESPONSE_SESSION_NOT_OPEN;
381    mSessionID = 0;
382    mSessionOpen = false;
383    mDatabase->sessionEnded();
384    return MTP_RESPONSE_OK;
385}
386
387MtpResponseCode MtpServer::doGetStorageIDs() {
388    if (!mSessionOpen)
389        return MTP_RESPONSE_SESSION_NOT_OPEN;
390
391    int count = mStorages.size();
392    mData.putUInt32(count);
393    for (int i = 0; i < count; i++)
394        mData.putUInt32(mStorages[i]->getStorageID());
395
396    return MTP_RESPONSE_OK;
397}
398
399MtpResponseCode MtpServer::doGetStorageInfo() {
400    MtpStringBuffer   string;
401
402    if (!mSessionOpen)
403        return MTP_RESPONSE_SESSION_NOT_OPEN;
404    MtpStorageID id = mRequest.getParameter(1);
405    MtpStorage* storage = getStorage(id);
406    if (!storage)
407        return MTP_RESPONSE_INVALID_STORAGE_ID;
408
409    mData.putUInt16(storage->getType());
410    mData.putUInt16(storage->getFileSystemType());
411    mData.putUInt16(storage->getAccessCapability());
412    mData.putUInt64(storage->getMaxCapacity());
413    mData.putUInt64(storage->getFreeSpace());
414    mData.putUInt32(1024*1024*1024); // Free Space in Objects
415    string.set(storage->getDescription());
416    mData.putString(string);
417    mData.putEmptyString();   // Volume Identifier
418
419    return MTP_RESPONSE_OK;
420}
421
422MtpResponseCode MtpServer::doGetObjectPropsSupported() {
423    if (!mSessionOpen)
424        return MTP_RESPONSE_SESSION_NOT_OPEN;
425    MtpObjectFormat format = mRequest.getParameter(1);
426    MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
427    mData.putAUInt16(properties);
428    delete properties;
429    return MTP_RESPONSE_OK;
430}
431
432MtpResponseCode MtpServer::doGetObjectHandles() {
433    if (!mSessionOpen)
434        return MTP_RESPONSE_SESSION_NOT_OPEN;
435    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
436    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
437    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
438                                                            // 0x00000000 for all objects?
439    if (parent == 0xFFFFFFFF)
440        parent = 0;
441
442    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
443    mData.putAUInt32(handles);
444    delete handles;
445    return MTP_RESPONSE_OK;
446}
447
448MtpResponseCode MtpServer::doGetNumObjects() {
449    if (!mSessionOpen)
450        return MTP_RESPONSE_SESSION_NOT_OPEN;
451    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
452    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
453    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
454                                                            // 0x00000000 for all objects?
455    if (parent == 0xFFFFFFFF)
456        parent = 0;
457
458    int count = mDatabase->getNumObjects(storageID, format, parent);
459    if (count >= 0) {
460        mResponse.setParameter(1, count);
461        return MTP_RESPONSE_OK;
462    } else {
463        mResponse.setParameter(1, 0);
464        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
465    }
466}
467
468MtpResponseCode MtpServer::doGetObjectReferences() {
469    if (!mSessionOpen)
470        return MTP_RESPONSE_SESSION_NOT_OPEN;
471    MtpStorageID handle = mRequest.getParameter(1);
472
473    // FIXME - check for invalid object handle
474    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
475    if (handles) {
476        mData.putAUInt32(handles);
477        delete handles;
478    } else {
479        mData.putEmptyArray();
480    }
481    return MTP_RESPONSE_OK;
482}
483
484MtpResponseCode MtpServer::doSetObjectReferences() {
485    if (!mSessionOpen)
486        return MTP_RESPONSE_SESSION_NOT_OPEN;
487    MtpStorageID handle = mRequest.getParameter(1);
488    MtpObjectHandleList* references = mData.getAUInt32();
489    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
490    delete references;
491    return result;
492}
493
494MtpResponseCode MtpServer::doGetObjectPropValue() {
495    MtpObjectHandle handle = mRequest.getParameter(1);
496    MtpObjectProperty property = mRequest.getParameter(2);
497    LOGD("GetObjectPropValue %d %s\n", handle,
498            MtpDebug::getObjectPropCodeName(property));
499
500    return mDatabase->getObjectPropertyValue(handle, property, mData);
501}
502
503MtpResponseCode MtpServer::doSetObjectPropValue() {
504    MtpObjectHandle handle = mRequest.getParameter(1);
505    MtpObjectProperty property = mRequest.getParameter(2);
506    LOGD("SetObjectPropValue %d %s\n", handle,
507            MtpDebug::getObjectPropCodeName(property));
508
509    return mDatabase->setObjectPropertyValue(handle, property, mData);
510}
511
512MtpResponseCode MtpServer::doGetDevicePropValue() {
513    MtpDeviceProperty property = mRequest.getParameter(1);
514    LOGD("GetDevicePropValue %s\n",
515            MtpDebug::getDevicePropCodeName(property));
516
517    return mDatabase->getDevicePropertyValue(property, mData);
518}
519
520MtpResponseCode MtpServer::doSetDevicePropValue() {
521    MtpDeviceProperty property = mRequest.getParameter(1);
522    LOGD("SetDevicePropValue %s\n",
523            MtpDebug::getDevicePropCodeName(property));
524
525    return mDatabase->setDevicePropertyValue(property, mData);
526}
527
528MtpResponseCode MtpServer::doResetDevicePropValue() {
529    MtpDeviceProperty property = mRequest.getParameter(1);
530    LOGD("ResetDevicePropValue %s\n",
531            MtpDebug::getDevicePropCodeName(property));
532
533    return mDatabase->resetDeviceProperty(property);
534}
535
536MtpResponseCode MtpServer::doGetObjectPropList() {
537
538    MtpObjectHandle handle = mRequest.getParameter(1);
539    // use uint32_t so we can support 0xFFFFFFFF
540    uint32_t format = mRequest.getParameter(2);
541    uint32_t property = mRequest.getParameter(3);
542    int groupCode = mRequest.getParameter(4);
543    int depth = mRequest.getParameter(5);
544   LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
545            handle, MtpDebug::getFormatCodeName(format),
546            MtpDebug::getObjectPropCodeName(property), groupCode, depth);
547
548    return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
549}
550
551MtpResponseCode MtpServer::doGetObjectInfo() {
552    MtpObjectHandle handle = mRequest.getParameter(1);
553    return mDatabase->getObjectInfo(handle, mData);
554}
555
556MtpResponseCode MtpServer::doGetObject() {
557    MtpObjectHandle handle = mRequest.getParameter(1);
558    MtpString pathBuf;
559    int64_t fileLength;
560    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
561    if (result != MTP_RESPONSE_OK)
562        return result;
563
564    const char* filePath = (const char *)pathBuf;
565    mtp_file_range  mfr;
566    mfr.fd = open(filePath, O_RDONLY);
567    if (mfr.fd < 0) {
568        return MTP_RESPONSE_GENERAL_ERROR;
569    }
570    mfr.offset = 0;
571    mfr.length = fileLength;
572
573    // send data header
574    mData.setOperationCode(mRequest.getOperationCode());
575    mData.setTransactionID(mRequest.getTransactionID());
576    mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
577
578    // then transfer the file
579    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
580    close(mfr.fd);
581    if (ret < 0) {
582        if (errno == ECANCELED)
583            return MTP_RESPONSE_TRANSACTION_CANCELLED;
584        else
585            return MTP_RESPONSE_GENERAL_ERROR;
586    }
587    return MTP_RESPONSE_OK;
588}
589
590MtpResponseCode MtpServer::doGetPartialObject() {
591    MtpObjectHandle handle = mRequest.getParameter(1);
592    uint32_t offset = mRequest.getParameter(2);
593    uint32_t length = mRequest.getParameter(3);
594    MtpString pathBuf;
595    int64_t fileLength;
596    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
597    if (result != MTP_RESPONSE_OK)
598        return result;
599    if (offset + length > fileLength)
600        length = fileLength - offset;
601
602    const char* filePath = (const char *)pathBuf;
603    mtp_file_range  mfr;
604    mfr.fd = open(filePath, O_RDONLY);
605    if (mfr.fd < 0) {
606        return MTP_RESPONSE_GENERAL_ERROR;
607    }
608    mfr.offset = offset;
609    mfr.length = length;
610    mResponse.setParameter(1, length);
611
612    // send data header
613    mData.setOperationCode(mRequest.getOperationCode());
614    mData.setTransactionID(mRequest.getTransactionID());
615    mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
616
617    // then transfer the file
618    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
619    close(mfr.fd);
620    if (ret < 0) {
621        if (errno == ECANCELED)
622            return MTP_RESPONSE_TRANSACTION_CANCELLED;
623        else
624            return MTP_RESPONSE_GENERAL_ERROR;
625    }
626    return MTP_RESPONSE_OK;
627}
628
629MtpResponseCode MtpServer::doSendObjectInfo() {
630    MtpString path;
631    MtpStorageID storageID = mRequest.getParameter(1);
632    MtpStorage* storage = getStorage(storageID);
633    MtpObjectHandle parent = mRequest.getParameter(2);
634    if (!storage)
635        return MTP_RESPONSE_INVALID_STORAGE_ID;
636
637    // special case the root
638    if (parent == MTP_PARENT_ROOT) {
639        path = storage->getPath();
640        parent = 0;
641    } else {
642        int64_t dummy;
643        int result = mDatabase->getObjectFilePath(parent, path, dummy);
644        if (result != MTP_RESPONSE_OK)
645            return result;
646    }
647
648    // read only the fields we need
649    mData.getUInt32();  // storage ID
650    MtpObjectFormat format = mData.getUInt16();
651    mData.getUInt16();  // protection status
652    mSendObjectFileSize = mData.getUInt32();
653    mData.getUInt16();  // thumb format
654    mData.getUInt32();  // thumb compressed size
655    mData.getUInt32();  // thumb pix width
656    mData.getUInt32();  // thumb pix height
657    mData.getUInt32();  // image pix width
658    mData.getUInt32();  // image pix height
659    mData.getUInt32();  // image bit depth
660    mData.getUInt32();  // parent
661    uint16_t associationType = mData.getUInt16();
662    uint32_t associationDesc = mData.getUInt32();   // association desc
663    mData.getUInt32();  // sequence number
664    MtpStringBuffer name, created, modified;
665    mData.getString(name);    // file name
666    mData.getString(created);      // date created
667    mData.getString(modified);     // date modified
668    // keywords follow
669
670    LOGD("name: %s format: %04X\n", (const char *)name, format);
671    time_t modifiedTime;
672    if (!parseDateTime(modified, modifiedTime))
673        modifiedTime = 0;
674
675    if (path[path.size() - 1] != '/')
676        path += "/";
677    path += (const char *)name;
678
679    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
680            format, parent, storageID, mSendObjectFileSize, modifiedTime);
681    if (handle == kInvalidObjectHandle) {
682        return MTP_RESPONSE_GENERAL_ERROR;
683    }
684
685  if (format == MTP_FORMAT_ASSOCIATION) {
686        mode_t mask = umask(0);
687        int ret = mkdir((const char *)path, mDirectoryPermission);
688        umask(mask);
689        if (ret && ret != -EEXIST)
690            return MTP_RESPONSE_GENERAL_ERROR;
691        chown((const char *)path, getuid(), mFileGroup);
692    } else {
693        mSendObjectFilePath = path;
694        // save the handle for the SendObject call, which should follow
695        mSendObjectHandle = handle;
696        mSendObjectFormat = format;
697    }
698
699    mResponse.setParameter(1, storageID);
700    mResponse.setParameter(2, parent);
701    mResponse.setParameter(3, handle);
702
703    return MTP_RESPONSE_OK;
704}
705
706MtpResponseCode MtpServer::doSendObject() {
707    MtpResponseCode result = MTP_RESPONSE_OK;
708    mode_t mask;
709    int ret;
710    uint64_t actualSize = -1;
711
712    if (mSendObjectHandle == kInvalidObjectHandle) {
713        LOGE("Expected SendObjectInfo before SendObject");
714        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
715        goto done;
716    }
717
718    // read the header
719    ret = mData.readDataHeader(mFD);
720    // FIXME - check for errors here.
721
722    // reset so we don't attempt to send this back
723    mData.reset();
724
725    mtp_file_range  mfr;
726    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
727    if (mfr.fd < 0) {
728        result = MTP_RESPONSE_GENERAL_ERROR;
729        goto done;
730    }
731    fchown(mfr.fd, getuid(), mFileGroup);
732    // set permissions
733    mask = umask(0);
734    fchmod(mfr.fd, mFilePermission);
735    umask(mask);
736
737    mfr.offset = 0;
738    mfr.length = mSendObjectFileSize;
739
740    LOGD("receiving %s\n", (const char *)mSendObjectFilePath);
741    // transfer the file
742    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
743    close(mfr.fd);
744
745    LOGV("MTP_RECEIVE_FILE returned %d", ret);
746
747    if (ret < 0) {
748        unlink(mSendObjectFilePath);
749        if (errno == ECANCELED)
750            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
751        else
752            result = MTP_RESPONSE_GENERAL_ERROR;
753    } else if (mSendObjectFileSize == 0xFFFFFFFF) {
754        // actual size is likely > 4 gig so stat the file to compute actual length
755        struct stat s;
756        if (lstat(mSendObjectFilePath, &s) == 0) {
757            actualSize = s.st_size;
758            LOGD("actualSize: %lld\n", actualSize);
759        }
760    }
761
762done:
763    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
764            actualSize, result == MTP_RESPONSE_OK);
765    mSendObjectHandle = kInvalidObjectHandle;
766    mSendObjectFormat = 0;
767    return result;
768}
769
770static void deleteRecursive(const char* path) {
771    char pathbuf[PATH_MAX];
772    int pathLength = strlen(path);
773    if (pathLength >= sizeof(pathbuf) - 1) {
774        LOGE("path too long: %s\n", path);
775    }
776    strcpy(pathbuf, path);
777    if (pathbuf[pathLength - 1] != '/') {
778        pathbuf[pathLength++] = '/';
779    }
780    char* fileSpot = pathbuf + pathLength;
781    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
782
783    DIR* dir = opendir(path);
784    if (!dir) {
785        LOGE("opendir %s failed: %s", path, strerror(errno));
786        return;
787    }
788
789    struct dirent* entry;
790    while ((entry = readdir(dir))) {
791        const char* name = entry->d_name;
792
793        // ignore "." and ".."
794        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
795            continue;
796        }
797
798        int nameLength = strlen(name);
799        if (nameLength > pathRemaining) {
800            LOGE("path %s/%s too long\n", path, name);
801            continue;
802        }
803        strcpy(fileSpot, name);
804
805        int type = entry->d_type;
806        if (entry->d_type == DT_DIR) {
807            deleteRecursive(pathbuf);
808            rmdir(pathbuf);
809        } else {
810            unlink(pathbuf);
811        }
812    }
813    closedir(dir);
814}
815
816static void deletePath(const char* path) {
817    struct stat statbuf;
818    if (stat(path, &statbuf) == 0) {
819        if (S_ISDIR(statbuf.st_mode)) {
820            deleteRecursive(path);
821            rmdir(path);
822        } else {
823            unlink(path);
824        }
825    } else {
826        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
827    }
828}
829
830MtpResponseCode MtpServer::doDeleteObject() {
831    MtpObjectHandle handle = mRequest.getParameter(1);
832    MtpObjectFormat format = mRequest.getParameter(2);
833    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
834    // FIXME - implement deleting objects by format
835
836    MtpString filePath;
837    int64_t fileLength;
838    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
839    if (result == MTP_RESPONSE_OK) {
840        LOGV("deleting %s", (const char *)filePath);
841        deletePath((const char *)filePath);
842        return mDatabase->deleteFile(handle);
843    } else {
844        return result;
845    }
846}
847
848MtpResponseCode MtpServer::doGetObjectPropDesc() {
849    MtpObjectProperty propCode = mRequest.getParameter(1);
850    MtpObjectFormat format = mRequest.getParameter(2);
851    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
852                                        MtpDebug::getFormatCodeName(format));
853    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
854    if (!property)
855        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
856    property->write(mData);
857    delete property;
858    return MTP_RESPONSE_OK;
859}
860
861MtpResponseCode MtpServer::doGetDevicePropDesc() {
862    MtpDeviceProperty propCode = mRequest.getParameter(1);
863    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
864    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
865    if (!property)
866        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
867    property->write(mData);
868    delete property;
869    return MTP_RESPONSE_OK;
870}
871
872}  // namespace android
873