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