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