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