MtpServer.cpp revision 343af4ef512869695456a91519e73ed3c3d82101
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
25#include <cutils/properties.h>
26
27#include "MtpDebug.h"
28#include "MtpDatabase.h"
29#include "MtpProperty.h"
30#include "MtpServer.h"
31#include "MtpStorage.h"
32#include "MtpStringBuffer.h"
33
34#include <linux/usb/f_mtp.h>
35
36namespace android {
37
38static const MtpOperationCode kSupportedOperationCodes[] = {
39    MTP_OPERATION_GET_DEVICE_INFO,
40    MTP_OPERATION_OPEN_SESSION,
41    MTP_OPERATION_CLOSE_SESSION,
42    MTP_OPERATION_GET_STORAGE_IDS,
43    MTP_OPERATION_GET_STORAGE_INFO,
44    MTP_OPERATION_GET_NUM_OBJECTS,
45    MTP_OPERATION_GET_OBJECT_HANDLES,
46    MTP_OPERATION_GET_OBJECT_INFO,
47    MTP_OPERATION_GET_OBJECT,
48//    MTP_OPERATION_GET_THUMB,
49    MTP_OPERATION_DELETE_OBJECT,
50    MTP_OPERATION_SEND_OBJECT_INFO,
51    MTP_OPERATION_SEND_OBJECT,
52//    MTP_OPERATION_INITIATE_CAPTURE,
53//    MTP_OPERATION_FORMAT_STORE,
54//    MTP_OPERATION_RESET_DEVICE,
55//    MTP_OPERATION_SELF_TEST,
56//    MTP_OPERATION_SET_OBJECT_PROTECTION,
57//    MTP_OPERATION_POWER_DOWN,
58//    MTP_OPERATION_GET_DEVICE_PROP_DESC,
59//    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
60//    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
61//    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
62//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
63//    MTP_OPERATION_MOVE_OBJECT,
64//    MTP_OPERATION_COPY_OBJECT,
65//    MTP_OPERATION_GET_PARTIAL_OBJECT,
66//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
67    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
68//    MTP_OPERATION_GET_OBJECT_PROP_DESC,
69    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
70//    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
71//    MTP_OPERATION_GET_OBJECT_REFERENCES,
72//    MTP_OPERATION_SET_OBJECT_REFERENCES,
73//    MTP_OPERATION_SKIP,
74};
75
76static const MtpEventCode kSupportedEventCodes[] = {
77    MTP_EVENT_OBJECT_ADDED,
78    MTP_EVENT_OBJECT_REMOVED,
79};
80
81static const MtpObjectProperty kSupportedObjectProperties[] = {
82    MTP_PROPERTY_STORAGE_ID,
83    MTP_PROPERTY_OBJECT_FORMAT,
84    MTP_PROPERTY_OBJECT_SIZE,
85    MTP_PROPERTY_OBJECT_FILE_NAME,
86    MTP_PROPERTY_PARENT_OBJECT,
87};
88
89static const MtpObjectFormat kSupportedPlaybackFormats[] = {
90    // MTP_FORMAT_UNDEFINED,
91    MTP_FORMAT_ASSOCIATION,
92    // MTP_FORMAT_TEXT,
93    // MTP_FORMAT_HTML,
94    MTP_FORMAT_MP3,
95    //MTP_FORMAT_AVI,
96    MTP_FORMAT_MPEG,
97    // MTP_FORMAT_ASF,
98    MTP_FORMAT_EXIF_JPEG,
99    MTP_FORMAT_TIFF_EP,
100    // MTP_FORMAT_BMP,
101    MTP_FORMAT_GIF,
102    MTP_FORMAT_JFIF,
103    MTP_FORMAT_PNG,
104    MTP_FORMAT_TIFF,
105    MTP_FORMAT_WMA,
106    MTP_FORMAT_OGG,
107    MTP_FORMAT_AAC,
108    // MTP_FORMAT_FLAC,
109    // MTP_FORMAT_WMV,
110    MTP_FORMAT_MP4_CONTAINER,
111    MTP_FORMAT_MP2,
112    MTP_FORMAT_3GP_CONTAINER,
113    // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
114    // MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
115    // MTP_FORMAT_WPL_PLAYLIST,
116    // MTP_FORMAT_M3U_PLAYLIST,
117    // MTP_FORMAT_MPL_PLAYLIST,
118    // MTP_FORMAT_PLS_PLAYLIST,
119};
120
121MtpServer::MtpServer(int fd, MtpDatabase* database,
122                    int fileGroup, int filePerm, int directoryPerm)
123    :   mFD(fd),
124        mDatabase(database),
125        mFileGroup(fileGroup),
126        mFilePermission(filePerm),
127        mDirectoryPermission(directoryPerm),
128        mSessionID(0),
129        mSessionOpen(false),
130        mSendObjectHandle(kInvalidObjectHandle),
131        mSendObjectFormat(0),
132        mSendObjectFileSize(0)
133{
134    initObjectProperties();
135}
136
137MtpServer::~MtpServer() {
138}
139
140void MtpServer::addStorage(const char* filePath) {
141    int index = mStorages.size() + 1;
142    index |= index << 16;   // set high and low part to our index
143    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
144    addStorage(storage);
145}
146
147MtpStorage* MtpServer::getStorage(MtpStorageID id) {
148    for (int i = 0; i < mStorages.size(); i++) {
149        MtpStorage* storage =  mStorages[i];
150        if (storage->getStorageID() == id)
151            return storage;
152    }
153    return NULL;
154}
155
156void MtpServer::run() {
157    int fd = mFD;
158
159    LOGV("MtpServer::run fd: %d\n", fd);
160
161    while (1) {
162        int ret = mRequest.read(fd);
163        if (ret < 0) {
164            LOGE("request read returned %d, errno: %d", ret, errno);
165            if (errno == ECANCELED) {
166                // return to top of loop and wait for next command
167                continue;
168            }
169            break;
170        }
171        MtpOperationCode operation = mRequest.getOperationCode();
172        MtpTransactionID transaction = mRequest.getTransactionID();
173
174        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
175        mRequest.dump();
176
177        // FIXME need to generalize this
178        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
179        if (dataIn) {
180            int ret = mData.read(fd);
181            if (ret < 0) {
182                LOGE("data read returned %d, errno: %d", ret, errno);
183                if (errno == ECANCELED) {
184                    // return to top of loop and wait for next command
185                    continue;
186                }
187                break;
188            }
189            LOGV("received data:");
190            mData.dump();
191        } else {
192            mData.reset();
193        }
194
195        if (handleRequest()) {
196            if (!dataIn && mData.hasData()) {
197                mData.setOperationCode(operation);
198                mData.setTransactionID(transaction);
199                LOGV("sending data:");
200                mData.dump();
201                ret = mData.write(fd);
202                if (ret < 0) {
203                    LOGE("request write returned %d, errno: %d", ret, errno);
204                    if (errno == ECANCELED) {
205                        // return to top of loop and wait for next command
206                        continue;
207                    }
208                    break;
209                }
210            }
211
212            mResponse.setTransactionID(transaction);
213            LOGV("sending response %04X", mResponse.getResponseCode());
214            ret = mResponse.write(fd);
215            if (ret < 0) {
216                LOGE("request write returned %d, errno: %d", ret, errno);
217                if (errno == ECANCELED) {
218                    // return to top of loop and wait for next command
219                    continue;
220                }
221                break;
222            }
223        } else {
224            LOGV("skipping response\n");
225        }
226    }
227}
228
229MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
230    for (int i = 0; i < mObjectProperties.size(); i++) {
231        MtpProperty* property = mObjectProperties[i];
232        if (property->getPropertyCode() == propCode)
233            return property;
234    }
235    return NULL;
236}
237
238MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
239    for (int i = 0; i < mDeviceProperties.size(); i++) {
240        MtpProperty* property = mDeviceProperties[i];
241        if (property->getPropertyCode() == propCode)
242            return property;
243    }
244    return NULL;
245}
246
247void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
248    if (mSessionOpen) {
249        LOGD("sendObjectAdded %d\n", handle);
250        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
251        mEvent.setTransactionID(mRequest.getTransactionID());
252        mEvent.setParameter(1, handle);
253        int ret = mEvent.write(mFD);
254        LOGD("mEvent.write returned %d\n", ret);
255    }
256}
257
258void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
259    if (mSessionOpen) {
260        LOGD("sendObjectRemoved %d\n", handle);
261        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
262        mEvent.setTransactionID(mRequest.getTransactionID());
263        mEvent.setParameter(1, handle);
264        int ret = mEvent.write(mFD);
265        LOGD("mEvent.write returned %d\n", ret);
266    }
267}
268
269void MtpServer::initObjectProperties() {
270    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
271    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
272    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
273    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
274    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
275}
276
277bool MtpServer::handleRequest() {
278    MtpOperationCode operation = mRequest.getOperationCode();
279    MtpResponseCode response;
280
281    mResponse.reset();
282
283    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
284        // FIXME - need to delete mSendObjectHandle from the database
285        LOGE("expected SendObject after SendObjectInfo");
286        mSendObjectHandle = kInvalidObjectHandle;
287    }
288
289    switch (operation) {
290        case MTP_OPERATION_GET_DEVICE_INFO:
291            response = doGetDeviceInfo();
292            break;
293        case MTP_OPERATION_OPEN_SESSION:
294            response = doOpenSession();
295            break;
296        case MTP_OPERATION_CLOSE_SESSION:
297            response = doCloseSession();
298            break;
299        case MTP_OPERATION_GET_STORAGE_IDS:
300            response = doGetStorageIDs();
301            break;
302         case MTP_OPERATION_GET_STORAGE_INFO:
303            response = doGetStorageInfo();
304            break;
305        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
306            response = doGetObjectPropsSupported();
307            break;
308        case MTP_OPERATION_GET_OBJECT_HANDLES:
309            response = doGetObjectHandles();
310            break;
311        case MTP_OPERATION_GET_NUM_OBJECTS:
312            response = doGetNumObjects();
313            break;
314        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
315            response = doGetObjectPropValue();
316            break;
317        case MTP_OPERATION_GET_OBJECT_INFO:
318            response = doGetObjectInfo();
319            break;
320        case MTP_OPERATION_GET_OBJECT:
321            response = doGetObject();
322            break;
323        case MTP_OPERATION_SEND_OBJECT_INFO:
324            response = doSendObjectInfo();
325            break;
326        case MTP_OPERATION_SEND_OBJECT:
327            response = doSendObject();
328            break;
329        case MTP_OPERATION_DELETE_OBJECT:
330            response = doDeleteObject();
331            break;
332        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
333            response = doGetObjectPropDesc();
334            break;
335        default:
336            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
337            break;
338    }
339
340    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
341        return false;
342    mResponse.setResponseCode(response);
343    return true;
344}
345
346MtpResponseCode MtpServer::doGetDeviceInfo() {
347    MtpStringBuffer   string;
348    char prop_value[PROPERTY_VALUE_MAX];
349
350    // fill in device info
351    mData.putUInt16(MTP_STANDARD_VERSION);
352    mData.putUInt32(6); // MTP Vendor Extension ID
353    mData.putUInt16(MTP_STANDARD_VERSION);
354    string.set("microsoft.com: 1.0;");
355    mData.putString(string); // MTP Extensions
356    mData.putUInt16(0); //Functional Mode
357    mData.putAUInt16(kSupportedOperationCodes,
358            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
359    mData.putAUInt16(kSupportedEventCodes,
360            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
361    mData.putEmptyArray(); // Device Properties Supported
362    mData.putEmptyArray(); // Capture Formats
363    mData.putAUInt16(kSupportedPlaybackFormats,
364            sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
365    // FIXME
366    string.set("Google, Inc.");
367    mData.putString(string);   // Manufacturer
368
369    property_get("ro.product.model", prop_value, "MTP Device");
370    string.set(prop_value);
371    mData.putString(string);   // Model
372    string.set("1.0");
373    mData.putString(string);   // Device Version
374
375    property_get("ro.serialno", prop_value, "????????");
376    string.set(prop_value);
377    mData.putString(string);   // Serial Number
378
379    return MTP_RESPONSE_OK;
380}
381
382MtpResponseCode MtpServer::doOpenSession() {
383    if (mSessionOpen) {
384        mResponse.setParameter(1, mSessionID);
385        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
386    }
387    mSessionID = mRequest.getParameter(1);
388    mSessionOpen = true;
389    return MTP_RESPONSE_OK;
390}
391
392MtpResponseCode MtpServer::doCloseSession() {
393    if (!mSessionOpen)
394        return MTP_RESPONSE_SESSION_NOT_OPEN;
395    mSessionID = 0;
396    mSessionOpen = false;
397    return MTP_RESPONSE_OK;
398}
399
400MtpResponseCode MtpServer::doGetStorageIDs() {
401    if (!mSessionOpen)
402        return MTP_RESPONSE_SESSION_NOT_OPEN;
403
404    int count = mStorages.size();
405    mData.putUInt32(count);
406    for (int i = 0; i < count; i++)
407        mData.putUInt32(mStorages[i]->getStorageID());
408
409    return MTP_RESPONSE_OK;
410}
411
412MtpResponseCode MtpServer::doGetStorageInfo() {
413    MtpStringBuffer   string;
414
415    if (!mSessionOpen)
416        return MTP_RESPONSE_SESSION_NOT_OPEN;
417    MtpStorageID id = mRequest.getParameter(1);
418    MtpStorage* storage = getStorage(id);
419    if (!storage)
420        return MTP_RESPONSE_INVALID_STORAGE_ID;
421
422    mData.putUInt16(storage->getType());
423    mData.putUInt16(storage->getFileSystemType());
424    mData.putUInt16(storage->getAccessCapability());
425    mData.putUInt64(storage->getMaxCapacity());
426    mData.putUInt64(storage->getFreeSpace());
427    mData.putUInt32(1024*1024*1024); // Free Space in Objects
428    string.set(storage->getDescription());
429    mData.putString(string);
430    mData.putEmptyString();   // Volume Identifier
431
432    return MTP_RESPONSE_OK;
433}
434
435MtpResponseCode MtpServer::doGetObjectPropsSupported() {
436    if (!mSessionOpen)
437        return MTP_RESPONSE_SESSION_NOT_OPEN;
438    MtpObjectFormat format = mRequest.getParameter(1);
439    mData.putAUInt16(kSupportedObjectProperties,
440            sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
441    return MTP_RESPONSE_OK;
442}
443
444MtpResponseCode MtpServer::doGetObjectHandles() {
445    if (!mSessionOpen)
446        return MTP_RESPONSE_SESSION_NOT_OPEN;
447    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
448    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
449    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
450                                                            // 0x00000000 for all objects?
451    if (parent == 0xFFFFFFFF)
452        parent = 0;
453
454    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
455    mData.putAUInt32(handles);
456    delete handles;
457    return MTP_RESPONSE_OK;
458}
459
460MtpResponseCode MtpServer::doGetNumObjects() {
461    if (!mSessionOpen)
462        return MTP_RESPONSE_SESSION_NOT_OPEN;
463    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
464    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
465    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
466                                                            // 0x00000000 for all objects?
467    if (parent == 0xFFFFFFFF)
468        parent = 0;
469
470    int count = mDatabase->getNumObjects(storageID, format, parent);
471    if (count >= 0) {
472        mResponse.setParameter(1, count);
473        return MTP_RESPONSE_OK;
474    } else {
475        mResponse.setParameter(1, 0);
476        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
477    }
478}
479
480MtpResponseCode MtpServer::doGetObjectPropValue() {
481    MtpObjectHandle handle = mRequest.getParameter(1);
482    MtpObjectProperty property = mRequest.getParameter(2);
483
484    return mDatabase->getObjectProperty(handle, property, mData);
485}
486
487MtpResponseCode MtpServer::doGetObjectInfo() {
488    MtpObjectHandle handle = mRequest.getParameter(1);
489    return mDatabase->getObjectInfo(handle, mData);
490}
491
492MtpResponseCode MtpServer::doGetObject() {
493    MtpObjectHandle handle = mRequest.getParameter(1);
494    MtpString pathBuf;
495    int64_t fileLength;
496    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
497    if (result != MTP_RESPONSE_OK)
498        return result;
499
500    const char* filePath = (const char *)pathBuf;
501    mtp_file_range  mfr;
502    mfr.fd = open(filePath, O_RDONLY);
503    if (mfr.fd < 0) {
504        return MTP_RESPONSE_GENERAL_ERROR;
505    }
506    mfr.offset = 0;
507    mfr.length = fileLength;
508
509    // send data header
510    mData.setOperationCode(mRequest.getOperationCode());
511    mData.setTransactionID(mRequest.getTransactionID());
512    mData.writeDataHeader(mFD, fileLength);
513
514    // then transfer the file
515    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
516    close(mfr.fd);
517    if (ret < 0) {
518        if (errno == ECANCELED)
519            return MTP_RESPONSE_TRANSACTION_CANCELLED;
520        else
521            return MTP_RESPONSE_GENERAL_ERROR;
522    }
523    return MTP_RESPONSE_OK;
524}
525
526MtpResponseCode MtpServer::doSendObjectInfo() {
527    MtpString path;
528    MtpStorageID storageID = mRequest.getParameter(1);
529    MtpStorage* storage = getStorage(storageID);
530    MtpObjectHandle parent = mRequest.getParameter(2);
531    if (!storage)
532        return MTP_RESPONSE_INVALID_STORAGE_ID;
533
534    // special case the root
535    if (parent == MTP_PARENT_ROOT) {
536        path = storage->getPath();
537        parent = 0;
538    } else {
539        int64_t dummy;
540        int result = mDatabase->getObjectFilePath(parent, path, dummy);
541        if (result != MTP_RESPONSE_OK)
542            return result;
543    }
544
545    // read only the fields we need
546    mData.getUInt32();  // storage ID
547    MtpObjectFormat format = mData.getUInt16();
548    mData.getUInt16();  // protection status
549    mSendObjectFileSize = mData.getUInt32();
550    mData.getUInt16();  // thumb format
551    mData.getUInt32();  // thumb compressed size
552    mData.getUInt32();  // thumb pix width
553    mData.getUInt32();  // thumb pix height
554    mData.getUInt32();  // image pix width
555    mData.getUInt32();  // image pix height
556    mData.getUInt32();  // image bit depth
557    mData.getUInt32();  // parent
558    uint16_t associationType = mData.getUInt16();
559    uint32_t associationDesc = mData.getUInt32();   // association desc
560    mData.getUInt32();  // sequence number
561    MtpStringBuffer name, created, modified;
562    mData.getString(name);    // file name
563    mData.getString(created);      // date created
564    mData.getString(modified);     // date modified
565    // keywords follow
566
567    time_t modifiedTime;
568    if (!parseDateTime(modified, modifiedTime))
569        modifiedTime = 0;
570
571    if (path[path.size() - 1] != '/')
572        path += "/";
573    path += (const char *)name;
574
575    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
576            format, parent, storageID, mSendObjectFileSize, modifiedTime);
577    if (handle == kInvalidObjectHandle) {
578        return MTP_RESPONSE_GENERAL_ERROR;
579    }
580
581  if (format == MTP_FORMAT_ASSOCIATION) {
582        mode_t mask = umask(0);
583        int ret = mkdir((const char *)path, mDirectoryPermission);
584        umask(mask);
585        if (ret && ret != -EEXIST)
586            return MTP_RESPONSE_GENERAL_ERROR;
587        chown((const char *)path, getuid(), mFileGroup);
588    } else {
589        mSendObjectFilePath = path;
590        // save the handle for the SendObject call, which should follow
591        mSendObjectHandle = handle;
592        mSendObjectFormat = format;
593    }
594
595    mResponse.setParameter(1, storageID);
596    mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
597    mResponse.setParameter(3, handle);
598
599    return MTP_RESPONSE_OK;
600}
601
602MtpResponseCode MtpServer::doSendObject() {
603    MtpResponseCode result = MTP_RESPONSE_OK;
604    mode_t mask;
605    int ret;
606
607    if (mSendObjectHandle == kInvalidObjectHandle) {
608        LOGE("Expected SendObjectInfo before SendObject");
609        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
610        goto done;
611    }
612
613    // read the header
614    ret = mData.readDataHeader(mFD);
615    // FIXME - check for errors here.
616
617    // reset so we don't attempt to send this back
618    mData.reset();
619
620    mtp_file_range  mfr;
621    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
622    if (mfr.fd < 0) {
623        result = MTP_RESPONSE_GENERAL_ERROR;
624        goto done;
625    }
626    fchown(mfr.fd, getuid(), mFileGroup);
627    // set permissions
628    mask = umask(0);
629    fchmod(mfr.fd, mFilePermission);
630    umask(mask);
631
632    mfr.offset = 0;
633    mfr.length = mSendObjectFileSize;
634
635    // transfer the file
636    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
637    close(mfr.fd);
638
639    LOGV("MTP_RECEIVE_FILE returned %d", ret);
640
641    if (ret < 0) {
642        unlink(mSendObjectFilePath);
643        if (errno == ECANCELED)
644            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
645        else
646            result = MTP_RESPONSE_GENERAL_ERROR;
647    }
648
649done:
650    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
651            result == MTP_RESPONSE_OK);
652    mSendObjectHandle = kInvalidObjectHandle;
653    mSendObjectFormat = 0;
654    return result;
655}
656
657MtpResponseCode MtpServer::doDeleteObject() {
658    MtpObjectHandle handle = mRequest.getParameter(1);
659    MtpObjectFormat format = mRequest.getParameter(1);
660    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
661    // FIXME - implement deleting objects by format
662    // FIXME - handle non-empty directories
663
664    MtpString filePath;
665    int64_t fileLength;
666    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
667    if (result == MTP_RESPONSE_OK) {
668        LOGV("deleting %s", (const char *)filePath);
669        // one of these should work
670        rmdir((const char *)filePath);
671        unlink((const char *)filePath);
672        return mDatabase->deleteFile(handle);
673    } else {
674        return result;
675    }
676}
677
678MtpResponseCode MtpServer::doGetObjectPropDesc() {
679    MtpObjectProperty propCode = mRequest.getParameter(1);
680    MtpObjectFormat format = mRequest.getParameter(2);
681    MtpProperty* property = getObjectProperty(propCode);
682    if (!property)
683        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
684
685    property->write(mData);
686    return MTP_RESPONSE_OK;
687}
688
689}  // namespace android
690