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