MtpServer.cpp revision 8d08c5aeee05425357b6603a22fa73fd6e095a47
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
347    property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
348    string.set(prop_value);
349    mData.putString(string);   // Manufacturer
350
351    property_get("ro.product.model", prop_value, "MTP Device");
352    string.set(prop_value);
353    mData.putString(string);   // Model
354    string.set("1.0");
355    mData.putString(string);   // Device Version
356
357    property_get("ro.serialno", prop_value, "????????");
358    string.set(prop_value);
359    mData.putString(string);   // Serial Number
360
361    delete playbackFormats;
362    delete captureFormats;
363    delete deviceProperties;
364
365    return MTP_RESPONSE_OK;
366}
367
368MtpResponseCode MtpServer::doOpenSession() {
369    if (mSessionOpen) {
370        mResponse.setParameter(1, mSessionID);
371        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
372    }
373    mSessionID = mRequest.getParameter(1);
374    mSessionOpen = true;
375
376    mDatabase->sessionStarted();
377
378    return MTP_RESPONSE_OK;
379}
380
381MtpResponseCode MtpServer::doCloseSession() {
382    if (!mSessionOpen)
383        return MTP_RESPONSE_SESSION_NOT_OPEN;
384    mSessionID = 0;
385    mSessionOpen = false;
386    mDatabase->sessionEnded();
387    return MTP_RESPONSE_OK;
388}
389
390MtpResponseCode MtpServer::doGetStorageIDs() {
391    if (!mSessionOpen)
392        return MTP_RESPONSE_SESSION_NOT_OPEN;
393
394    int count = mStorages.size();
395    mData.putUInt32(count);
396    for (int i = 0; i < count; i++)
397        mData.putUInt32(mStorages[i]->getStorageID());
398
399    return MTP_RESPONSE_OK;
400}
401
402MtpResponseCode MtpServer::doGetStorageInfo() {
403    MtpStringBuffer   string;
404
405    if (!mSessionOpen)
406        return MTP_RESPONSE_SESSION_NOT_OPEN;
407    MtpStorageID id = mRequest.getParameter(1);
408    MtpStorage* storage = getStorage(id);
409    if (!storage)
410        return MTP_RESPONSE_INVALID_STORAGE_ID;
411
412    mData.putUInt16(storage->getType());
413    mData.putUInt16(storage->getFileSystemType());
414    mData.putUInt16(storage->getAccessCapability());
415    mData.putUInt64(storage->getMaxCapacity());
416    mData.putUInt64(storage->getFreeSpace());
417    mData.putUInt32(1024*1024*1024); // Free Space in Objects
418    string.set(storage->getDescription());
419    mData.putString(string);
420    mData.putEmptyString();   // Volume Identifier
421
422    return MTP_RESPONSE_OK;
423}
424
425MtpResponseCode MtpServer::doGetObjectPropsSupported() {
426    if (!mSessionOpen)
427        return MTP_RESPONSE_SESSION_NOT_OPEN;
428    MtpObjectFormat format = mRequest.getParameter(1);
429    MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
430    mData.putAUInt16(properties);
431    delete properties;
432    return MTP_RESPONSE_OK;
433}
434
435MtpResponseCode MtpServer::doGetObjectHandles() {
436    if (!mSessionOpen)
437        return MTP_RESPONSE_SESSION_NOT_OPEN;
438    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
439    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
440    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
441                                                            // 0x00000000 for all objects?
442    if (parent == 0xFFFFFFFF)
443        parent = 0;
444
445    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
446    mData.putAUInt32(handles);
447    delete handles;
448    return MTP_RESPONSE_OK;
449}
450
451MtpResponseCode MtpServer::doGetNumObjects() {
452    if (!mSessionOpen)
453        return MTP_RESPONSE_SESSION_NOT_OPEN;
454    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
455    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
456    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
457                                                            // 0x00000000 for all objects?
458    if (parent == 0xFFFFFFFF)
459        parent = 0;
460
461    int count = mDatabase->getNumObjects(storageID, format, parent);
462    if (count >= 0) {
463        mResponse.setParameter(1, count);
464        return MTP_RESPONSE_OK;
465    } else {
466        mResponse.setParameter(1, 0);
467        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
468    }
469}
470
471MtpResponseCode MtpServer::doGetObjectReferences() {
472    if (!mSessionOpen)
473        return MTP_RESPONSE_SESSION_NOT_OPEN;
474    MtpStorageID handle = mRequest.getParameter(1);
475
476    // FIXME - check for invalid object handle
477    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
478    if (handles) {
479        mData.putAUInt32(handles);
480        delete handles;
481    } else {
482        mData.putEmptyArray();
483    }
484    return MTP_RESPONSE_OK;
485}
486
487MtpResponseCode MtpServer::doSetObjectReferences() {
488    if (!mSessionOpen)
489        return MTP_RESPONSE_SESSION_NOT_OPEN;
490    MtpStorageID handle = mRequest.getParameter(1);
491    MtpObjectHandleList* references = mData.getAUInt32();
492    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
493    delete references;
494    return result;
495}
496
497MtpResponseCode MtpServer::doGetObjectPropValue() {
498    MtpObjectHandle handle = mRequest.getParameter(1);
499    MtpObjectProperty property = mRequest.getParameter(2);
500    LOGV("GetObjectPropValue %d %s\n", handle,
501            MtpDebug::getObjectPropCodeName(property));
502
503    return mDatabase->getObjectPropertyValue(handle, property, mData);
504}
505
506MtpResponseCode MtpServer::doSetObjectPropValue() {
507    MtpObjectHandle handle = mRequest.getParameter(1);
508    MtpObjectProperty property = mRequest.getParameter(2);
509    LOGV("SetObjectPropValue %d %s\n", handle,
510            MtpDebug::getObjectPropCodeName(property));
511
512    return mDatabase->setObjectPropertyValue(handle, property, mData);
513}
514
515MtpResponseCode MtpServer::doGetDevicePropValue() {
516    MtpDeviceProperty property = mRequest.getParameter(1);
517    LOGV("GetDevicePropValue %s\n",
518            MtpDebug::getDevicePropCodeName(property));
519
520    return mDatabase->getDevicePropertyValue(property, mData);
521}
522
523MtpResponseCode MtpServer::doSetDevicePropValue() {
524    MtpDeviceProperty property = mRequest.getParameter(1);
525    LOGV("SetDevicePropValue %s\n",
526            MtpDebug::getDevicePropCodeName(property));
527
528    return mDatabase->setDevicePropertyValue(property, mData);
529}
530
531MtpResponseCode MtpServer::doResetDevicePropValue() {
532    MtpDeviceProperty property = mRequest.getParameter(1);
533    LOGV("ResetDevicePropValue %s\n",
534            MtpDebug::getDevicePropCodeName(property));
535
536    return mDatabase->resetDeviceProperty(property);
537}
538
539MtpResponseCode MtpServer::doGetObjectPropList() {
540
541    MtpObjectHandle handle = mRequest.getParameter(1);
542    // use uint32_t so we can support 0xFFFFFFFF
543    uint32_t format = mRequest.getParameter(2);
544    uint32_t property = mRequest.getParameter(3);
545    int groupCode = mRequest.getParameter(4);
546    int depth = mRequest.getParameter(5);
547   LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
548            handle, MtpDebug::getFormatCodeName(format),
549            MtpDebug::getObjectPropCodeName(property), groupCode, depth);
550
551    return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
552}
553
554MtpResponseCode MtpServer::doGetObjectInfo() {
555    MtpObjectHandle handle = mRequest.getParameter(1);
556    return mDatabase->getObjectInfo(handle, mData);
557}
558
559MtpResponseCode MtpServer::doGetObject() {
560    MtpObjectHandle handle = mRequest.getParameter(1);
561    MtpString pathBuf;
562    int64_t fileLength;
563    MtpObjectFormat format;
564    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
565    if (result != MTP_RESPONSE_OK)
566        return result;
567
568    const char* filePath = (const char *)pathBuf;
569    mtp_file_range  mfr;
570    mfr.fd = open(filePath, O_RDONLY);
571    if (mfr.fd < 0) {
572        return MTP_RESPONSE_GENERAL_ERROR;
573    }
574    mfr.offset = 0;
575    mfr.length = fileLength;
576
577    // send data header
578    mData.setOperationCode(mRequest.getOperationCode());
579    mData.setTransactionID(mRequest.getTransactionID());
580    mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
581
582    // then transfer the file
583    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
584    close(mfr.fd);
585    if (ret < 0) {
586        if (errno == ECANCELED)
587            return MTP_RESPONSE_TRANSACTION_CANCELLED;
588        else
589            return MTP_RESPONSE_GENERAL_ERROR;
590    }
591    return MTP_RESPONSE_OK;
592}
593
594MtpResponseCode MtpServer::doGetPartialObject() {
595    MtpObjectHandle handle = mRequest.getParameter(1);
596    uint32_t offset = mRequest.getParameter(2);
597    uint32_t length = mRequest.getParameter(3);
598    MtpString pathBuf;
599    int64_t fileLength;
600    MtpObjectFormat format;
601    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
602    if (result != MTP_RESPONSE_OK)
603        return result;
604    if (offset + length > fileLength)
605        length = fileLength - offset;
606
607    const char* filePath = (const char *)pathBuf;
608    mtp_file_range  mfr;
609    mfr.fd = open(filePath, O_RDONLY);
610    if (mfr.fd < 0) {
611        return MTP_RESPONSE_GENERAL_ERROR;
612    }
613    mfr.offset = offset;
614    mfr.length = length;
615    mResponse.setParameter(1, length);
616
617    // send data header
618    mData.setOperationCode(mRequest.getOperationCode());
619    mData.setTransactionID(mRequest.getTransactionID());
620    mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
621
622    // then transfer the file
623    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
624    close(mfr.fd);
625    if (ret < 0) {
626        if (errno == ECANCELED)
627            return MTP_RESPONSE_TRANSACTION_CANCELLED;
628        else
629            return MTP_RESPONSE_GENERAL_ERROR;
630    }
631    return MTP_RESPONSE_OK;
632}
633
634MtpResponseCode MtpServer::doSendObjectInfo() {
635    MtpString path;
636    MtpStorageID storageID = mRequest.getParameter(1);
637    MtpStorage* storage = getStorage(storageID);
638    MtpObjectHandle parent = mRequest.getParameter(2);
639    if (!storage)
640        return MTP_RESPONSE_INVALID_STORAGE_ID;
641
642    // special case the root
643    if (parent == MTP_PARENT_ROOT) {
644        path = storage->getPath();
645        parent = 0;
646    } else {
647        int64_t length;
648        MtpObjectFormat format;
649        int result = mDatabase->getObjectFilePath(parent, path, length, format);
650        if (result != MTP_RESPONSE_OK)
651            return result;
652        if (format != MTP_FORMAT_ASSOCIATION)
653            return MTP_RESPONSE_INVALID_PARENT_OBJECT;
654    }
655
656    // read only the fields we need
657    mData.getUInt32();  // storage ID
658    MtpObjectFormat format = mData.getUInt16();
659    mData.getUInt16();  // protection status
660    mSendObjectFileSize = mData.getUInt32();
661    mData.getUInt16();  // thumb format
662    mData.getUInt32();  // thumb compressed size
663    mData.getUInt32();  // thumb pix width
664    mData.getUInt32();  // thumb pix height
665    mData.getUInt32();  // image pix width
666    mData.getUInt32();  // image pix height
667    mData.getUInt32();  // image bit depth
668    mData.getUInt32();  // parent
669    uint16_t associationType = mData.getUInt16();
670    uint32_t associationDesc = mData.getUInt32();   // association desc
671    mData.getUInt32();  // sequence number
672    MtpStringBuffer name, created, modified;
673    mData.getString(name);    // file name
674    mData.getString(created);      // date created
675    mData.getString(modified);     // date modified
676    // keywords follow
677
678    LOGV("name: %s format: %04X\n", (const char *)name, format);
679    time_t modifiedTime;
680    if (!parseDateTime(modified, modifiedTime))
681        modifiedTime = 0;
682
683    if (path[path.size() - 1] != '/')
684        path += "/";
685    path += (const char *)name;
686
687    // check space first
688    if (mSendObjectFileSize > storage->getFreeSpace())
689        return MTP_RESPONSE_STORAGE_FULL;
690
691    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
692            format, parent, storageID, mSendObjectFileSize, modifiedTime);
693    if (handle == kInvalidObjectHandle) {
694        return MTP_RESPONSE_GENERAL_ERROR;
695    }
696
697  if (format == MTP_FORMAT_ASSOCIATION) {
698        mode_t mask = umask(0);
699        int ret = mkdir((const char *)path, mDirectoryPermission);
700        umask(mask);
701        if (ret && ret != -EEXIST)
702            return MTP_RESPONSE_GENERAL_ERROR;
703        chown((const char *)path, getuid(), mFileGroup);
704
705        // SendObject does not get sent for directories, so call endSendObject here instead
706        mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
707    } else {
708        mSendObjectFilePath = path;
709        // save the handle for the SendObject call, which should follow
710        mSendObjectHandle = handle;
711        mSendObjectFormat = format;
712    }
713
714    mResponse.setParameter(1, storageID);
715    mResponse.setParameter(2, parent);
716    mResponse.setParameter(3, handle);
717
718    return MTP_RESPONSE_OK;
719}
720
721MtpResponseCode MtpServer::doSendObject() {
722    MtpResponseCode result = MTP_RESPONSE_OK;
723    mode_t mask;
724    int ret;
725
726    if (mSendObjectHandle == kInvalidObjectHandle) {
727        LOGE("Expected SendObjectInfo before SendObject");
728        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
729        goto done;
730    }
731
732    // read the header
733    ret = mData.readDataHeader(mFD);
734    // FIXME - check for errors here.
735
736    // reset so we don't attempt to send this back
737    mData.reset();
738
739    mtp_file_range  mfr;
740    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
741    if (mfr.fd < 0) {
742        result = MTP_RESPONSE_GENERAL_ERROR;
743        goto done;
744    }
745    fchown(mfr.fd, getuid(), mFileGroup);
746    // set permissions
747    mask = umask(0);
748    fchmod(mfr.fd, mFilePermission);
749    umask(mask);
750
751    mfr.offset = 0;
752    mfr.length = mSendObjectFileSize;
753
754    LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
755    // transfer the file
756    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
757    close(mfr.fd);
758
759    LOGV("MTP_RECEIVE_FILE returned %d", ret);
760
761    if (ret < 0) {
762        unlink(mSendObjectFilePath);
763        if (errno == ECANCELED)
764            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
765        else
766            result = MTP_RESPONSE_GENERAL_ERROR;
767    }
768
769done:
770    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
771            result == MTP_RESPONSE_OK);
772    mSendObjectHandle = kInvalidObjectHandle;
773    mSendObjectFormat = 0;
774    return result;
775}
776
777static void deleteRecursive(const char* path) {
778    char pathbuf[PATH_MAX];
779    int pathLength = strlen(path);
780    if (pathLength >= sizeof(pathbuf) - 1) {
781        LOGE("path too long: %s\n", path);
782    }
783    strcpy(pathbuf, path);
784    if (pathbuf[pathLength - 1] != '/') {
785        pathbuf[pathLength++] = '/';
786    }
787    char* fileSpot = pathbuf + pathLength;
788    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
789
790    DIR* dir = opendir(path);
791    if (!dir) {
792        LOGE("opendir %s failed: %s", path, strerror(errno));
793        return;
794    }
795
796    struct dirent* entry;
797    while ((entry = readdir(dir))) {
798        const char* name = entry->d_name;
799
800        // ignore "." and ".."
801        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
802            continue;
803        }
804
805        int nameLength = strlen(name);
806        if (nameLength > pathRemaining) {
807            LOGE("path %s/%s too long\n", path, name);
808            continue;
809        }
810        strcpy(fileSpot, name);
811
812        int type = entry->d_type;
813        if (entry->d_type == DT_DIR) {
814            deleteRecursive(pathbuf);
815            rmdir(pathbuf);
816        } else {
817            unlink(pathbuf);
818        }
819    }
820    closedir(dir);
821}
822
823static void deletePath(const char* path) {
824    struct stat statbuf;
825    if (stat(path, &statbuf) == 0) {
826        if (S_ISDIR(statbuf.st_mode)) {
827            deleteRecursive(path);
828            rmdir(path);
829        } else {
830            unlink(path);
831        }
832    } else {
833        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
834    }
835}
836
837MtpResponseCode MtpServer::doDeleteObject() {
838    MtpObjectHandle handle = mRequest.getParameter(1);
839    MtpObjectFormat format = mRequest.getParameter(2);
840    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
841    // FIXME - implement deleting objects by format
842
843    MtpString filePath;
844    int64_t fileLength;
845    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
846    if (result == MTP_RESPONSE_OK) {
847        LOGV("deleting %s", (const char *)filePath);
848        deletePath((const char *)filePath);
849        return mDatabase->deleteFile(handle);
850    } else {
851        return result;
852    }
853}
854
855MtpResponseCode MtpServer::doGetObjectPropDesc() {
856    MtpObjectProperty propCode = mRequest.getParameter(1);
857    MtpObjectFormat format = mRequest.getParameter(2);
858    LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
859                                        MtpDebug::getFormatCodeName(format));
860    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
861    if (!property)
862        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
863    property->write(mData);
864    delete property;
865    return MTP_RESPONSE_OK;
866}
867
868MtpResponseCode MtpServer::doGetDevicePropDesc() {
869    MtpDeviceProperty propCode = mRequest.getParameter(1);
870    LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
871    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
872    if (!property)
873        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
874    property->write(mData);
875    delete property;
876    return MTP_RESPONSE_OK;
877}
878
879}  // namespace android
880