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