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