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