MtpServer.cpp revision e1b8cf1cafb75ce1339b67eb1764e224a257c579
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 "MtpProperty.h"
34#include "MtpServer.h"
35#include "MtpStorage.h"
36#include "MtpStringBuffer.h"
37
38#include <linux/usb/f_mtp.h>
39
40namespace android {
41
42static const MtpOperationCode kSupportedOperationCodes[] = {
43    MTP_OPERATION_GET_DEVICE_INFO,
44    MTP_OPERATION_OPEN_SESSION,
45    MTP_OPERATION_CLOSE_SESSION,
46    MTP_OPERATION_GET_STORAGE_IDS,
47    MTP_OPERATION_GET_STORAGE_INFO,
48    MTP_OPERATION_GET_NUM_OBJECTS,
49    MTP_OPERATION_GET_OBJECT_HANDLES,
50    MTP_OPERATION_GET_OBJECT_INFO,
51    MTP_OPERATION_GET_OBJECT,
52//    MTP_OPERATION_GET_THUMB,
53    MTP_OPERATION_DELETE_OBJECT,
54    MTP_OPERATION_SEND_OBJECT_INFO,
55    MTP_OPERATION_SEND_OBJECT,
56//    MTP_OPERATION_INITIATE_CAPTURE,
57//    MTP_OPERATION_FORMAT_STORE,
58//    MTP_OPERATION_RESET_DEVICE,
59//    MTP_OPERATION_SELF_TEST,
60//    MTP_OPERATION_SET_OBJECT_PROTECTION,
61//    MTP_OPERATION_POWER_DOWN,
62    MTP_OPERATION_GET_DEVICE_PROP_DESC,
63    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
64    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
65    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
66//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
67//    MTP_OPERATION_MOVE_OBJECT,
68//    MTP_OPERATION_COPY_OBJECT,
69//    MTP_OPERATION_GET_PARTIAL_OBJECT,
70//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
71    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
72    MTP_OPERATION_GET_OBJECT_PROP_DESC,
73    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
74    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
75    MTP_OPERATION_GET_OBJECT_PROP_LIST,
76//    MTP_OPERATION_SET_OBJECT_PROP_LIST,
77//    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
78//    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
79    MTP_OPERATION_GET_OBJECT_REFERENCES,
80    MTP_OPERATION_SET_OBJECT_REFERENCES,
81//    MTP_OPERATION_SKIP,
82};
83
84static const MtpEventCode kSupportedEventCodes[] = {
85    MTP_EVENT_OBJECT_ADDED,
86    MTP_EVENT_OBJECT_REMOVED,
87};
88
89MtpServer::MtpServer(int fd, MtpDatabase* database,
90                    int fileGroup, int filePerm, int directoryPerm)
91    :   mFD(fd),
92        mDatabase(database),
93        mFileGroup(fileGroup),
94        mFilePermission(filePerm),
95        mDirectoryPermission(directoryPerm),
96        mSessionID(0),
97        mSessionOpen(false),
98        mSendObjectHandle(kInvalidObjectHandle),
99        mSendObjectFormat(0),
100        mSendObjectFileSize(0)
101{
102}
103
104MtpServer::~MtpServer() {
105}
106
107void MtpServer::addStorage(const char* filePath) {
108    int index = mStorages.size() + 1;
109    index |= index << 16;   // set high and low part to our index
110    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
111    addStorage(storage);
112}
113
114MtpStorage* MtpServer::getStorage(MtpStorageID id) {
115    for (int i = 0; i < mStorages.size(); i++) {
116        MtpStorage* storage =  mStorages[i];
117        if (storage->getStorageID() == id)
118            return storage;
119    }
120    return NULL;
121}
122
123void MtpServer::run() {
124    int fd = mFD;
125
126    LOGV("MtpServer::run fd: %d\n", fd);
127
128    while (1) {
129        int ret = mRequest.read(fd);
130        if (ret < 0) {
131            LOGE("request read returned %d, errno: %d", ret, errno);
132            if (errno == ECANCELED) {
133                // return to top of loop and wait for next command
134                continue;
135            }
136            break;
137        }
138        MtpOperationCode operation = mRequest.getOperationCode();
139        MtpTransactionID transaction = mRequest.getTransactionID();
140
141        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
142        mRequest.dump();
143
144        // FIXME need to generalize this
145        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
146                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
147                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
148                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
149        if (dataIn) {
150            int ret = mData.read(fd);
151            if (ret < 0) {
152                LOGE("data read returned %d, errno: %d", ret, errno);
153                if (errno == ECANCELED) {
154                    // return to top of loop and wait for next command
155                    continue;
156                }
157                break;
158            }
159            LOGV("received data:");
160            mData.dump();
161        } else {
162            mData.reset();
163        }
164
165        if (handleRequest()) {
166            if (!dataIn && mData.hasData()) {
167                mData.setOperationCode(operation);
168                mData.setTransactionID(transaction);
169                LOGV("sending data:");
170                mData.dump();
171                ret = mData.write(fd);
172                if (ret < 0) {
173                    LOGE("request write returned %d, errno: %d", ret, errno);
174                    if (errno == ECANCELED) {
175                        // return to top of loop and wait for next command
176                        continue;
177                    }
178                    break;
179                }
180            }
181
182            mResponse.setTransactionID(transaction);
183            LOGV("sending response %04X", mResponse.getResponseCode());
184            ret = mResponse.write(fd);
185            mResponse.dump();
186            if (ret < 0) {
187                LOGE("request write returned %d, errno: %d", ret, errno);
188                if (errno == ECANCELED) {
189                    // return to top of loop and wait for next command
190                    continue;
191                }
192                break;
193            }
194        } else {
195            LOGV("skipping response\n");
196        }
197    }
198
199    if (mSessionOpen)
200        mDatabase->sessionEnded();
201}
202
203void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
204    if (mSessionOpen) {
205        LOGD("sendObjectAdded %d\n", handle);
206        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
207        mEvent.setTransactionID(mRequest.getTransactionID());
208        mEvent.setParameter(1, handle);
209        int ret = mEvent.write(mFD);
210        LOGD("mEvent.write returned %d\n", ret);
211    }
212}
213
214void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
215    if (mSessionOpen) {
216        LOGD("sendObjectRemoved %d\n", handle);
217        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
218        mEvent.setTransactionID(mRequest.getTransactionID());
219        mEvent.setParameter(1, handle);
220        int ret = mEvent.write(mFD);
221        LOGD("mEvent.write returned %d\n", ret);
222    }
223}
224
225bool MtpServer::handleRequest() {
226    MtpOperationCode operation = mRequest.getOperationCode();
227    MtpResponseCode response;
228
229    mResponse.reset();
230
231    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
232        // FIXME - need to delete mSendObjectHandle from the database
233        LOGE("expected SendObject after SendObjectInfo");
234        mSendObjectHandle = kInvalidObjectHandle;
235    }
236
237    switch (operation) {
238        case MTP_OPERATION_GET_DEVICE_INFO:
239            response = doGetDeviceInfo();
240            break;
241        case MTP_OPERATION_OPEN_SESSION:
242            response = doOpenSession();
243            break;
244        case MTP_OPERATION_CLOSE_SESSION:
245            response = doCloseSession();
246            break;
247        case MTP_OPERATION_GET_STORAGE_IDS:
248            response = doGetStorageIDs();
249            break;
250         case MTP_OPERATION_GET_STORAGE_INFO:
251            response = doGetStorageInfo();
252            break;
253        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
254            response = doGetObjectPropsSupported();
255            break;
256        case MTP_OPERATION_GET_OBJECT_HANDLES:
257            response = doGetObjectHandles();
258            break;
259        case MTP_OPERATION_GET_NUM_OBJECTS:
260            response = doGetNumObjects();
261            break;
262        case MTP_OPERATION_GET_OBJECT_REFERENCES:
263            response = doGetObjectReferences();
264            break;
265        case MTP_OPERATION_SET_OBJECT_REFERENCES:
266            response = doSetObjectReferences();
267            break;
268        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
269            response = doGetObjectPropValue();
270            break;
271        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
272            response = doSetObjectPropValue();
273            break;
274        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
275            response = doGetDevicePropValue();
276            break;
277        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
278            response = doSetDevicePropValue();
279            break;
280        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
281            response = doResetDevicePropValue();
282            break;
283        case MTP_OPERATION_GET_OBJECT_PROP_LIST:
284            response = doGetObjectPropList();
285            break;
286        case MTP_OPERATION_GET_OBJECT_INFO:
287            response = doGetObjectInfo();
288            break;
289        case MTP_OPERATION_GET_OBJECT:
290            response = doGetObject();
291            break;
292        case MTP_OPERATION_SEND_OBJECT_INFO:
293            response = doSendObjectInfo();
294            break;
295        case MTP_OPERATION_SEND_OBJECT:
296            response = doSendObject();
297            break;
298        case MTP_OPERATION_DELETE_OBJECT:
299            response = doDeleteObject();
300            break;
301        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
302            response = doGetObjectPropDesc();
303            break;
304        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
305            response = doGetDevicePropDesc();
306            break;
307        default:
308            LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
309            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
310            break;
311    }
312
313    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
314        return false;
315    mResponse.setResponseCode(response);
316    return true;
317}
318
319MtpResponseCode MtpServer::doGetDeviceInfo() {
320    MtpStringBuffer   string;
321    char prop_value[PROPERTY_VALUE_MAX];
322
323    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
324    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
325    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
326
327    // fill in device info
328    mData.putUInt16(MTP_STANDARD_VERSION);
329    mData.putUInt32(6); // MTP Vendor Extension ID
330    mData.putUInt16(MTP_STANDARD_VERSION);
331    string.set("microsoft.com: 1.0;");
332    mData.putString(string); // MTP Extensions
333    mData.putUInt16(0); //Functional Mode
334    mData.putAUInt16(kSupportedOperationCodes,
335            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
336    mData.putAUInt16(kSupportedEventCodes,
337            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
338    mData.putAUInt16(deviceProperties); // Device Properties Supported
339    mData.putAUInt16(captureFormats); // Capture Formats
340    mData.putAUInt16(playbackFormats);  // Playback Formats
341    // FIXME
342    string.set("Google, Inc.");
343    mData.putString(string);   // Manufacturer
344
345    property_get("ro.product.model", prop_value, "MTP Device");
346    string.set(prop_value);
347    mData.putString(string);   // Model
348    string.set("1.0");
349    mData.putString(string);   // Device Version
350
351    property_get("ro.serialno", prop_value, "????????");
352    string.set(prop_value);
353    mData.putString(string);   // Serial Number
354
355    delete playbackFormats;
356    delete captureFormats;
357    delete deviceProperties;
358
359    return MTP_RESPONSE_OK;
360}
361
362MtpResponseCode MtpServer::doOpenSession() {
363    if (mSessionOpen) {
364        mResponse.setParameter(1, mSessionID);
365        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
366    }
367    mSessionID = mRequest.getParameter(1);
368    mSessionOpen = true;
369
370    mDatabase->sessionStarted();
371
372    return MTP_RESPONSE_OK;
373}
374
375MtpResponseCode MtpServer::doCloseSession() {
376    if (!mSessionOpen)
377        return MTP_RESPONSE_SESSION_NOT_OPEN;
378    mSessionID = 0;
379    mSessionOpen = false;
380    mDatabase->sessionEnded();
381    return MTP_RESPONSE_OK;
382}
383
384MtpResponseCode MtpServer::doGetStorageIDs() {
385    if (!mSessionOpen)
386        return MTP_RESPONSE_SESSION_NOT_OPEN;
387
388    int count = mStorages.size();
389    mData.putUInt32(count);
390    for (int i = 0; i < count; i++)
391        mData.putUInt32(mStorages[i]->getStorageID());
392
393    return MTP_RESPONSE_OK;
394}
395
396MtpResponseCode MtpServer::doGetStorageInfo() {
397    MtpStringBuffer   string;
398
399    if (!mSessionOpen)
400        return MTP_RESPONSE_SESSION_NOT_OPEN;
401    MtpStorageID id = mRequest.getParameter(1);
402    MtpStorage* storage = getStorage(id);
403    if (!storage)
404        return MTP_RESPONSE_INVALID_STORAGE_ID;
405
406    mData.putUInt16(storage->getType());
407    mData.putUInt16(storage->getFileSystemType());
408    mData.putUInt16(storage->getAccessCapability());
409    mData.putUInt64(storage->getMaxCapacity());
410    mData.putUInt64(storage->getFreeSpace());
411    mData.putUInt32(1024*1024*1024); // Free Space in Objects
412    string.set(storage->getDescription());
413    mData.putString(string);
414    mData.putEmptyString();   // Volume Identifier
415
416    return MTP_RESPONSE_OK;
417}
418
419MtpResponseCode MtpServer::doGetObjectPropsSupported() {
420    if (!mSessionOpen)
421        return MTP_RESPONSE_SESSION_NOT_OPEN;
422    MtpObjectFormat format = mRequest.getParameter(1);
423    MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
424    mData.putAUInt16(properties);
425    delete properties;
426    return MTP_RESPONSE_OK;
427}
428
429MtpResponseCode MtpServer::doGetObjectHandles() {
430    if (!mSessionOpen)
431        return MTP_RESPONSE_SESSION_NOT_OPEN;
432    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
433    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
434    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
435                                                            // 0x00000000 for all objects?
436    if (parent == 0xFFFFFFFF)
437        parent = 0;
438
439    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
440    mData.putAUInt32(handles);
441    delete handles;
442    return MTP_RESPONSE_OK;
443}
444
445MtpResponseCode MtpServer::doGetNumObjects() {
446    if (!mSessionOpen)
447        return MTP_RESPONSE_SESSION_NOT_OPEN;
448    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
449    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
450    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
451                                                            // 0x00000000 for all objects?
452    if (parent == 0xFFFFFFFF)
453        parent = 0;
454
455    int count = mDatabase->getNumObjects(storageID, format, parent);
456    if (count >= 0) {
457        mResponse.setParameter(1, count);
458        return MTP_RESPONSE_OK;
459    } else {
460        mResponse.setParameter(1, 0);
461        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
462    }
463}
464
465MtpResponseCode MtpServer::doGetObjectReferences() {
466    if (!mSessionOpen)
467        return MTP_RESPONSE_SESSION_NOT_OPEN;
468    MtpStorageID handle = mRequest.getParameter(1);
469
470    // FIXME - check for invalid object handle
471    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
472    if (handles) {
473        mData.putAUInt32(handles);
474        delete handles;
475    } else {
476        mData.putEmptyArray();
477    }
478    return MTP_RESPONSE_OK;
479}
480
481MtpResponseCode MtpServer::doSetObjectReferences() {
482    if (!mSessionOpen)
483        return MTP_RESPONSE_SESSION_NOT_OPEN;
484    MtpStorageID handle = mRequest.getParameter(1);
485    MtpObjectHandleList* references = mData.getAUInt32();
486    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
487    delete references;
488    return result;
489}
490
491MtpResponseCode MtpServer::doGetObjectPropValue() {
492    MtpObjectHandle handle = mRequest.getParameter(1);
493    MtpObjectProperty property = mRequest.getParameter(2);
494    LOGD("GetObjectPropValue %d %s\n", handle,
495            MtpDebug::getObjectPropCodeName(property));
496
497    return mDatabase->getObjectPropertyValue(handle, property, mData);
498}
499
500MtpResponseCode MtpServer::doSetObjectPropValue() {
501    MtpObjectHandle handle = mRequest.getParameter(1);
502    MtpObjectProperty property = mRequest.getParameter(2);
503    LOGD("SetObjectPropValue %d %s\n", handle,
504            MtpDebug::getObjectPropCodeName(property));
505
506    return mDatabase->setObjectPropertyValue(handle, property, mData);
507}
508
509MtpResponseCode MtpServer::doGetDevicePropValue() {
510    MtpDeviceProperty property = mRequest.getParameter(1);
511    LOGD("GetDevicePropValue %s\n",
512            MtpDebug::getDevicePropCodeName(property));
513
514    return mDatabase->getDevicePropertyValue(property, mData);
515}
516
517MtpResponseCode MtpServer::doSetDevicePropValue() {
518    MtpDeviceProperty property = mRequest.getParameter(1);
519    LOGD("SetDevicePropValue %s\n",
520            MtpDebug::getDevicePropCodeName(property));
521
522    return mDatabase->setDevicePropertyValue(property, mData);
523}
524
525MtpResponseCode MtpServer::doResetDevicePropValue() {
526    MtpDeviceProperty property = mRequest.getParameter(1);
527    LOGD("ResetDevicePropValue %s\n",
528            MtpDebug::getDevicePropCodeName(property));
529
530    return mDatabase->resetDeviceProperty(property);
531}
532
533MtpResponseCode MtpServer::doGetObjectPropList() {
534
535    MtpObjectHandle handle = mRequest.getParameter(1);
536    MtpObjectFormat format = mRequest.getParameter(2);
537    MtpDeviceProperty property = mRequest.getParameter(3);
538    int groupCode = mRequest.getParameter(4);
539    int depth = mRequest.getParameter(4);
540   LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
541            handle, MtpDebug::getFormatCodeName(format),
542            MtpDebug::getObjectPropCodeName(property), groupCode, depth);
543
544    return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
545}
546
547MtpResponseCode MtpServer::doGetObjectInfo() {
548    MtpObjectHandle handle = mRequest.getParameter(1);
549    return mDatabase->getObjectInfo(handle, mData);
550}
551
552MtpResponseCode MtpServer::doGetObject() {
553    MtpObjectHandle handle = mRequest.getParameter(1);
554    MtpString pathBuf;
555    int64_t fileLength;
556    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
557    if (result != MTP_RESPONSE_OK)
558        return result;
559
560    const char* filePath = (const char *)pathBuf;
561    mtp_file_range  mfr;
562    mfr.fd = open(filePath, O_RDONLY);
563    if (mfr.fd < 0) {
564        return MTP_RESPONSE_GENERAL_ERROR;
565    }
566    mfr.offset = 0;
567    mfr.length = fileLength;
568
569    // send data header
570    mData.setOperationCode(mRequest.getOperationCode());
571    mData.setTransactionID(mRequest.getTransactionID());
572    mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
573
574    // then transfer the file
575    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
576    close(mfr.fd);
577    if (ret < 0) {
578        if (errno == ECANCELED)
579            return MTP_RESPONSE_TRANSACTION_CANCELLED;
580        else
581            return MTP_RESPONSE_GENERAL_ERROR;
582    }
583    return MTP_RESPONSE_OK;
584}
585
586MtpResponseCode MtpServer::doSendObjectInfo() {
587    MtpString path;
588    MtpStorageID storageID = mRequest.getParameter(1);
589    MtpStorage* storage = getStorage(storageID);
590    MtpObjectHandle parent = mRequest.getParameter(2);
591    if (!storage)
592        return MTP_RESPONSE_INVALID_STORAGE_ID;
593
594    // special case the root
595    if (parent == MTP_PARENT_ROOT) {
596        path = storage->getPath();
597        parent = 0;
598    } else {
599        int64_t dummy;
600        int result = mDatabase->getObjectFilePath(parent, path, dummy);
601        if (result != MTP_RESPONSE_OK)
602            return result;
603    }
604
605    // read only the fields we need
606    mData.getUInt32();  // storage ID
607    MtpObjectFormat format = mData.getUInt16();
608    mData.getUInt16();  // protection status
609    mSendObjectFileSize = mData.getUInt32();
610    mData.getUInt16();  // thumb format
611    mData.getUInt32();  // thumb compressed size
612    mData.getUInt32();  // thumb pix width
613    mData.getUInt32();  // thumb pix height
614    mData.getUInt32();  // image pix width
615    mData.getUInt32();  // image pix height
616    mData.getUInt32();  // image bit depth
617    mData.getUInt32();  // parent
618    uint16_t associationType = mData.getUInt16();
619    uint32_t associationDesc = mData.getUInt32();   // association desc
620    mData.getUInt32();  // sequence number
621    MtpStringBuffer name, created, modified;
622    mData.getString(name);    // file name
623    mData.getString(created);      // date created
624    mData.getString(modified);     // date modified
625    // keywords follow
626
627    LOGD("name: %s format: %04X\n", (const char *)name, format);
628    time_t modifiedTime;
629    if (!parseDateTime(modified, modifiedTime))
630        modifiedTime = 0;
631
632    if (path[path.size() - 1] != '/')
633        path += "/";
634    path += (const char *)name;
635
636    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
637            format, parent, storageID, mSendObjectFileSize, modifiedTime);
638    if (handle == kInvalidObjectHandle) {
639        return MTP_RESPONSE_GENERAL_ERROR;
640    }
641
642  if (format == MTP_FORMAT_ASSOCIATION) {
643        mode_t mask = umask(0);
644        int ret = mkdir((const char *)path, mDirectoryPermission);
645        umask(mask);
646        if (ret && ret != -EEXIST)
647            return MTP_RESPONSE_GENERAL_ERROR;
648        chown((const char *)path, getuid(), mFileGroup);
649    } else {
650        mSendObjectFilePath = path;
651        // save the handle for the SendObject call, which should follow
652        mSendObjectHandle = handle;
653        mSendObjectFormat = format;
654    }
655
656    mResponse.setParameter(1, storageID);
657    mResponse.setParameter(2, parent);
658    mResponse.setParameter(3, handle);
659
660    return MTP_RESPONSE_OK;
661}
662
663MtpResponseCode MtpServer::doSendObject() {
664    MtpResponseCode result = MTP_RESPONSE_OK;
665    mode_t mask;
666    int ret;
667    uint64_t actualSize = -1;
668
669    if (mSendObjectHandle == kInvalidObjectHandle) {
670        LOGE("Expected SendObjectInfo before SendObject");
671        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
672        goto done;
673    }
674
675    // read the header
676    ret = mData.readDataHeader(mFD);
677    // FIXME - check for errors here.
678
679    // reset so we don't attempt to send this back
680    mData.reset();
681
682    mtp_file_range  mfr;
683    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
684    if (mfr.fd < 0) {
685        result = MTP_RESPONSE_GENERAL_ERROR;
686        goto done;
687    }
688    fchown(mfr.fd, getuid(), mFileGroup);
689    // set permissions
690    mask = umask(0);
691    fchmod(mfr.fd, mFilePermission);
692    umask(mask);
693
694    mfr.offset = 0;
695    mfr.length = mSendObjectFileSize;
696
697    LOGD("receiving %s\n", (const char *)mSendObjectFilePath);
698    // transfer the file
699    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
700    close(mfr.fd);
701
702    LOGV("MTP_RECEIVE_FILE returned %d", ret);
703
704    if (ret < 0) {
705        unlink(mSendObjectFilePath);
706        if (errno == ECANCELED)
707            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
708        else
709            result = MTP_RESPONSE_GENERAL_ERROR;
710    } else if (mSendObjectFileSize == 0xFFFFFFFF) {
711        // actual size is likely > 4 gig so stat the file to compute actual length
712        struct stat s;
713        if (lstat(mSendObjectFilePath, &s) == 0) {
714            actualSize = s.st_size;
715            LOGD("actualSize: %lld\n", actualSize);
716        }
717    }
718
719done:
720    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
721            actualSize, result == MTP_RESPONSE_OK);
722    mSendObjectHandle = kInvalidObjectHandle;
723    mSendObjectFormat = 0;
724    return result;
725}
726
727static void deleteRecursive(const char* path) {
728    char pathbuf[PATH_MAX];
729    int pathLength = strlen(path);
730    if (pathLength >= sizeof(pathbuf) - 1) {
731        LOGE("path too long: %s\n", path);
732    }
733    strcpy(pathbuf, path);
734    if (pathbuf[pathLength - 1] != '/') {
735        pathbuf[pathLength++] = '/';
736    }
737    char* fileSpot = pathbuf + pathLength;
738    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
739
740    DIR* dir = opendir(path);
741    if (!dir) {
742        LOGE("opendir %s failed: %s", path, strerror(errno));
743        return;
744    }
745
746    struct dirent* entry;
747    while ((entry = readdir(dir))) {
748        const char* name = entry->d_name;
749
750        // ignore "." and ".."
751        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
752            continue;
753        }
754
755        int nameLength = strlen(name);
756        if (nameLength > pathRemaining) {
757            LOGE("path %s/%s too long\n", path, name);
758            continue;
759        }
760        strcpy(fileSpot, name);
761
762        int type = entry->d_type;
763        if (entry->d_type == DT_DIR) {
764            deleteRecursive(pathbuf);
765            rmdir(pathbuf);
766        } else {
767            unlink(pathbuf);
768        }
769    }
770    closedir(dir);
771}
772
773static void deletePath(const char* path) {
774    struct stat statbuf;
775    if (stat(path, &statbuf) == 0) {
776        if (S_ISDIR(statbuf.st_mode)) {
777            deleteRecursive(path);
778            rmdir(path);
779        } else {
780            unlink(path);
781        }
782    } else {
783        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
784    }
785}
786
787MtpResponseCode MtpServer::doDeleteObject() {
788    MtpObjectHandle handle = mRequest.getParameter(1);
789    MtpObjectFormat format = mRequest.getParameter(2);
790    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
791    // FIXME - implement deleting objects by format
792
793    MtpString filePath;
794    int64_t fileLength;
795    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
796    if (result == MTP_RESPONSE_OK) {
797        LOGV("deleting %s", (const char *)filePath);
798        deletePath((const char *)filePath);
799        return mDatabase->deleteFile(handle);
800    } else {
801        return result;
802    }
803}
804
805MtpResponseCode MtpServer::doGetObjectPropDesc() {
806    MtpObjectProperty propCode = mRequest.getParameter(1);
807    MtpObjectFormat format = mRequest.getParameter(2);
808    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
809                                        MtpDebug::getFormatCodeName(format));
810    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
811    if (!property)
812        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
813    property->write(mData);
814    delete property;
815    return MTP_RESPONSE_OK;
816}
817
818MtpResponseCode MtpServer::doGetDevicePropDesc() {
819    MtpDeviceProperty propCode = mRequest.getParameter(1);
820    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
821    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
822    if (!property)
823        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
824    property->write(mData);
825    delete property;
826    return MTP_RESPONSE_OK;
827}
828
829}  // namespace android
830