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     ALOGV("MtpServer::run fd: %d\n", fd);
    157 
    158     while (1) {
    159         int ret = mRequest.read(fd);
    160         if (ret < 0) {
    161             ALOGV("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         ALOGV("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                 ALOGE("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             ALOGV("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                 ALOGV("sending data:");
    200                 mData.dump();
    201                 ret = mData.write(fd);
    202                 if (ret < 0) {
    203                     ALOGE("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             ALOGV("sending response %04X", mResponse.getResponseCode());
    214             ret = mResponse.write(fd);
    215             mResponse.dump();
    216             if (ret < 0) {
    217                 ALOGE("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             ALOGV("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     ALOGV("sendObjectAdded %d\n", handle);
    246     sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
    247 }
    248 
    249 void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
    250     ALOGV("sendObjectRemoved %d\n", handle);
    251     sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
    252 }
    253 
    254 void MtpServer::sendStoreAdded(MtpStorageID id) {
    255     ALOGV("sendStoreAdded %08X\n", id);
    256     sendEvent(MTP_EVENT_STORE_ADDED, id);
    257 }
    258 
    259 void MtpServer::sendStoreRemoved(MtpStorageID id) {
    260     ALOGV("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         ALOGV("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     ALOGE("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         ALOGE("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             ALOGE("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     ALOGV("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     ALOGV("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     ALOGV("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     ALOGV("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     ALOGV("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    ALOGV("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         formatDateTime(info.mDateCreated, date, sizeof(date));
    708         mData.putString(date);   // date created
    709         formatDateTime(info.mDateModified, date, sizeof(date));
    710         mData.putString(date);   // date modified
    711         mData.putEmptyString();   // keywords
    712     }
    713     return result;
    714 }
    715 
    716 MtpResponseCode MtpServer::doGetObject() {
    717     if (!hasStorage())
    718         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    719     MtpObjectHandle handle = mRequest.getParameter(1);
    720     MtpString pathBuf;
    721     int64_t fileLength;
    722     MtpObjectFormat format;
    723     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
    724     if (result != MTP_RESPONSE_OK)
    725         return result;
    726 
    727     const char* filePath = (const char *)pathBuf;
    728     mtp_file_range  mfr;
    729     mfr.fd = open(filePath, O_RDONLY);
    730     if (mfr.fd < 0) {
    731         return MTP_RESPONSE_GENERAL_ERROR;
    732     }
    733     mfr.offset = 0;
    734     mfr.length = fileLength;
    735     mfr.command = mRequest.getOperationCode();
    736     mfr.transaction_id = mRequest.getTransactionID();
    737 
    738     // then transfer the file
    739     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
    740     ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
    741     close(mfr.fd);
    742     if (ret < 0) {
    743         if (errno == ECANCELED)
    744             return MTP_RESPONSE_TRANSACTION_CANCELLED;
    745         else
    746             return MTP_RESPONSE_GENERAL_ERROR;
    747     }
    748     return MTP_RESPONSE_OK;
    749 }
    750 
    751 MtpResponseCode MtpServer::doGetThumb() {
    752     MtpObjectHandle handle = mRequest.getParameter(1);
    753     size_t thumbSize;
    754     void* thumb = mDatabase->getThumbnail(handle, thumbSize);
    755     if (thumb) {
    756         // send data
    757         mData.setOperationCode(mRequest.getOperationCode());
    758         mData.setTransactionID(mRequest.getTransactionID());
    759         mData.writeData(mFD, thumb, thumbSize);
    760         free(thumb);
    761         return MTP_RESPONSE_OK;
    762     } else {
    763         return MTP_RESPONSE_GENERAL_ERROR;
    764     }
    765 }
    766 
    767 MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
    768     if (!hasStorage())
    769         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
    770     MtpObjectHandle handle = mRequest.getParameter(1);
    771     uint64_t offset;
    772     uint32_t length;
    773     offset = mRequest.getParameter(2);
    774     if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
    775         // android extension with 64 bit offset
    776         uint64_t offset2 = mRequest.getParameter(3);
    777         offset = offset | (offset2 << 32);
    778         length = mRequest.getParameter(4);
    779     } else {
    780         // standard GetPartialObject
    781         length = mRequest.getParameter(3);
    782     }
    783     MtpString pathBuf;
    784     int64_t fileLength;
    785     MtpObjectFormat format;
    786     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
    787     if (result != MTP_RESPONSE_OK)
    788         return result;
    789     if (offset + length > fileLength)
    790         length = fileLength - offset;
    791 
    792     const char* filePath = (const char *)pathBuf;
    793     mtp_file_range  mfr;
    794     mfr.fd = open(filePath, O_RDONLY);
    795     if (mfr.fd < 0) {
    796         return MTP_RESPONSE_GENERAL_ERROR;
    797     }
    798     mfr.offset = offset;
    799     mfr.length = length;
    800     mfr.command = mRequest.getOperationCode();
    801     mfr.transaction_id = mRequest.getTransactionID();
    802     mResponse.setParameter(1, length);
    803 
    804     // transfer the file
    805     int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
    806     ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
    807     close(mfr.fd);
    808     if (ret < 0) {
    809         if (errno == ECANCELED)
    810             return MTP_RESPONSE_TRANSACTION_CANCELLED;
    811         else
    812             return MTP_RESPONSE_GENERAL_ERROR;
    813     }
    814     return MTP_RESPONSE_OK;
    815 }
    816 
    817 MtpResponseCode MtpServer::doSendObjectInfo() {
    818     MtpString path;
    819     MtpStorageID storageID = mRequest.getParameter(1);
    820     MtpStorage* storage = getStorage(storageID);
    821     MtpObjectHandle parent = mRequest.getParameter(2);
    822     if (!storage)
    823         return MTP_RESPONSE_INVALID_STORAGE_ID;
    824 
    825     // special case the root
    826     if (parent == MTP_PARENT_ROOT) {
    827         path = storage->getPath();
    828         parent = 0;
    829     } else {
    830         int64_t length;
    831         MtpObjectFormat format;
    832         int result = mDatabase->getObjectFilePath(parent, path, length, format);
    833         if (result != MTP_RESPONSE_OK)
    834             return result;
    835         if (format != MTP_FORMAT_ASSOCIATION)
    836             return MTP_RESPONSE_INVALID_PARENT_OBJECT;
    837     }
    838 
    839     // read only the fields we need
    840     mData.getUInt32();  // storage ID
    841     MtpObjectFormat format = mData.getUInt16();
    842     mData.getUInt16();  // protection status
    843     mSendObjectFileSize = mData.getUInt32();
    844     mData.getUInt16();  // thumb format
    845     mData.getUInt32();  // thumb compressed size
    846     mData.getUInt32();  // thumb pix width
    847     mData.getUInt32();  // thumb pix height
    848     mData.getUInt32();  // image pix width
    849     mData.getUInt32();  // image pix height
    850     mData.getUInt32();  // image bit depth
    851     mData.getUInt32();  // parent
    852     uint16_t associationType = mData.getUInt16();
    853     uint32_t associationDesc = mData.getUInt32();   // association desc
    854     mData.getUInt32();  // sequence number
    855     MtpStringBuffer name, created, modified;
    856     mData.getString(name);    // file name
    857     mData.getString(created);      // date created
    858     mData.getString(modified);     // date modified
    859     // keywords follow
    860 
    861     ALOGV("name: %s format: %04X\n", (const char *)name, format);
    862     time_t modifiedTime;
    863     if (!parseDateTime(modified, modifiedTime))
    864         modifiedTime = 0;
    865 
    866     if (path[path.size() - 1] != '/')
    867         path += "/";
    868     path += (const char *)name;
    869 
    870     // check space first
    871     if (mSendObjectFileSize > storage->getFreeSpace())
    872         return MTP_RESPONSE_STORAGE_FULL;
    873     uint64_t maxFileSize = storage->getMaxFileSize();
    874     // check storage max file size
    875     if (maxFileSize != 0) {
    876         // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
    877         // is >= 0xFFFFFFFF
    878         if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
    879             return MTP_RESPONSE_OBJECT_TOO_LARGE;
    880     }
    881 
    882     ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
    883     MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
    884             format, parent, storageID, mSendObjectFileSize, modifiedTime);
    885     if (handle == kInvalidObjectHandle) {
    886         return MTP_RESPONSE_GENERAL_ERROR;
    887     }
    888 
    889   if (format == MTP_FORMAT_ASSOCIATION) {
    890         mode_t mask = umask(0);
    891         int ret = mkdir((const char *)path, mDirectoryPermission);
    892         umask(mask);
    893         if (ret && ret != -EEXIST)
    894             return MTP_RESPONSE_GENERAL_ERROR;
    895         chown((const char *)path, getuid(), mFileGroup);
    896 
    897         // SendObject does not get sent for directories, so call endSendObject here instead
    898         mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
    899     } else {
    900         mSendObjectFilePath = path;
    901         // save the handle for the SendObject call, which should follow
    902         mSendObjectHandle = handle;
    903         mSendObjectFormat = format;
    904     }
    905 
    906     mResponse.setParameter(1, storageID);
    907     mResponse.setParameter(2, parent);
    908     mResponse.setParameter(3, handle);
    909 
    910     return MTP_RESPONSE_OK;
    911 }
    912 
    913 MtpResponseCode MtpServer::doSendObject() {
    914     if (!hasStorage())
    915         return MTP_RESPONSE_GENERAL_ERROR;
    916     MtpResponseCode result = MTP_RESPONSE_OK;
    917     mode_t mask;
    918     int ret, initialData;
    919 
    920     if (mSendObjectHandle == kInvalidObjectHandle) {
    921         ALOGE("Expected SendObjectInfo before SendObject");
    922         result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
    923         goto done;
    924     }
    925 
    926     // read the header, and possibly some data
    927     ret = mData.read(mFD);
    928     if (ret < MTP_CONTAINER_HEADER_SIZE) {
    929         result = MTP_RESPONSE_GENERAL_ERROR;
    930         goto done;
    931     }
    932     initialData = ret - MTP_CONTAINER_HEADER_SIZE;
    933 
    934     mtp_file_range  mfr;
    935     mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    936     if (mfr.fd < 0) {
    937         result = MTP_RESPONSE_GENERAL_ERROR;
    938         goto done;
    939     }
    940     fchown(mfr.fd, getuid(), mFileGroup);
    941     // set permissions
    942     mask = umask(0);
    943     fchmod(mfr.fd, mFilePermission);
    944     umask(mask);
    945 
    946     if (initialData > 0)
    947         ret = write(mfr.fd, mData.getData(), initialData);
    948 
    949     if (mSendObjectFileSize - initialData > 0) {
    950         mfr.offset = initialData;
    951         if (mSendObjectFileSize == 0xFFFFFFFF) {
    952             // tell driver to read until it receives a short packet
    953             mfr.length = 0xFFFFFFFF;
    954         } else {
    955             mfr.length = mSendObjectFileSize - initialData;
    956         }
    957 
    958         ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
    959         // transfer the file
    960         ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
    961         ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
    962     }
    963     close(mfr.fd);
    964 
    965     if (ret < 0) {
    966         unlink(mSendObjectFilePath);
    967         if (errno == ECANCELED)
    968             result = MTP_RESPONSE_TRANSACTION_CANCELLED;
    969         else
    970             result = MTP_RESPONSE_GENERAL_ERROR;
    971     }
    972 
    973 done:
    974     // reset so we don't attempt to send the data back
    975     mData.reset();
    976 
    977     mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
    978             result == MTP_RESPONSE_OK);
    979     mSendObjectHandle = kInvalidObjectHandle;
    980     mSendObjectFormat = 0;
    981     return result;
    982 }
    983 
    984 static void deleteRecursive(const char* path) {
    985     char pathbuf[PATH_MAX];
    986     int pathLength = strlen(path);
    987     if (pathLength >= sizeof(pathbuf) - 1) {
    988         ALOGE("path too long: %s\n", path);
    989     }
    990     strcpy(pathbuf, path);
    991     if (pathbuf[pathLength - 1] != '/') {
    992         pathbuf[pathLength++] = '/';
    993     }
    994     char* fileSpot = pathbuf + pathLength;
    995     int pathRemaining = sizeof(pathbuf) - pathLength - 1;
    996 
    997     DIR* dir = opendir(path);
    998     if (!dir) {
    999         ALOGE("opendir %s failed: %s", path, strerror(errno));
   1000         return;
   1001     }
   1002 
   1003     struct dirent* entry;
   1004     while ((entry = readdir(dir))) {
   1005         const char* name = entry->d_name;
   1006 
   1007         // ignore "." and ".."
   1008         if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
   1009             continue;
   1010         }
   1011 
   1012         int nameLength = strlen(name);
   1013         if (nameLength > pathRemaining) {
   1014             ALOGE("path %s/%s too long\n", path, name);
   1015             continue;
   1016         }
   1017         strcpy(fileSpot, name);
   1018 
   1019         int type = entry->d_type;
   1020         if (entry->d_type == DT_DIR) {
   1021             deleteRecursive(pathbuf);
   1022             rmdir(pathbuf);
   1023         } else {
   1024             unlink(pathbuf);
   1025         }
   1026     }
   1027     closedir(dir);
   1028 }
   1029 
   1030 static void deletePath(const char* path) {
   1031     struct stat statbuf;
   1032     if (stat(path, &statbuf) == 0) {
   1033         if (S_ISDIR(statbuf.st_mode)) {
   1034             deleteRecursive(path);
   1035             rmdir(path);
   1036         } else {
   1037             unlink(path);
   1038         }
   1039     } else {
   1040         ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
   1041     }
   1042 }
   1043 
   1044 MtpResponseCode MtpServer::doDeleteObject() {
   1045     if (!hasStorage())
   1046         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
   1047     MtpObjectHandle handle = mRequest.getParameter(1);
   1048     MtpObjectFormat format = mRequest.getParameter(2);
   1049     // FIXME - support deleting all objects if handle is 0xFFFFFFFF
   1050     // FIXME - implement deleting objects by format
   1051 
   1052     MtpString filePath;
   1053     int64_t fileLength;
   1054     int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
   1055     if (result == MTP_RESPONSE_OK) {
   1056         ALOGV("deleting %s", (const char *)filePath);
   1057         result = mDatabase->deleteFile(handle);
   1058         // Don't delete the actual files unless the database deletion is allowed
   1059         if (result == MTP_RESPONSE_OK) {
   1060             deletePath((const char *)filePath);
   1061         }
   1062     }
   1063 
   1064     return result;
   1065 }
   1066 
   1067 MtpResponseCode MtpServer::doGetObjectPropDesc() {
   1068     MtpObjectProperty propCode = mRequest.getParameter(1);
   1069     MtpObjectFormat format = mRequest.getParameter(2);
   1070     ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
   1071                                         MtpDebug::getFormatCodeName(format));
   1072     MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
   1073     if (!property)
   1074         return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
   1075     property->write(mData);
   1076     delete property;
   1077     return MTP_RESPONSE_OK;
   1078 }
   1079 
   1080 MtpResponseCode MtpServer::doGetDevicePropDesc() {
   1081     MtpDeviceProperty propCode = mRequest.getParameter(1);
   1082     ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
   1083     MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
   1084     if (!property)
   1085         return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
   1086     property->write(mData);
   1087     delete property;
   1088     return MTP_RESPONSE_OK;
   1089 }
   1090 
   1091 MtpResponseCode MtpServer::doSendPartialObject() {
   1092     if (!hasStorage())
   1093         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
   1094     MtpObjectHandle handle = mRequest.getParameter(1);
   1095     uint64_t offset = mRequest.getParameter(2);
   1096     uint64_t offset2 = mRequest.getParameter(3);
   1097     offset = offset | (offset2 << 32);
   1098     uint32_t length = mRequest.getParameter(4);
   1099 
   1100     ObjectEdit* edit = getEditObject(handle);
   1101     if (!edit) {
   1102         ALOGE("object not open for edit in doSendPartialObject");
   1103         return MTP_RESPONSE_GENERAL_ERROR;
   1104     }
   1105 
   1106     // can't start writing past the end of the file
   1107     if (offset > edit->mSize) {
   1108         ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
   1109         return MTP_RESPONSE_GENERAL_ERROR;
   1110     }
   1111 
   1112     const char* filePath = (const char *)edit->mPath;
   1113     ALOGV("receiving partial %s %lld %lld\n", filePath, offset, length);
   1114 
   1115     // read the header, and possibly some data
   1116     int ret = mData.read(mFD);
   1117     if (ret < MTP_CONTAINER_HEADER_SIZE)
   1118         return MTP_RESPONSE_GENERAL_ERROR;
   1119     int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
   1120 
   1121     if (initialData > 0) {
   1122         ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
   1123         offset += initialData;
   1124         length -= initialData;
   1125     }
   1126 
   1127     if (length > 0) {
   1128         mtp_file_range  mfr;
   1129         mfr.fd = edit->mFD;
   1130         mfr.offset = offset;
   1131         mfr.length = length;
   1132 
   1133         // transfer the file
   1134         ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
   1135         ALOGV("MTP_RECEIVE_FILE returned %d", ret);
   1136     }
   1137     if (ret < 0) {
   1138         mResponse.setParameter(1, 0);
   1139         if (errno == ECANCELED)
   1140             return MTP_RESPONSE_TRANSACTION_CANCELLED;
   1141         else
   1142             return MTP_RESPONSE_GENERAL_ERROR;
   1143     }
   1144 
   1145     // reset so we don't attempt to send this back
   1146     mData.reset();
   1147     mResponse.setParameter(1, length);
   1148     uint64_t end = offset + length;
   1149     if (end > edit->mSize) {
   1150         edit->mSize = end;
   1151     }
   1152     return MTP_RESPONSE_OK;
   1153 }
   1154 
   1155 MtpResponseCode MtpServer::doTruncateObject() {
   1156     MtpObjectHandle handle = mRequest.getParameter(1);
   1157     ObjectEdit* edit = getEditObject(handle);
   1158     if (!edit) {
   1159         ALOGE("object not open for edit in doTruncateObject");
   1160         return MTP_RESPONSE_GENERAL_ERROR;
   1161     }
   1162 
   1163     uint64_t offset = mRequest.getParameter(2);
   1164     uint64_t offset2 = mRequest.getParameter(3);
   1165     offset |= (offset2 << 32);
   1166     if (ftruncate(edit->mFD, offset) != 0) {
   1167         return MTP_RESPONSE_GENERAL_ERROR;
   1168     } else {
   1169         edit->mSize = offset;
   1170         return MTP_RESPONSE_OK;
   1171     }
   1172 }
   1173 
   1174 MtpResponseCode MtpServer::doBeginEditObject() {
   1175     MtpObjectHandle handle = mRequest.getParameter(1);
   1176     if (getEditObject(handle)) {
   1177         ALOGE("object already open for edit in doBeginEditObject");
   1178         return MTP_RESPONSE_GENERAL_ERROR;
   1179     }
   1180 
   1181     MtpString path;
   1182     int64_t fileLength;
   1183     MtpObjectFormat format;
   1184     int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
   1185     if (result != MTP_RESPONSE_OK)
   1186         return result;
   1187 
   1188     int fd = open((const char *)path, O_RDWR | O_EXCL);
   1189     if (fd < 0) {
   1190         ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
   1191         return MTP_RESPONSE_GENERAL_ERROR;
   1192     }
   1193 
   1194     addEditObject(handle, path, fileLength, format, fd);
   1195     return MTP_RESPONSE_OK;
   1196 }
   1197 
   1198 MtpResponseCode MtpServer::doEndEditObject() {
   1199     MtpObjectHandle handle = mRequest.getParameter(1);
   1200     ObjectEdit* edit = getEditObject(handle);
   1201     if (!edit) {
   1202         ALOGE("object not open for edit in doEndEditObject");
   1203         return MTP_RESPONSE_GENERAL_ERROR;
   1204     }
   1205 
   1206     commitEdit(edit);
   1207     removeEditObject(handle);
   1208     return MTP_RESPONSE_OK;
   1209 }
   1210 
   1211 }  // namespace android
   1212