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