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    MtpObjectHandle handle = mRequest.getParameter(1);
823    uint64_t offset;
824    uint32_t length;
825    offset = mRequest.getParameter(2);
826    if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
827        // MTP_OPERATION_GET_PARTIAL_OBJECT_64 takes 4 arguments
828        if (mRequest.getParameterCount() < 4)
829            return MTP_RESPONSE_INVALID_PARAMETER;
830
831        // android extension with 64 bit offset
832        uint64_t offset2 = mRequest.getParameter(3);
833        offset = offset | (offset2 << 32);
834        length = mRequest.getParameter(4);
835    } else {
836        // MTP_OPERATION_GET_PARTIAL_OBJECT takes 3 arguments
837        if (mRequest.getParameterCount() < 3)
838            return MTP_RESPONSE_INVALID_PARAMETER;
839
840        // standard GetPartialObject
841        length = mRequest.getParameter(3);
842    }
843    MtpString pathBuf;
844    int64_t fileLength;
845    MtpObjectFormat format;
846    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
847    if (result != MTP_RESPONSE_OK)
848        return result;
849    if (offset + length > (uint64_t)fileLength)
850        length = fileLength - offset;
851
852    const char* filePath = (const char *)pathBuf;
853    mtp_file_range  mfr;
854    mfr.fd = open(filePath, O_RDONLY);
855    if (mfr.fd < 0) {
856        return MTP_RESPONSE_GENERAL_ERROR;
857    }
858    mfr.offset = offset;
859    mfr.length = length;
860    mfr.command = mRequest.getOperationCode();
861    mfr.transaction_id = mRequest.getTransactionID();
862    mResponse.setParameter(1, length);
863
864    // transfer the file
865    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
866    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
867    close(mfr.fd);
868    if (ret < 0) {
869        if (errno == ECANCELED)
870            return MTP_RESPONSE_TRANSACTION_CANCELLED;
871        else
872            return MTP_RESPONSE_GENERAL_ERROR;
873    }
874    return MTP_RESPONSE_OK;
875}
876
877MtpResponseCode MtpServer::doSendObjectInfo() {
878    MtpString path;
879    uint16_t temp16;
880    uint32_t temp32;
881
882    if (mRequest.getParameterCount() < 2)
883        return MTP_RESPONSE_INVALID_PARAMETER;
884    MtpStorageID storageID = mRequest.getParameter(1);
885    MtpStorage* storage = getStorage(storageID);
886    MtpObjectHandle parent = mRequest.getParameter(2);
887    if (!storage)
888        return MTP_RESPONSE_INVALID_STORAGE_ID;
889
890    // special case the root
891    if (parent == MTP_PARENT_ROOT) {
892        path = storage->getPath();
893        parent = 0;
894    } else {
895        int64_t length;
896        MtpObjectFormat format;
897        int result = mDatabase->getObjectFilePath(parent, path, length, format);
898        if (result != MTP_RESPONSE_OK)
899            return result;
900        if (format != MTP_FORMAT_ASSOCIATION)
901            return MTP_RESPONSE_INVALID_PARENT_OBJECT;
902    }
903
904    // read only the fields we need
905    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // storage ID
906    if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
907    MtpObjectFormat format = temp16;
908    if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // protection status
909    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
910    mSendObjectFileSize = temp32;
911    if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb format
912    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb compressed size
913    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix width
914    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix height
915    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix width
916    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix height
917    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image bit depth
918    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // parent
919    if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
920    uint16_t associationType = temp16;
921    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
922    uint32_t associationDesc = temp32;        // association desc
923    if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // sequence number
924    MtpStringBuffer name, created, modified;
925    if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER;    // file name
926    if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER;      // date created
927    if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER;     // date modified
928    // keywords follow
929
930    ALOGV("name: %s format: %04X\n", (const char *)name, format);
931    time_t modifiedTime;
932    if (!parseDateTime(modified, modifiedTime))
933        modifiedTime = 0;
934
935    if (path[path.size() - 1] != '/')
936        path += "/";
937    path += (const char *)name;
938
939    // check space first
940    if (mSendObjectFileSize > storage->getFreeSpace())
941        return MTP_RESPONSE_STORAGE_FULL;
942    uint64_t maxFileSize = storage->getMaxFileSize();
943    // check storage max file size
944    if (maxFileSize != 0) {
945        // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
946        // is >= 0xFFFFFFFF
947        if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
948            return MTP_RESPONSE_OBJECT_TOO_LARGE;
949    }
950
951    ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
952    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
953            format, parent, storageID, mSendObjectFileSize, modifiedTime);
954    if (handle == kInvalidObjectHandle) {
955        return MTP_RESPONSE_GENERAL_ERROR;
956    }
957
958  if (format == MTP_FORMAT_ASSOCIATION) {
959        mode_t mask = umask(0);
960        int ret = mkdir((const char *)path, mDirectoryPermission);
961        umask(mask);
962        if (ret && ret != -EEXIST)
963            return MTP_RESPONSE_GENERAL_ERROR;
964        chown((const char *)path, getuid(), mFileGroup);
965
966        // SendObject does not get sent for directories, so call endSendObject here instead
967        mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
968    } else {
969        mSendObjectFilePath = path;
970        // save the handle for the SendObject call, which should follow
971        mSendObjectHandle = handle;
972        mSendObjectFormat = format;
973    }
974
975    mResponse.setParameter(1, storageID);
976    mResponse.setParameter(2, parent);
977    mResponse.setParameter(3, handle);
978
979    return MTP_RESPONSE_OK;
980}
981
982MtpResponseCode MtpServer::doSendObject() {
983    if (!hasStorage())
984        return MTP_RESPONSE_GENERAL_ERROR;
985    MtpResponseCode result = MTP_RESPONSE_OK;
986    mode_t mask;
987    int ret, initialData;
988
989    if (mSendObjectHandle == kInvalidObjectHandle) {
990        ALOGE("Expected SendObjectInfo before SendObject");
991        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
992        goto done;
993    }
994
995    // read the header, and possibly some data
996    ret = mData.read(mFD);
997    if (ret < MTP_CONTAINER_HEADER_SIZE) {
998        result = MTP_RESPONSE_GENERAL_ERROR;
999        goto done;
1000    }
1001    initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1002
1003    mtp_file_range  mfr;
1004    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
1005    if (mfr.fd < 0) {
1006        result = MTP_RESPONSE_GENERAL_ERROR;
1007        goto done;
1008    }
1009    fchown(mfr.fd, getuid(), mFileGroup);
1010    // set permissions
1011    mask = umask(0);
1012    fchmod(mfr.fd, mFilePermission);
1013    umask(mask);
1014
1015    if (initialData > 0) {
1016        ret = write(mfr.fd, mData.getData(), initialData);
1017    }
1018
1019    if (ret < 0) {
1020        ALOGE("failed to write initial data");
1021        result = MTP_RESPONSE_GENERAL_ERROR;
1022    } else {
1023        if (mSendObjectFileSize - initialData > 0) {
1024            mfr.offset = initialData;
1025            if (mSendObjectFileSize == 0xFFFFFFFF) {
1026                // tell driver to read until it receives a short packet
1027                mfr.length = 0xFFFFFFFF;
1028            } else {
1029                mfr.length = mSendObjectFileSize - initialData;
1030            }
1031
1032            ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
1033            // transfer the file
1034            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1035            ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
1036        }
1037    }
1038    close(mfr.fd);
1039
1040    if (ret < 0) {
1041        unlink(mSendObjectFilePath);
1042        if (errno == ECANCELED)
1043            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
1044        else
1045            result = MTP_RESPONSE_GENERAL_ERROR;
1046    }
1047
1048done:
1049    // reset so we don't attempt to send the data back
1050    mData.reset();
1051
1052    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
1053            result == MTP_RESPONSE_OK);
1054    mSendObjectHandle = kInvalidObjectHandle;
1055    mSendObjectFormat = 0;
1056    return result;
1057}
1058
1059static void deleteRecursive(const char* path) {
1060    char pathbuf[PATH_MAX];
1061    size_t pathLength = strlen(path);
1062    if (pathLength >= sizeof(pathbuf) - 1) {
1063        ALOGE("path too long: %s\n", path);
1064    }
1065    strcpy(pathbuf, path);
1066    if (pathbuf[pathLength - 1] != '/') {
1067        pathbuf[pathLength++] = '/';
1068    }
1069    char* fileSpot = pathbuf + pathLength;
1070    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
1071
1072    DIR* dir = opendir(path);
1073    if (!dir) {
1074        ALOGE("opendir %s failed: %s", path, strerror(errno));
1075        return;
1076    }
1077
1078    struct dirent* entry;
1079    while ((entry = readdir(dir))) {
1080        const char* name = entry->d_name;
1081
1082        // ignore "." and ".."
1083        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1084            continue;
1085        }
1086
1087        int nameLength = strlen(name);
1088        if (nameLength > pathRemaining) {
1089            ALOGE("path %s/%s too long\n", path, name);
1090            continue;
1091        }
1092        strcpy(fileSpot, name);
1093
1094        int type = entry->d_type;
1095        if (entry->d_type == DT_DIR) {
1096            deleteRecursive(pathbuf);
1097            rmdir(pathbuf);
1098        } else {
1099            unlink(pathbuf);
1100        }
1101    }
1102    closedir(dir);
1103}
1104
1105static void deletePath(const char* path) {
1106    struct stat statbuf;
1107    if (stat(path, &statbuf) == 0) {
1108        if (S_ISDIR(statbuf.st_mode)) {
1109            deleteRecursive(path);
1110            rmdir(path);
1111        } else {
1112            unlink(path);
1113        }
1114    } else {
1115        ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
1116    }
1117}
1118
1119MtpResponseCode MtpServer::doDeleteObject() {
1120    if (!hasStorage())
1121        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1122    if (mRequest.getParameterCount() < 1)
1123        return MTP_RESPONSE_INVALID_PARAMETER;
1124    MtpObjectHandle handle = mRequest.getParameter(1);
1125    MtpObjectFormat format;
1126    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1127    // FIXME - implement deleting objects by format
1128
1129    MtpString filePath;
1130    int64_t fileLength;
1131    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1132    if (result == MTP_RESPONSE_OK) {
1133        ALOGV("deleting %s", (const char *)filePath);
1134        result = mDatabase->deleteFile(handle);
1135        // Don't delete the actual files unless the database deletion is allowed
1136        if (result == MTP_RESPONSE_OK) {
1137            deletePath((const char *)filePath);
1138        }
1139    }
1140
1141    return result;
1142}
1143
1144MtpResponseCode MtpServer::doGetObjectPropDesc() {
1145    if (mRequest.getParameterCount() < 2)
1146        return MTP_RESPONSE_INVALID_PARAMETER;
1147    MtpObjectProperty propCode = mRequest.getParameter(1);
1148    MtpObjectFormat format = mRequest.getParameter(2);
1149    ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1150                                        MtpDebug::getFormatCodeName(format));
1151    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1152    if (!property)
1153        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1154    property->write(mData);
1155    delete property;
1156    return MTP_RESPONSE_OK;
1157}
1158
1159MtpResponseCode MtpServer::doGetDevicePropDesc() {
1160    if (mRequest.getParameterCount() < 1)
1161        return MTP_RESPONSE_INVALID_PARAMETER;
1162    MtpDeviceProperty propCode = mRequest.getParameter(1);
1163    ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1164    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1165    if (!property)
1166        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1167    property->write(mData);
1168    delete property;
1169    return MTP_RESPONSE_OK;
1170}
1171
1172MtpResponseCode MtpServer::doSendPartialObject() {
1173    if (!hasStorage())
1174        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1175    if (mRequest.getParameterCount() < 4)
1176        return MTP_RESPONSE_INVALID_PARAMETER;
1177    MtpObjectHandle handle = mRequest.getParameter(1);
1178    uint64_t offset = mRequest.getParameter(2);
1179    uint64_t offset2 = mRequest.getParameter(3);
1180    offset = offset | (offset2 << 32);
1181    uint32_t length = mRequest.getParameter(4);
1182
1183    ObjectEdit* edit = getEditObject(handle);
1184    if (!edit) {
1185        ALOGE("object not open for edit in doSendPartialObject");
1186        return MTP_RESPONSE_GENERAL_ERROR;
1187    }
1188
1189    // can't start writing past the end of the file
1190    if (offset > edit->mSize) {
1191        ALOGD("writing past end of object, offset: %" PRIu64 ", edit->mSize: %" PRIu64,
1192            offset, edit->mSize);
1193        return MTP_RESPONSE_GENERAL_ERROR;
1194    }
1195
1196    const char* filePath = (const char *)edit->mPath;
1197    ALOGV("receiving partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
1198
1199    // read the header, and possibly some data
1200    int ret = mData.read(mFD);
1201    if (ret < MTP_CONTAINER_HEADER_SIZE)
1202        return MTP_RESPONSE_GENERAL_ERROR;
1203    int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1204
1205    if (initialData > 0) {
1206        ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
1207        offset += initialData;
1208        length -= initialData;
1209    }
1210
1211    if (ret < 0) {
1212        ALOGE("failed to write initial data");
1213    } else {
1214        if (length > 0) {
1215            mtp_file_range  mfr;
1216            mfr.fd = edit->mFD;
1217            mfr.offset = offset;
1218            mfr.length = length;
1219
1220            // transfer the file
1221            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1222            ALOGV("MTP_RECEIVE_FILE returned %d", ret);
1223        }
1224    }
1225    if (ret < 0) {
1226        mResponse.setParameter(1, 0);
1227        if (errno == ECANCELED)
1228            return MTP_RESPONSE_TRANSACTION_CANCELLED;
1229        else
1230            return MTP_RESPONSE_GENERAL_ERROR;
1231    }
1232
1233    // reset so we don't attempt to send this back
1234    mData.reset();
1235    mResponse.setParameter(1, length);
1236    uint64_t end = offset + length;
1237    if (end > edit->mSize) {
1238        edit->mSize = end;
1239    }
1240    return MTP_RESPONSE_OK;
1241}
1242
1243MtpResponseCode MtpServer::doTruncateObject() {
1244    if (mRequest.getParameterCount() < 3)
1245        return MTP_RESPONSE_INVALID_PARAMETER;
1246    MtpObjectHandle handle = mRequest.getParameter(1);
1247    ObjectEdit* edit = getEditObject(handle);
1248    if (!edit) {
1249        ALOGE("object not open for edit in doTruncateObject");
1250        return MTP_RESPONSE_GENERAL_ERROR;
1251    }
1252
1253    uint64_t offset = mRequest.getParameter(2);
1254    uint64_t offset2 = mRequest.getParameter(3);
1255    offset |= (offset2 << 32);
1256    if (ftruncate(edit->mFD, offset) != 0) {
1257        return MTP_RESPONSE_GENERAL_ERROR;
1258    } else {
1259        edit->mSize = offset;
1260        return MTP_RESPONSE_OK;
1261    }
1262}
1263
1264MtpResponseCode MtpServer::doBeginEditObject() {
1265    if (mRequest.getParameterCount() < 1)
1266        return MTP_RESPONSE_INVALID_PARAMETER;
1267    MtpObjectHandle handle = mRequest.getParameter(1);
1268    if (getEditObject(handle)) {
1269        ALOGE("object already open for edit in doBeginEditObject");
1270        return MTP_RESPONSE_GENERAL_ERROR;
1271    }
1272
1273    MtpString path;
1274    int64_t fileLength;
1275    MtpObjectFormat format;
1276    int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1277    if (result != MTP_RESPONSE_OK)
1278        return result;
1279
1280    int fd = open((const char *)path, O_RDWR | O_EXCL);
1281    if (fd < 0) {
1282        ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1283        return MTP_RESPONSE_GENERAL_ERROR;
1284    }
1285
1286    addEditObject(handle, path, fileLength, format, fd);
1287    return MTP_RESPONSE_OK;
1288}
1289
1290MtpResponseCode MtpServer::doEndEditObject() {
1291    if (mRequest.getParameterCount() < 1)
1292        return MTP_RESPONSE_INVALID_PARAMETER;
1293    MtpObjectHandle handle = mRequest.getParameter(1);
1294    ObjectEdit* edit = getEditObject(handle);
1295    if (!edit) {
1296        ALOGE("object not open for edit in doEndEditObject");
1297        return MTP_RESPONSE_GENERAL_ERROR;
1298    }
1299
1300    commitEdit(edit);
1301    removeEditObject(handle);
1302    return MTP_RESPONSE_OK;
1303}
1304
1305}  // namespace android
1306