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