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