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
46namespace android {
47
48static 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
96static 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
104MtpServer::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
131MtpServer::~MtpServer() {
132}
133
134void MtpServer::addStorage(MtpStorage* storage) {
135    std::lock_guard<std::mutex> lg(mMutex);
136
137    mStorages.push_back(storage);
138    sendStoreAdded(storage->getStorageID());
139}
140
141void 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
150MtpStorage* 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
160bool MtpServer::hasStorage(MtpStorageID id) {
161    if (id == 0 || id == 0xFFFFFFFF)
162        return mStorages.size() > 0;
163    return (getStorage(id) != nullptr);
164}
165
166void 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
252void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
253    ALOGV("sendObjectAdded %d\n", handle);
254    sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
255}
256
257void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
258    ALOGV("sendObjectRemoved %d\n", handle);
259    sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
260}
261
262void MtpServer::sendStoreAdded(MtpStorageID id) {
263    ALOGV("sendStoreAdded %08X\n", id);
264    sendEvent(MTP_EVENT_STORE_ADDED, id);
265}
266
267void MtpServer::sendStoreRemoved(MtpStorageID id) {
268    ALOGV("sendStoreRemoved %08X\n", id);
269    sendEvent(MTP_EVENT_STORE_REMOVED, id);
270}
271
272void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) {
273    ALOGV("sendDevicePropertyChanged %d\n", property);
274    sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property);
275}
276
277void 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
287void 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
293MtpServer::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
302void 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
315void MtpServer::commitEdit(ObjectEdit* edit) {
316    mDatabase->rescanFile((const char *)edit->mPath, edit->mHandle, edit->mFormat);
317}
318
319
320bool 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
451MtpResponseCode 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
496MtpResponseCode 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
510MtpResponseCode 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
518MtpResponseCode 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
530MtpResponseCode 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
556MtpResponseCode 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
568MtpResponseCode 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
589MtpResponseCode 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
611MtpResponseCode 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
631MtpResponseCode 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
648MtpResponseCode 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
661MtpResponseCode 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
674MtpResponseCode 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
684MtpResponseCode 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
694MtpResponseCode 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
704MtpResponseCode 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
723MtpResponseCode 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
766MtpResponseCode 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
816MtpResponseCode 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
834MtpResponseCode 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
894MtpResponseCode 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
998MtpResponseCode 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
1076MtpResponseCode 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
1145MtpResponseCode 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
1240done:
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
1257MtpResponseCode 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
1284MtpResponseCode 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
1299MtpResponseCode 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
1312MtpResponseCode 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
1387MtpResponseCode 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
1408MtpResponseCode 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
1434MtpResponseCode 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