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