Home | History | Annotate | Download | only in mtp
      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 "MtpObjectInfo.h"
     34 #include "MtpProperty.h"
     35 #include "MtpServer.h"
     36 #include "MtpStorage.h"
     37 #include "MtpStringBuffer.h"
     38 
     39 #include <linux/usb/f_mtp.h>
     40 
     41 namespace android {
     42 
     43 static const MtpOperationCode kSupportedOperationCodes[] = {
     44     MTP_OPERATION_GET_DEVICE_INFO,
     45     MTP_OPERATION_OPEN_SESSION,
     46     MTP_OPERATION_CLOSE_SESSION,
     47     MTP_OPERATION_GET_STORAGE_IDS,
     48     MTP_OPERATION_GET_STORAGE_INFO,
     49     MTP_OPERATION_GET_NUM_OBJECTS,
     50     MTP_OPERATION_GET_OBJECT_HANDLES,
     51     MTP_OPERATION_GET_OBJECT_INFO,
     52     MTP_OPERATION_GET_OBJECT,
     53     MTP_OPERATION_GET_THUMB,
     54     MTP_OPERATION_DELETE_OBJECT,
     55     MTP_OPERATION_SEND_OBJECT_INFO,
     56     MTP_OPERATION_SEND_OBJECT,
     57 //    MTP_OPERATION_INITIATE_CAPTURE,
     58 //    MTP_OPERATION_FORMAT_STORE,
     59 //    MTP_OPERATION_RESET_DEVICE,
     60 //    MTP_OPERATION_SELF_TEST,
     61 //    MTP_OPERATION_SET_OBJECT_PROTECTION,
     62 //    MTP_OPERATION_POWER_DOWN,
     63     MTP_OPERATION_GET_DEVICE_PROP_DESC,
     64     MTP_OPERATION_GET_DEVICE_PROP_VALUE,
     65     MTP_OPERATION_SET_DEVICE_PROP_VALUE,
     66     MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
     67 //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
     68 //    MTP_OPERATION_MOVE_OBJECT,
     69 //    MTP_OPERATION_COPY_OBJECT,
     70     MTP_OPERATION_GET_PARTIAL_OBJECT,
     71 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     72     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
     73     MTP_OPERATION_GET_OBJECT_PROP_DESC,
     74     MTP_OPERATION_GET_OBJECT_PROP_VALUE,
     75     MTP_OPERATION_SET_OBJECT_PROP_VALUE,
     76     MTP_OPERATION_GET_OBJECT_PROP_LIST,
     77 //    MTP_OPERATION_SET_OBJECT_PROP_LIST,
     78 //    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
     79 //    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
     80     MTP_OPERATION_GET_OBJECT_REFERENCES,
     81     MTP_OPERATION_SET_OBJECT_REFERENCES,
     82 //    MTP_OPERATION_SKIP,
     83     // Android extension for direct file IO
     84     MTP_OPERATION_GET_PARTIAL_OBJECT_64,
     85     MTP_OPERATION_SEND_PARTIAL_OBJECT,
     86     MTP_OPERATION_TRUNCATE_OBJECT,
     87     MTP_OPERATION_BEGIN_EDIT_OBJECT,
     88     MTP_OPERATION_END_EDIT_OBJECT,
     89 };
     90 
     91 static const MtpEventCode kSupportedEventCodes[] = {
     92     MTP_EVENT_OBJECT_ADDED,
     93     MTP_EVENT_OBJECT_REMOVED,
     94     MTP_EVENT_STORE_ADDED,
     95     MTP_EVENT_STORE_REMOVED,
     96 };
     97 
     98 MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
     99                     int fileGroup, int filePerm, int directoryPerm)
    100     :   mFD(fd),
    101         mDatabase(database),
    102         mPtp(ptp),
    103         mFileGroup(fileGroup),
    104         mFilePermission(filePerm),
    105         mDirectoryPermission(directoryPerm),
    106         mSessionID(0),
    107         mSessionOpen(false),
    108         mSendObjectHandle(kInvalidObjectHandle),
    109         mSendObjectFormat(0),
    110         mSendObjectFileSize(0)
    111 {
    112 }
    113 
    114 MtpServer::~MtpServer() {
    115 }
    116 
    117 void MtpServer::addStorage(MtpStorage* storage) {
    118     Mutex::Autolock autoLock(mMutex);
    119 
    120     mStorages.push(storage);
    121     sendStoreAdded(storage->getStorageID());
    122 }
    123 
    124 void MtpServer::removeStorage(MtpStorage* storage) {
    125     Mutex::Autolock autoLock(mMutex);
    126 
    127     for (int i = 0; i < mStorages.size(); i++) {
    128         if (mStorages[i] == storage) {
    129             mStorages.removeAt(i);
    130             sendStoreRemoved(storage->getStorageID());
    131             break;
    132         }
    133     }
    134 }
    135 
    136 MtpStorage* MtpServer::getStorage(MtpStorageID id) {
    137     if (id == 0)
    138         return mStorages[0];
    139     for (int i = 0; i < mStorages.size(); i++) {
    140         MtpStorage* storage = mStorages[i];
    141         if (storage->getStorageID() == id)
    142             return storage;
    143     }
    144     return NULL;
    145 }
    146 
    147 bool MtpServer::hasStorage(MtpStorageID id) {
    148     if (id == 0 || id == 0xFFFFFFFF)
    149         return mStorages.size() > 0;
    150     return (getStorage(id) != NULL);
    151 }
    152 
    153 void MtpServer::run() {
    154     int fd = mFD;
    155 
    156     LOGV("MtpServer::run fd: %d\n", fd);
    157 
    158     while (1) {
    159         int ret = mRequest.read(fd);
    160         if (ret < 0) {
    161             LOGV("request read returned %d, errno: %d", ret, errno);
    162             if (errno == ECANCELED) {
    163                 // return to top of loop and wait for next command
    164                 continue;
    165             }
    166             break;
    167         }
    168         MtpOperationCode operation = mRequest.getOperationCode();
    169         MtpTransactionID transaction = mRequest.getTransactionID();
    170 
    171         LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
    172         mRequest.dump();
    173 
    174         // FIXME need to generalize this
    175         bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
    176                     || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
    177                     || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
    178                     || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
    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             mResponse.dump();
    216             if (ret < 0) {
    217                 LOGE("request write returned %d, errno: %d", ret, errno);
    218                 if (errno == ECANCELED) {
    219                     // return to top of loop and wait for next command
    220                     continue;
    221                 }
    222                 break;
    223             }
    224         } else {
    225             LOGV("skipping response\n");
    226         }
    227     }
    228 
    229     // commit any open edits
    230     int count = mObjectEditList.size();
    231     for (int i = 0; i < count; i++) {
    232         ObjectEdit* edit = mObjectEditList[i];
    233         commitEdit(edit);
    234         delete edit;
    235     }
    236     mObjectEditList.clear();
    237 
    238     if (mSessionOpen)
    239         mDatabase->sessionEnded();
    240     close(fd);
    241     mFD = -1;
    242 }
    243 
    244 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
    245     LOGV("sendObjectAdded %d\n", handle);
    246     sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
    247 }
    248 
    249 void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
    250     LOGV("sendObjectRemoved %d\n", handle);
    251     sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
    252 }
    253 
    254 void MtpServer::sendStoreAdded(MtpStorageID id) {
    255     LOGV("sendStoreAdded %08X\n", id);
    256     sendEvent(MTP_EVENT_STORE_ADDED, id);
    257 }
    258 
    259 void MtpServer::sendStoreRemoved(MtpStorageID id) {
    260     LOGV("sendStoreRemoved %08X\n", id);
    261     sendEvent(MTP_EVENT_STORE_REMOVED, id);
    262 }
    263 
    264 void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
    265     if (mSessionOpen) {
    266         mEvent.setEventCode(code);
    267         mEvent.setTransactionID(mRequest.getTransactionID());
    268         mEvent.setParameter(1, param1);
    269         int ret = mEvent.write(mFD);
    270         LOGV("mEvent.write returned %d\n", ret);
    271     }
    272 }
    273 
    274 void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
    275         uint64_t size, MtpObjectFormat format, int fd) {
    276     ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
    277     mObjectEditList.add(edit);
    278 }
    279 
    280 MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
    281     int count = mObjectEditList.size();
    282     for (int i = 0; i < count; i++) {
    283         ObjectEdit* edit = mObjectEditList[i];
    284         if (edit->mHandle == handle) return edit;
    285     }
    286     return NULL;
    287 }
    288 
    289 void MtpServer::removeEditObject(MtpObjectHandle handle) {
    290     int count = mObjectEditList.size();
    291     for (int i = 0; i < count; i++) {
    292         ObjectEdit* edit = mObjectEditList[i];
    293         if (edit->mHandle == handle) {
    294             delete edit;
    295             mObjectEditList.removeAt(i);
    296             return;
    297         }
    298     }
    299     LOGE("ObjectEdit not found in removeEditObject");
    300 }
    301 
    302 void MtpServer::commitEdit(ObjectEdit* edit) {
    303     mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
    304 }
    305 
    306 
    307 bool MtpServer::handleRequest() {
    308     Mutex::Autolock autoLock(mMutex);
    309 
    310     MtpOperationCode operation = mRequest.getOperationCode();
    311     MtpResponseCode response;
    312 
    313     mResponse.reset();
    314 
    315     if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
    316         // FIXME - need to delete mSendObjectHandle from the database
    317         LOGE("expected SendObject after SendObjectInfo");
    318         mSendObjectHandle = kInvalidObjectHandle;
    319     }
    320 
    321     switch (operation) {
    322         case MTP_OPERATION_GET_DEVICE_INFO:
    323             response = doGetDeviceInfo();
    324             break;
    325         case MTP_OPERATION_OPEN_SESSION:
    326             response = doOpenSession();
    327             break;
    328         case MTP_OPERATION_CLOSE_SESSION:
    329             response = doCloseSession();
    330             break;
    331         case MTP_OPERATION_GET_STORAGE_IDS:
    332             response = doGetStorageIDs();
    333             break;
    334          case MTP_OPERATION_GET_STORAGE_INFO:
    335             response = doGetStorageInfo();
    336             break;
    337         case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
    338             response = doGetObjectPropsSupported();
    339             break;
    340         case MTP_OPERATION_GET_OBJECT_HANDLES:
    341             response = doGetObjectHandles();
    342             break;
    343         case MTP_OPERATION_GET_NUM_OBJECTS:
    344             response = doGetNumObjects();
    345             break;
    346         case MTP_OPERATION_GET_OBJECT_REFERENCES:
    347             response = doGetObjectReferences();
    348             break;
    349         case MTP_OPERATION_SET_OBJECT_REFERENCES:
    350             response = doSetObjectReferences();
    351             break;
    352         case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
    353             response = doGetObjectPropValue();
    354             break;
    355         case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
    356             response = doSetObjectPropValue();
    357             break;
    358         case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
    359             response = doGetDevicePropValue();
    360             break;
    361         case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
    362             response = doSetDevicePropValue();
    363             break;
    364         case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
    365             response = doResetDevicePropValue();
    366             break;
    367         case MTP_OPERATION_GET_OBJECT_PROP_LIST:
    368             response = doGetObjectPropList();
    369             break;
    370         case MTP_OPERATION_GET_OBJECT_INFO:
    371             response = doGetObjectInfo();
    372             break;
    373         case MTP_OPERATION_GET_OBJECT:
    374             response = doGetObject();
    375             break;
    376         case MTP_OPERATION_GET_THUMB:
    377             response = doGetThumb();
    378             break;
    379         case MTP_OPERATION_GET_PARTIAL_OBJECT:
    380         case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
    381             response = doGetPartialObject(operation);
    382             break;
    383         case MTP_OPERATION_SEND_OBJECT_INFO:
    384             response = doSendObjectInfo();
    385             break;
    386         case MTP_OPERATION_SEND_OBJECT:
    387             response = doSendObject();
    388             break;
    389         case MTP_OPERATION_DELETE_OBJECT:
    390             response = doDeleteObject();
    391             break;
    392         case MTP_OPERATION_GET_OBJECT_PROP_DESC:
    393             response = doGetObjectPropDesc();
    394             break;
    395         case MTP_OPERATION_GET_DEVICE_PROP_DESC:
    396             response = doGetDevicePropDesc();
    397             break;
    398         case MTP_OPERATION_SEND_PARTIAL_OBJECT:
    399             response = doSendPartialObject();
    400             break;
    401         case MTP_OPERATION_TRUNCATE_OBJECT:
    402             response = doTruncateObject();
    403             break;
    404         case MTP_OPERATION_BEGIN_EDIT_OBJECT:
    405             response = doBeginEditObject();
    406             break;
    407         case MTP_OPERATION_END_EDIT_OBJECT:
    408             response = doEndEditObject();
    409             break;
    410         default:
    411             LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
    412             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
    413             break;
    414     }
    415 
    416     if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
    417         return false;
    418     mResponse.setResponseCode(response);
    419     return true;
    420 }
    421 
    422 MtpResponseCode MtpServer::doGetDeviceInfo() {
    423     MtpStringBuffer   string;
    424     char prop_value[PROPERTY_VALUE_MAX];
    425 
    426     MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
    427     MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
    428     MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
    429 
    430     // fill in device info
    431     mData.putUInt16(MTP_STANDARD_VERSION);
    432     if (mPtp) {
    433         mData.putUInt32(0);
    434     } else {
    435         // MTP Vendor Extension ID
    436         mData.putUInt32(6);
    437     }
    438     mData.putUInt16(MTP_STANDARD_VERSION);
    439     if (mPtp) {
    440         // no extensions
    441         string.set("");
    442     } else {
    443         // MTP extensions
    444         string.set("microsoft.com: 1.0; android.com: 1.0;");
    445     }
    446     mData.putString(string); // MTP Extensions
    447     mData.putUInt16(0); //Functional Mode
    448     mData.putAUInt16(kSupportedOperationCodes,
    449             sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
    450     mData.putAUInt16(kSupportedEventCodes,
    451             sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
    452     mData.putAUInt16(deviceProperties); // Device Properties Supported
    453     mData.putAUInt16(captureFormats); // Capture Formats
    454     mData.putAUInt16(playbackFormats);  // Playback Formats
    455 
    456     property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
    457     string.set(prop_value);
    458     mData.putString(string);   // Manufacturer
    459 
    460     property_get("ro.product.model", prop_value, "MTP Device");
    461     string.set(prop_value);
    462     mData.putString(string);   // Model
    463     string.set("1.0");
    464     mData.putString(string);   // Device Version
    465 
    466     property_get("ro.serialno", prop_value, "????????");
    467     string.set(prop_value);
    468     mData.putString(string);   // Serial Number
    469 
    470     delete playbackFormats;
    471     delete captureFormats;
    472     delete deviceProperties;
    473 
    474     return MTP_RESPONSE_OK;
    475 }
    476 
    477 MtpResponseCode MtpServer::doOpenSession() {
    478     if (mSessionOpen) {
    479         mResponse.setParameter(1, mSessionID);
    480         return MTP_RESPONSE_SESSION_ALREADY_OPEN;
    481     }
    482     mSessionID = mRequest.getParameter(1);
    483     mSessionOpen = true;
    484 
    485     mDatabase->sessionStarted();
    486 
    487     return MTP_RESPONSE_OK;
    488 }
    489 
    490 MtpResponseCode MtpServer::doCloseSession() {
    491     if (!mSessionOpen)
    492         return MTP_RESPONSE_SESSION_NOT_OPEN;
    493     mSessionID = 0;
    494     mSessionOpen = false;
    495     mDatabase->sessionEnded();
    496     return MTP_RESPONSE_OK;
    497 }
    498 
    499 MtpResponseCode MtpServer::doGetStorageIDs() {
    500     if (!mSessionOpen)
    501         return MTP_RESPONSE_SESSION_NOT_OPEN;
    502 
    503     int count = mStorages.size();
    504     mData.putUInt32(count);
    505     for (int i = 0; i < count; i++)
    506         mData.putUInt32(mStorages[i]->getStorageID());
    507 
    508     return MTP_RESPONSE_OK;
    509 }
    510 
    511 MtpResponseCode MtpServer::doGetStorageInfo() {
    512     MtpStringBuffer   string;
    513 
    514     if (!mSessionOpen)
    515         return MTP_RESPONSE_SESSION_NOT_OPEN;
    516     MtpStorageID id = mRequest.getParameter(1);
    517     MtpStorage* storage = getStorage(id);
    518     if (!storage)
    519         return MTP_RESPONSE_INVALID_STORAGE_ID;
    520 
    521     mData.putUInt16(storage->getType());
    522     mData.putUInt16(storage->getFileSystemType());
    523     mData.putUInt16(storage->getAccessCapability());
    524     mData.putUInt64(storage->getMaxCapacity());
    525     mData.putUInt64(storage->getFreeSpace());
    526     mData.putUInt32(1024*1024*1024); // Free Space in Objects
    527     string.set(storage->getDescription());
    528     mData.putString(string);
    529     mData.putEmptyString();   // Volume Identifier
    530 
    531     return MTP_RESPONSE_OK;
    532 }
    533 
    534 MtpResponseCode MtpServer::doGetObjectPropsSupported() {
    535     if (!mSessionOpen)
    536         return MTP_RESPONSE_SESSION_NOT_OPEN;
    537     MtpObjectFormat format = mRequest.getParameter(1);
    538     MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
    539     mData.putAUInt16(properties);
    540     delete properties;
    541     return MTP_RESPONSE_OK;
    542 }
    543 
    544 MtpResponseCode MtpServer::doGetObjectHandles() {
    545     if (!mSessionOpen)
    546         return MTP_RESPONSE_SESSION_NOT_OPEN;
    547     MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
    548     MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
    549     MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
    550                                                             // 0x00000000 for all objects
    551 
    552     if (!hasStorage(storageID))
    553         return MTP_RESPONSE_INVALID_STORAGE_ID;
    554 
    555     MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
    556     mData.putAUInt32(handles);
    557     delete handles;
    558     return MTP_RESPONSE_OK;
    559 }
    560 
    561 MtpResponseCode MtpServer::doGetNumObjects() {
    562     if (!mSessionOpen)
    563         return MTP_RESPONSE_SESSION_NOT_OPEN;
    564     MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
    565     MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
    566     MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
    567                                                             // 0x00000000 for all objects
    568     if (!hasStorage(storageID))
    569         return MTP_RESPONSE_INVALID_STORAGE_ID;
    570 
    571     int count = mDatabase->getNumObjects(storageID, format, parent);
    572     if (count >= 0) {
    573         mResponse.setParameter(1, count);
    574         return MTP_RESPONSE_OK;
    575     } else {
    576         mResponse.setParameter(1, 0);
    577         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    578     }
    579 }
    580 
    581 MtpResponseCode MtpServer::doGetObjectReferences() {
    582     if (!mSessionOpen)
    583         return MTP_RESPONSE_SESSION_NOT_OPEN;
    584     if (!hasStorage())
    585         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    586     MtpObjectHandle handle = mRequest.getParameter(1);
    587 
    588     // FIXME - check for invalid object handle
    589     MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
    590     if (handles) {
    591         mData.putAUInt32(handles);
    592         delete handles;
    593     } else {
    594         mData.putEmptyArray();
    595     }
    596     return MTP_RESPONSE_OK;
    597 }
    598 
    599 MtpResponseCode MtpServer::doSetObjectReferences() {
    600     if (!mSessionOpen)
    601         return MTP_RESPONSE_SESSION_NOT_OPEN;
    602     if (!hasStorage())
    603         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    604     MtpStorageID handle = mRequest.getParameter(1);
    605 
    606     MtpObjectHandleList* references = mData.getAUInt32();
    607     MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
    608     delete references;
    609     return result;
    610 }
    611 
    612 MtpResponseCode MtpServer::doGetObjectPropValue() {
    613     if (!hasStorage())
    614         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    615     MtpObjectHandle handle = mRequest.getParameter(1);
    616     MtpObjectProperty property = mRequest.getParameter(2);
    617     LOGV("GetObjectPropValue %d %s\n", handle,
    618             MtpDebug::getObjectPropCodeName(property));
    619 
    620     return mDatabase->getObjectPropertyValue(handle, property, mData);
    621 }
    622 
    623 MtpResponseCode MtpServer::doSetObjectPropValue() {
    624     if (!hasStorage())
    625         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    626     MtpObjectHandle handle = mRequest.getParameter(1);
    627     MtpObjectProperty property = mRequest.getParameter(2);
    628     LOGV("SetObjectPropValue %d %s\n", handle,
    629             MtpDebug::getObjectPropCodeName(property));
    630 
    631     return mDatabase->setObjectPropertyValue(handle, property, mData);
    632 }
    633 
    634 MtpResponseCode MtpServer::doGetDevicePropValue() {
    635     MtpDeviceProperty property = mRequest.getParameter(1);
    636     LOGV("GetDevicePropValue %s\n",
    637             MtpDebug::getDevicePropCodeName(property));
    638 
    639     return mDatabase->getDevicePropertyValue(property, mData);
    640 }
    641 
    642 MtpResponseCode MtpServer::doSetDevicePropValue() {
    643     MtpDeviceProperty property = mRequest.getParameter(1);
    644     LOGV("SetDevicePropValue %s\n",
    645             MtpDebug::getDevicePropCodeName(property));
    646 
    647     return mDatabase->setDevicePropertyValue(property, mData);
    648 }
    649 
    650 MtpResponseCode MtpServer::doResetDevicePropValue() {
    651     MtpDeviceProperty property = mRequest.getParameter(1);
    652     LOGV("ResetDevicePropValue %s\n",
    653             MtpDebug::getDevicePropCodeName(property));
    654 
    655     return mDatabase->resetDeviceProperty(property);
    656 }
    657 
    658 MtpResponseCode MtpServer::doGetObjectPropList() {
    659     if (!hasStorage())
    660         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    661 
    662     MtpObjectHandle handle = mRequest.getParameter(1);
    663     // use uint32_t so we can support 0xFFFFFFFF
    664     uint32_t format = mRequest.getParameter(2);
    665     uint32_t property = mRequest.getParameter(3);
    666     int groupCode = mRequest.getParameter(4);
    667     int depth = mRequest.getParameter(5);
    668    LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
    669             handle, MtpDebug::getFormatCodeName(format),
    670             MtpDebug::getObjectPropCodeName(property), groupCode, depth);
    671 
    672     return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
    673 }
    674 
    675 MtpResponseCode MtpServer::doGetObjectInfo() {
    676     if (!hasStorage())
    677         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    678     MtpObjectHandle handle = mRequest.getParameter(1);
    679     MtpObjectInfo info(handle);
    680     MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
    681     if (result == MTP_RESPONSE_OK) {
    682         char    date[20];
    683 
    684         mData.putUInt32(info.mStorageID);
    685         mData.putUInt16(info.mFormat);
    686         mData.putUInt16(info.mProtectionStatus);
    687 
    688         // if object is being edited the database size may be out of date
    689         uint32_t size = info.mCompressedSize;
    690         ObjectEdit* edit = getEditObject(handle);
    691         if (edit)
    692             size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
    693         mData.putUInt32(size);
    694 
    695         mData.putUInt16(info.mThumbFormat);
    696         mData.putUInt32(info.mThumbCompressedSize);
    697         mData.putUInt32(info.mThumbPixWidth);
    698         mData.putUInt32(info.mThumbPixHeight);
    699         mData.putUInt32(info.mImagePixWidth);
    700         mData.putUInt32(info.mImagePixHeight);
    701         mData.putUInt32(info.mImagePixDepth);
    702         mData.putUInt32(info.mParent);
    703         mData.putUInt16(info.mAssociationType);
    704         mData.putUInt32(info.mAssociationDesc);
    705         mData.putUInt32(info.mSequenceNumber);
    706         mData.putString(info.mName);
    707         mData.putEmptyString();    // date created
    708         formatDateTime(info.mDateModified, date, sizeof(date));
    709         mData.putString(date);   // date modified
    710         mData.putEmptyString();   // keywords
    711     }
    712     return result;
    713 }
    714 
    715 MtpResponseCode MtpServer::doGetObject() {
    716     if (!hasStorage())
    717         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    718     MtpObjectHandle handle = mRequest.getParameter(1);
    719     MtpString pathBuf;
    720     int64_t fileLength;
    721     MtpObjectFormat format;
    722     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
    723     if (result != MTP_RESPONSE_OK)
    724         return result;
    725 
    726     const char* filePath = (const char *)pathBuf;
    727     mtp_file_range  mfr;
    728     mfr.fd = open(filePath, O_RDONLY);
    729     if (mfr.fd < 0) {
    730         return MTP_RESPONSE_GENERAL_ERROR;
    731     }
    732     mfr.offset = 0;
    733     mfr.length = fileLength;
    734     mfr.command = mRequest.getOperationCode();
    735     mfr.transaction_id = mRequest.getTransactionID();
    736 
    737     // then transfer the file
    738     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
    739     LOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
    740     close(mfr.fd);
    741     if (ret < 0) {
    742         if (errno == ECANCELED)
    743             return MTP_RESPONSE_TRANSACTION_CANCELLED;
    744         else
    745             return MTP_RESPONSE_GENERAL_ERROR;
    746     }
    747     return MTP_RESPONSE_OK;
    748 }
    749 
    750 MtpResponseCode MtpServer::doGetThumb() {
    751     MtpObjectHandle handle = mRequest.getParameter(1);
    752     size_t thumbSize;
    753     void* thumb = mDatabase->getThumbnail(handle, thumbSize);
    754     if (thumb) {
    755         // send data
    756         mData.setOperationCode(mRequest.getOperationCode());
    757         mData.setTransactionID(mRequest.getTransactionID());
    758         mData.writeData(mFD, thumb, thumbSize);
    759         free(thumb);
    760         return MTP_RESPONSE_OK;
    761     } else {
    762         return MTP_RESPONSE_GENERAL_ERROR;
    763     }
    764 }
    765 
    766 MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
    767     if (!hasStorage())
    768         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    769     MtpObjectHandle handle = mRequest.getParameter(1);
    770     uint64_t offset;
    771     uint32_t length;
    772     offset = mRequest.getParameter(2);
    773     if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
    774         // android extension with 64 bit offset
    775         uint64_t offset2 = mRequest.getParameter(3);
    776         offset = offset | (offset2 << 32);
    777         length = mRequest.getParameter(4);
    778     } else {
    779         // standard GetPartialObject
    780         length = mRequest.getParameter(3);
    781     }
    782     MtpString pathBuf;
    783     int64_t fileLength;
    784     MtpObjectFormat format;
    785     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
    786     if (result != MTP_RESPONSE_OK)
    787         return result;
    788     if (offset + length > fileLength)
    789         length = fileLength - offset;
    790 
    791     const char* filePath = (const char *)pathBuf;
    792     mtp_file_range  mfr;
    793     mfr.fd = open(filePath, O_RDONLY);
    794     if (mfr.fd < 0) {
    795         return MTP_RESPONSE_GENERAL_ERROR;
    796     }
    797     mfr.offset = offset;
    798     mfr.length = length;
    799     mfr.command = mRequest.getOperationCode();
    800     mfr.transaction_id = mRequest.getTransactionID();
    801     mResponse.setParameter(1, length);
    802 
    803     // transfer the file
    804     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
    805     LOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
    806     close(mfr.fd);
    807     if (ret < 0) {
    808         if (errno == ECANCELED)
    809             return MTP_RESPONSE_TRANSACTION_CANCELLED;
    810         else
    811             return MTP_RESPONSE_GENERAL_ERROR;
    812     }
    813     return MTP_RESPONSE_OK;
    814 }
    815 
    816 MtpResponseCode MtpServer::doSendObjectInfo() {
    817     MtpString path;
    818     MtpStorageID storageID = mRequest.getParameter(1);
    819     MtpStorage* storage = getStorage(storageID);
    820     MtpObjectHandle parent = mRequest.getParameter(2);
    821     if (!storage)
    822         return MTP_RESPONSE_INVALID_STORAGE_ID;
    823 
    824     // special case the root
    825     if (parent == MTP_PARENT_ROOT) {
    826         path = storage->getPath();
    827         parent = 0;
    828     } else {
    829         int64_t length;
    830         MtpObjectFormat format;
    831         int result = mDatabase->getObjectFilePath(parent, path, length, format);
    832         if (result != MTP_RESPONSE_OK)
    833             return result;
    834         if (format != MTP_FORMAT_ASSOCIATION)
    835             return MTP_RESPONSE_INVALID_PARENT_OBJECT;
    836     }
    837 
    838     // read only the fields we need
    839     mData.getUInt32();  // storage ID
    840     MtpObjectFormat format = mData.getUInt16();
    841     mData.getUInt16();  // protection status
    842     mSendObjectFileSize = mData.getUInt32();
    843     mData.getUInt16();  // thumb format
    844     mData.getUInt32();  // thumb compressed size
    845     mData.getUInt32();  // thumb pix width
    846     mData.getUInt32();  // thumb pix height
    847     mData.getUInt32();  // image pix width
    848     mData.getUInt32();  // image pix height
    849     mData.getUInt32();  // image bit depth
    850     mData.getUInt32();  // parent
    851     uint16_t associationType = mData.getUInt16();
    852     uint32_t associationDesc = mData.getUInt32();   // association desc
    853     mData.getUInt32();  // sequence number
    854     MtpStringBuffer name, created, modified;
    855     mData.getString(name);    // file name
    856     mData.getString(created);      // date created
    857     mData.getString(modified);     // date modified
    858     // keywords follow
    859 
    860     LOGV("name: %s format: %04X\n", (const char *)name, format);
    861     time_t modifiedTime;
    862     if (!parseDateTime(modified, modifiedTime))
    863         modifiedTime = 0;
    864 
    865     if (path[path.size() - 1] != '/')
    866         path += "/";
    867     path += (const char *)name;
    868 
    869     // check space first
    870     if (mSendObjectFileSize > storage->getFreeSpace())
    871         return MTP_RESPONSE_STORAGE_FULL;
    872     uint64_t maxFileSize = storage->getMaxFileSize();
    873     // check storage max file size
    874     if (maxFileSize != 0) {
    875         // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
    876         // is >= 0xFFFFFFFF
    877         if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
    878             return MTP_RESPONSE_OBJECT_TOO_LARGE;
    879     }
    880 
    881 LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
    882     MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
    883             format, parent, storageID, mSendObjectFileSize, modifiedTime);
    884     if (handle == kInvalidObjectHandle) {
    885         return MTP_RESPONSE_GENERAL_ERROR;
    886     }
    887 
    888   if (format == MTP_FORMAT_ASSOCIATION) {
    889         mode_t mask = umask(0);
    890         int ret = mkdir((const char *)path, mDirectoryPermission);
    891         umask(mask);
    892         if (ret && ret != -EEXIST)
    893             return MTP_RESPONSE_GENERAL_ERROR;
    894         chown((const char *)path, getuid(), mFileGroup);
    895 
    896         // SendObject does not get sent for directories, so call endSendObject here instead
    897         mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
    898     } else {
    899         mSendObjectFilePath = path;
    900         // save the handle for the SendObject call, which should follow
    901         mSendObjectHandle = handle;
    902         mSendObjectFormat = format;
    903     }
    904 
    905     mResponse.setParameter(1, storageID);
    906     mResponse.setParameter(2, parent);
    907     mResponse.setParameter(3, handle);
    908 
    909     return MTP_RESPONSE_OK;
    910 }
    911 
    912 MtpResponseCode MtpServer::doSendObject() {
    913     if (!hasStorage())
    914         return MTP_RESPONSE_GENERAL_ERROR;
    915     MtpResponseCode result = MTP_RESPONSE_OK;
    916     mode_t mask;
    917     int ret, initialData;
    918 
    919     if (mSendObjectHandle == kInvalidObjectHandle) {
    920         LOGE("Expected SendObjectInfo before SendObject");
    921         result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
    922         goto done;
    923     }
    924 
    925     // read the header, and possibly some data
    926     ret = mData.read(mFD);
    927     if (ret < MTP_CONTAINER_HEADER_SIZE) {
    928         result = MTP_RESPONSE_GENERAL_ERROR;
    929         goto done;
    930     }
    931     initialData = ret - MTP_CONTAINER_HEADER_SIZE;
    932 
    933     mtp_file_range  mfr;
    934     mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
    935     if (mfr.fd < 0) {
    936         result = MTP_RESPONSE_GENERAL_ERROR;
    937         goto done;
    938     }
    939     fchown(mfr.fd, getuid(), mFileGroup);
    940     // set permissions
    941     mask = umask(0);
    942     fchmod(mfr.fd, mFilePermission);
    943     umask(mask);
    944 
    945     if (initialData > 0)
    946         ret = write(mfr.fd, mData.getData(), initialData);
    947 
    948     if (mSendObjectFileSize - initialData > 0) {
    949         mfr.offset = initialData;
    950         if (mSendObjectFileSize == 0xFFFFFFFF) {
    951             // tell driver to read until it receives a short packet
    952             mfr.length = 0xFFFFFFFF;
    953         } else {
    954             mfr.length = mSendObjectFileSize - initialData;
    955         }
    956 
    957         LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
    958         // transfer the file
    959         ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
    960         LOGV("MTP_RECEIVE_FILE returned %d\n", ret);
    961     }
    962     close(mfr.fd);
    963 
    964     if (ret < 0) {
    965         unlink(mSendObjectFilePath);
    966         if (errno == ECANCELED)
    967             result = MTP_RESPONSE_TRANSACTION_CANCELLED;
    968         else
    969             result = MTP_RESPONSE_GENERAL_ERROR;
    970     }
    971 
    972 done:
    973     // reset so we don't attempt to send the data back
    974     mData.reset();
    975 
    976     mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
    977             result == MTP_RESPONSE_OK);
    978     mSendObjectHandle = kInvalidObjectHandle;
    979     mSendObjectFormat = 0;
    980     return result;
    981 }
    982 
    983 static void deleteRecursive(const char* path) {
    984     char pathbuf[PATH_MAX];
    985     int pathLength = strlen(path);
    986     if (pathLength >= sizeof(pathbuf) - 1) {
    987         LOGE("path too long: %s\n", path);
    988     }
    989     strcpy(pathbuf, path);
    990     if (pathbuf[pathLength - 1] != '/') {
    991         pathbuf[pathLength++] = '/';
    992     }
    993     char* fileSpot = pathbuf + pathLength;
    994     int pathRemaining = sizeof(pathbuf) - pathLength - 1;
    995 
    996     DIR* dir = opendir(path);
    997     if (!dir) {
    998         LOGE("opendir %s failed: %s", path, strerror(errno));
    999         return;
   1000     }
   1001 
   1002     struct dirent* entry;
   1003     while ((entry = readdir(dir))) {
   1004         const char* name = entry->d_name;
   1005 
   1006         // ignore "." and ".."
   1007         if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
   1008             continue;
   1009         }
   1010 
   1011         int nameLength = strlen(name);
   1012         if (nameLength > pathRemaining) {
   1013             LOGE("path %s/%s too long\n", path, name);
   1014             continue;
   1015         }
   1016         strcpy(fileSpot, name);
   1017 
   1018         int type = entry->d_type;
   1019         if (entry->d_type == DT_DIR) {
   1020             deleteRecursive(pathbuf);
   1021             rmdir(pathbuf);
   1022         } else {
   1023             unlink(pathbuf);
   1024         }
   1025     }
   1026     closedir(dir);
   1027 }
   1028 
   1029 static void deletePath(const char* path) {
   1030     struct stat statbuf;
   1031     if (stat(path, &statbuf) == 0) {
   1032         if (S_ISDIR(statbuf.st_mode)) {
   1033             deleteRecursive(path);
   1034             rmdir(path);
   1035         } else {
   1036             unlink(path);
   1037         }
   1038     } else {
   1039         LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
   1040     }
   1041 }
   1042 
   1043 MtpResponseCode MtpServer::doDeleteObject() {
   1044     if (!hasStorage())
   1045         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
   1046     MtpObjectHandle handle = mRequest.getParameter(1);
   1047     MtpObjectFormat format = mRequest.getParameter(2);
   1048     // FIXME - support deleting all objects if handle is 0xFFFFFFFF
   1049     // FIXME - implement deleting objects by format
   1050 
   1051     MtpString filePath;
   1052     int64_t fileLength;
   1053     int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
   1054     if (result == MTP_RESPONSE_OK) {
   1055         LOGV("deleting %s", (const char *)filePath);
   1056         result = mDatabase->deleteFile(handle);
   1057         // Don't delete the actual files unless the database deletion is allowed
   1058         if (result == MTP_RESPONSE_OK) {
   1059             deletePath((const char *)filePath);
   1060         }
   1061     }
   1062 
   1063     return result;
   1064 }
   1065 
   1066 MtpResponseCode MtpServer::doGetObjectPropDesc() {
   1067     MtpObjectProperty propCode = mRequest.getParameter(1);
   1068     MtpObjectFormat format = mRequest.getParameter(2);
   1069     LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
   1070                                         MtpDebug::getFormatCodeName(format));
   1071     MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
   1072     if (!property)
   1073         return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
   1074     property->write(mData);
   1075     delete property;
   1076     return MTP_RESPONSE_OK;
   1077 }
   1078 
   1079 MtpResponseCode MtpServer::doGetDevicePropDesc() {
   1080     MtpDeviceProperty propCode = mRequest.getParameter(1);
   1081     LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
   1082     MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
   1083     if (!property)
   1084         return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
   1085     property->write(mData);
   1086     delete property;
   1087     return MTP_RESPONSE_OK;
   1088 }
   1089 
   1090 MtpResponseCode MtpServer::doSendPartialObject() {
   1091     if (!hasStorage())
   1092         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
   1093     MtpObjectHandle handle = mRequest.getParameter(1);
   1094     uint64_t offset = mRequest.getParameter(2);
   1095     uint64_t offset2 = mRequest.getParameter(3);
   1096     offset = offset | (offset2 << 32);
   1097     uint32_t length = mRequest.getParameter(4);
   1098 
   1099     ObjectEdit* edit = getEditObject(handle);
   1100     if (!edit) {
   1101         LOGE("object not open for edit in doSendPartialObject");
   1102         return MTP_RESPONSE_GENERAL_ERROR;
   1103     }
   1104 
   1105     // can't start writing past the end of the file
   1106     if (offset > edit->mSize) {
   1107         LOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
   1108         return MTP_RESPONSE_GENERAL_ERROR;
   1109     }
   1110 
   1111     const char* filePath = (const char *)edit->mPath;
   1112     LOGV("receiving partial %s %lld %lld\n", filePath, offset, length);
   1113 
   1114     // read the header, and possibly some data
   1115     int ret = mData.read(mFD);
   1116     if (ret < MTP_CONTAINER_HEADER_SIZE)
   1117         return MTP_RESPONSE_GENERAL_ERROR;
   1118     int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
   1119 
   1120     if (initialData > 0) {
   1121         ret = write(edit->mFD, mData.getData(), initialData);
   1122         offset += initialData;
   1123         length -= initialData;
   1124     }
   1125 
   1126     if (length > 0) {
   1127         mtp_file_range  mfr;
   1128         mfr.fd = edit->mFD;
   1129         mfr.offset = offset;
   1130         mfr.length = length;
   1131 
   1132         // transfer the file
   1133         ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
   1134         LOGV("MTP_RECEIVE_FILE returned %d", ret);
   1135     }
   1136     if (ret < 0) {
   1137         mResponse.setParameter(1, 0);
   1138         if (errno == ECANCELED)
   1139             return MTP_RESPONSE_TRANSACTION_CANCELLED;
   1140         else
   1141             return MTP_RESPONSE_GENERAL_ERROR;
   1142     }
   1143 
   1144     // reset so we don't attempt to send this back
   1145     mData.reset();
   1146     mResponse.setParameter(1, length);
   1147     uint64_t end = offset + length;
   1148     if (end > edit->mSize) {
   1149         edit->mSize = end;
   1150     }
   1151     return MTP_RESPONSE_OK;
   1152 }
   1153 
   1154 MtpResponseCode MtpServer::doTruncateObject() {
   1155     MtpObjectHandle handle = mRequest.getParameter(1);
   1156     ObjectEdit* edit = getEditObject(handle);
   1157     if (!edit) {
   1158         LOGE("object not open for edit in doTruncateObject");
   1159         return MTP_RESPONSE_GENERAL_ERROR;
   1160     }
   1161 
   1162     uint64_t offset = mRequest.getParameter(2);
   1163     uint64_t offset2 = mRequest.getParameter(3);
   1164     offset |= (offset2 << 32);
   1165     if (ftruncate(edit->mFD, offset) != 0) {
   1166         return MTP_RESPONSE_GENERAL_ERROR;
   1167     } else {
   1168         edit->mSize = offset;
   1169         return MTP_RESPONSE_OK;
   1170     }
   1171 }
   1172 
   1173 MtpResponseCode MtpServer::doBeginEditObject() {
   1174     MtpObjectHandle handle = mRequest.getParameter(1);
   1175     if (getEditObject(handle)) {
   1176         LOGE("object already open for edit in doBeginEditObject");
   1177         return MTP_RESPONSE_GENERAL_ERROR;
   1178     }
   1179 
   1180     MtpString path;
   1181     int64_t fileLength;
   1182     MtpObjectFormat format;
   1183     int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
   1184     if (result != MTP_RESPONSE_OK)
   1185         return result;
   1186 
   1187     int fd = open((const char *)path, O_RDWR | O_EXCL);
   1188     if (fd < 0) {
   1189         LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
   1190         return MTP_RESPONSE_GENERAL_ERROR;
   1191     }
   1192 
   1193     addEditObject(handle, path, fileLength, format, fd);
   1194     return MTP_RESPONSE_OK;
   1195 }
   1196 
   1197 MtpResponseCode MtpServer::doEndEditObject() {
   1198     MtpObjectHandle handle = mRequest.getParameter(1);
   1199     ObjectEdit* edit = getEditObject(handle);
   1200     if (!edit) {
   1201         LOGE("object not open for edit in doEndEditObject");
   1202         return MTP_RESPONSE_GENERAL_ERROR;
   1203     }
   1204 
   1205     commitEdit(edit);
   1206     removeEditObject(handle);
   1207     return MTP_RESPONSE_OK;
   1208 }
   1209 
   1210 }  // namespace android
   1211