MtpServer.cpp revision dcd89ecad321e2e052322fe2b1907d50d762b311
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 <inttypes.h>
24#include <errno.h>
25#include <sys/stat.h>
26#include <dirent.h>
27
28#include <cutils/properties.h>
29
30#define LOG_TAG "MtpServer"
31
32#include "MtpDebug.h"
33#include "MtpDatabase.h"
34#include "MtpObjectInfo.h"
35#include "MtpProperty.h"
36#include "MtpServer.h"
37#include "MtpStorage.h"
38#include "MtpStringBuffer.h"
39
40#include <linux/usb/f_mtp.h>
41
42namespace android {
43
44static const MtpOperationCode kSupportedOperationCodes[] = {
45    MTP_OPERATION_GET_DEVICE_INFO,
46    MTP_OPERATION_OPEN_SESSION,
47    MTP_OPERATION_CLOSE_SESSION,
48    MTP_OPERATION_GET_STORAGE_IDS,
49    MTP_OPERATION_GET_STORAGE_INFO,
50    MTP_OPERATION_GET_NUM_OBJECTS,
51    MTP_OPERATION_GET_OBJECT_HANDLES,
52    MTP_OPERATION_GET_OBJECT_INFO,
53    MTP_OPERATION_GET_OBJECT,
54    MTP_OPERATION_GET_THUMB,
55    MTP_OPERATION_DELETE_OBJECT,
56    MTP_OPERATION_SEND_OBJECT_INFO,
57    MTP_OPERATION_SEND_OBJECT,
58//    MTP_OPERATION_INITIATE_CAPTURE,
59//    MTP_OPERATION_FORMAT_STORE,
60//    MTP_OPERATION_RESET_DEVICE,
61//    MTP_OPERATION_SELF_TEST,
62//    MTP_OPERATION_SET_OBJECT_PROTECTION,
63//    MTP_OPERATION_POWER_DOWN,
64    MTP_OPERATION_GET_DEVICE_PROP_DESC,
65    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
66    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
67    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
68//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
69//    MTP_OPERATION_MOVE_OBJECT,
70//    MTP_OPERATION_COPY_OBJECT,
71    MTP_OPERATION_GET_PARTIAL_OBJECT,
72//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
73    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
74    MTP_OPERATION_GET_OBJECT_PROP_DESC,
75    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
76    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
77    MTP_OPERATION_GET_OBJECT_PROP_LIST,
78//    MTP_OPERATION_SET_OBJECT_PROP_LIST,
79//    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
80//    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
81    MTP_OPERATION_GET_OBJECT_REFERENCES,
82    MTP_OPERATION_SET_OBJECT_REFERENCES,
83//    MTP_OPERATION_SKIP,
84    // Android extension for direct file IO
85    MTP_OPERATION_GET_PARTIAL_OBJECT_64,
86    MTP_OPERATION_SEND_PARTIAL_OBJECT,
87    MTP_OPERATION_TRUNCATE_OBJECT,
88    MTP_OPERATION_BEGIN_EDIT_OBJECT,
89    MTP_OPERATION_END_EDIT_OBJECT,
90};
91
92static const MtpEventCode kSupportedEventCodes[] = {
93    MTP_EVENT_OBJECT_ADDED,
94    MTP_EVENT_OBJECT_REMOVED,
95    MTP_EVENT_STORE_ADDED,
96    MTP_EVENT_STORE_REMOVED,
97    MTP_EVENT_DEVICE_PROP_CHANGED,
98};
99
100MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
101                    int fileGroup, int filePerm, int directoryPerm)
102    :   mFD(fd),
103        mDatabase(database),
104        mPtp(ptp),
105        mFileGroup(fileGroup),
106        mFilePermission(filePerm),
107        mDirectoryPermission(directoryPerm),
108        mSessionID(0),
109        mSessionOpen(false),
110        mSendObjectHandle(kInvalidObjectHandle),
111        mSendObjectFormat(0),
112        mSendObjectFileSize(0)
113{
114}
115
116MtpServer::~MtpServer() {
117}
118
119void MtpServer::addStorage(MtpStorage* storage) {
120    Mutex::Autolock autoLock(mMutex);
121
122    mStorages.push(storage);
123    sendStoreAdded(storage->getStorageID());
124}
125
126void MtpServer::removeStorage(MtpStorage* storage) {
127    Mutex::Autolock autoLock(mMutex);
128
129    for (size_t i = 0; i < mStorages.size(); i++) {
130        if (mStorages[i] == storage) {
131            mStorages.removeAt(i);
132            sendStoreRemoved(storage->getStorageID());
133            break;
134        }
135    }
136}
137
138MtpStorage* MtpServer::getStorage(MtpStorageID id) {
139    if (id == 0)
140        return mStorages[0];
141    for (size_t i = 0; i < mStorages.size(); i++) {
142        MtpStorage* storage = mStorages[i];
143        if (storage->getStorageID() == id)
144            return storage;
145    }
146    return NULL;
147}
148
149bool MtpServer::hasStorage(MtpStorageID id) {
150    if (id == 0 || id == 0xFFFFFFFF)
151        return mStorages.size() > 0;
152    return (getStorage(id) != NULL);
153}
154
155void MtpServer::run() {
156    int fd = mFD;
157
158    ALOGV("MtpServer::run fd: %d\n", fd);
159
160    while (1) {
161        int ret = mRequest.read(fd);
162        if (ret < 0) {
163            ALOGV("request read returned %d, errno: %d", ret, errno);
164            if (errno == ECANCELED) {
165                // return to top of loop and wait for next command
166                continue;
167            }
168            break;
169        }
170        MtpOperationCode operation = mRequest.getOperationCode();
171        MtpTransactionID transaction = mRequest.getTransactionID();
172
173        ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
174        mRequest.dump();
175
176        // FIXME need to generalize this
177        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
178                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
179                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
180                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
181        if (dataIn) {
182            int ret = mData.read(fd);
183            if (ret < 0) {
184                ALOGE("data read returned %d, errno: %d", ret, errno);
185                if (errno == ECANCELED) {
186                    // return to top of loop and wait for next command
187                    continue;
188                }
189                break;
190            }
191            ALOGV("received data:");
192            mData.dump();
193        } else {
194            mData.reset();
195        }
196
197        if (handleRequest()) {
198            if (!dataIn && mData.hasData()) {
199                mData.setOperationCode(operation);
200                mData.setTransactionID(transaction);
201                ALOGV("sending data:");
202                mData.dump();
203                ret = mData.write(fd);
204                if (ret < 0) {
205                    ALOGE("request write returned %d, errno: %d", ret, errno);
206                    if (errno == ECANCELED) {
207                        // return to top of loop and wait for next command
208                        continue;
209                    }
210                    break;
211                }
212            }
213
214            mResponse.setTransactionID(transaction);
215            ALOGV("sending response %04X", mResponse.getResponseCode());
216            ret = mResponse.write(fd);
217            mResponse.dump();
218            if (ret < 0) {
219                ALOGE("request write returned %d, errno: %d", ret, errno);
220                if (errno == ECANCELED) {
221                    // return to top of loop and wait for next command
222                    continue;
223                }
224                break;
225            }
226        } else {
227            ALOGV("skipping response\n");
228        }
229    }
230
231    // commit any open edits
232    int count = mObjectEditList.size();
233    for (int i = 0; i < count; i++) {
234        ObjectEdit* edit = mObjectEditList[i];
235        commitEdit(edit);
236        delete edit;
237    }
238    mObjectEditList.clear();
239
240    if (mSessionOpen)
241        mDatabase->sessionEnded();
242    close(fd);
243    mFD = -1;
244}
245
246void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
247    ALOGV("sendObjectAdded %d\n", handle);
248    sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
249}
250
251void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
252    ALOGV("sendObjectRemoved %d\n", handle);
253    sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
254}
255
256void MtpServer::sendStoreAdded(MtpStorageID id) {
257    ALOGV("sendStoreAdded %08X\n", id);
258    sendEvent(MTP_EVENT_STORE_ADDED, id);
259}
260
261void MtpServer::sendStoreRemoved(MtpStorageID id) {
262    ALOGV("sendStoreRemoved %08X\n", id);
263    sendEvent(MTP_EVENT_STORE_REMOVED, id);
264}
265
266void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) {
267    ALOGV("sendDevicePropertyChanged %d\n", property);
268    sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property);
269}
270
271void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
272    if (mSessionOpen) {
273        mEvent.setEventCode(code);
274        mEvent.setTransactionID(mRequest.getTransactionID());
275        mEvent.setParameter(1, param1);
276        int ret = mEvent.write(mFD);
277        ALOGV("mEvent.write returned %d\n", ret);
278    }
279}
280
281void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
282        uint64_t size, MtpObjectFormat format, int fd) {
283    ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
284    mObjectEditList.add(edit);
285}
286
287MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
288    int count = mObjectEditList.size();
289    for (int i = 0; i < count; i++) {
290        ObjectEdit* edit = mObjectEditList[i];
291        if (edit->mHandle == handle) return edit;
292    }
293    return NULL;
294}
295
296void MtpServer::removeEditObject(MtpObjectHandle handle) {
297    int count = mObjectEditList.size();
298    for (int i = 0; i < count; i++) {
299        ObjectEdit* edit = mObjectEditList[i];
300        if (edit->mHandle == handle) {
301            delete edit;
302            mObjectEditList.removeAt(i);
303            return;
304        }
305    }
306    ALOGE("ObjectEdit not found in removeEditObject");
307}
308
309void MtpServer::commitEdit(ObjectEdit* edit) {
310    mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
311}
312
313
314bool MtpServer::handleRequest() {
315    Mutex::Autolock autoLock(mMutex);
316
317    MtpOperationCode operation = mRequest.getOperationCode();
318    MtpResponseCode response;
319
320    mResponse.reset();
321
322    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
323        // FIXME - need to delete mSendObjectHandle from the database
324        ALOGE("expected SendObject after SendObjectInfo");
325        mSendObjectHandle = kInvalidObjectHandle;
326    }
327
328    int containertype = mRequest.getContainerType();
329    if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
330        ALOGE("wrong container type %d", containertype);
331        return false;
332    }
333
334    ALOGV("got command %s (%x)", MtpDebug::getOperationCodeName(operation), operation);
335
336    switch (operation) {
337        case MTP_OPERATION_GET_DEVICE_INFO:
338            response = doGetDeviceInfo();
339            break;
340        case MTP_OPERATION_OPEN_SESSION:
341            response = doOpenSession();
342            break;
343        case MTP_OPERATION_CLOSE_SESSION:
344            response = doCloseSession();
345            break;
346        case MTP_OPERATION_GET_STORAGE_IDS:
347            response = doGetStorageIDs();
348            break;
349         case MTP_OPERATION_GET_STORAGE_INFO:
350            response = doGetStorageInfo();
351            break;
352        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
353            response = doGetObjectPropsSupported();
354            break;
355        case MTP_OPERATION_GET_OBJECT_HANDLES:
356            response = doGetObjectHandles();
357            break;
358        case MTP_OPERATION_GET_NUM_OBJECTS:
359            response = doGetNumObjects();
360            break;
361        case MTP_OPERATION_GET_OBJECT_REFERENCES:
362            response = doGetObjectReferences();
363            break;
364        case MTP_OPERATION_SET_OBJECT_REFERENCES:
365            response = doSetObjectReferences();
366            break;
367        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
368            response = doGetObjectPropValue();
369            break;
370        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
371            response = doSetObjectPropValue();
372            break;
373        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
374            response = doGetDevicePropValue();
375            break;
376        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
377            response = doSetDevicePropValue();
378            break;
379        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
380            response = doResetDevicePropValue();
381            break;
382        case MTP_OPERATION_GET_OBJECT_PROP_LIST:
383            response = doGetObjectPropList();
384            break;
385        case MTP_OPERATION_GET_OBJECT_INFO:
386            response = doGetObjectInfo();
387            break;
388        case MTP_OPERATION_GET_OBJECT:
389            response = doGetObject();
390            break;
391        case MTP_OPERATION_GET_THUMB:
392            response = doGetThumb();
393            break;
394        case MTP_OPERATION_GET_PARTIAL_OBJECT:
395        case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
396            response = doGetPartialObject(operation);
397            break;
398        case MTP_OPERATION_SEND_OBJECT_INFO:
399            response = doSendObjectInfo();
400            break;
401        case MTP_OPERATION_SEND_OBJECT:
402            response = doSendObject();
403            break;
404        case MTP_OPERATION_DELETE_OBJECT:
405            response = doDeleteObject();
406            break;
407        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
408            response = doGetObjectPropDesc();
409            break;
410        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
411            response = doGetDevicePropDesc();
412            break;
413        case MTP_OPERATION_SEND_PARTIAL_OBJECT:
414            response = doSendPartialObject();
415            break;
416        case MTP_OPERATION_TRUNCATE_OBJECT:
417            response = doTruncateObject();
418            break;
419        case MTP_OPERATION_BEGIN_EDIT_OBJECT:
420            response = doBeginEditObject();
421            break;
422        case MTP_OPERATION_END_EDIT_OBJECT:
423            response = doEndEditObject();
424            break;
425        default:
426            ALOGE("got unsupported command %s (%x)",
427                    MtpDebug::getOperationCodeName(operation), operation);
428            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
429            break;
430    }
431
432    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
433        return false;
434    mResponse.setResponseCode(response);
435    return true;
436}
437
438MtpResponseCode MtpServer::doGetDeviceInfo() {
439    MtpStringBuffer   string;
440    char prop_value[PROPERTY_VALUE_MAX];
441
442    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
443    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
444    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
445
446    // fill in device info
447    mData.putUInt16(MTP_STANDARD_VERSION);
448    if (mPtp) {
449        mData.putUInt32(0);
450    } else {
451        // MTP Vendor Extension ID
452        mData.putUInt32(6);
453    }
454    mData.putUInt16(MTP_STANDARD_VERSION);
455    if (mPtp) {
456        // no extensions
457        string.set("");
458    } else {
459        // MTP extensions
460        string.set("microsoft.com: 1.0; android.com: 1.0;");
461    }
462    mData.putString(string); // MTP Extensions
463    mData.putUInt16(0); //Functional Mode
464    mData.putAUInt16(kSupportedOperationCodes,
465            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
466    mData.putAUInt16(kSupportedEventCodes,
467            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
468    mData.putAUInt16(deviceProperties); // Device Properties Supported
469    mData.putAUInt16(captureFormats); // Capture Formats
470    mData.putAUInt16(playbackFormats);  // Playback Formats
471
472    property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
473    string.set(prop_value);
474    mData.putString(string);   // Manufacturer
475
476    property_get("ro.product.model", prop_value, "MTP Device");
477    string.set(prop_value);
478    mData.putString(string);   // Model
479    string.set("1.0");
480    mData.putString(string);   // Device Version
481
482    property_get("ro.serialno", prop_value, "????????");
483    string.set(prop_value);
484    mData.putString(string);   // Serial Number
485
486    delete playbackFormats;
487    delete captureFormats;
488    delete deviceProperties;
489
490    return MTP_RESPONSE_OK;
491}
492
493MtpResponseCode MtpServer::doOpenSession() {
494    if (mSessionOpen) {
495        mResponse.setParameter(1, mSessionID);
496        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
497    }
498    mSessionID = mRequest.getParameter(1);
499    mSessionOpen = true;
500
501    mDatabase->sessionStarted();
502
503    return MTP_RESPONSE_OK;
504}
505
506MtpResponseCode MtpServer::doCloseSession() {
507    if (!mSessionOpen)
508        return MTP_RESPONSE_SESSION_NOT_OPEN;
509    mSessionID = 0;
510    mSessionOpen = false;
511    mDatabase->sessionEnded();
512    return MTP_RESPONSE_OK;
513}
514
515MtpResponseCode MtpServer::doGetStorageIDs() {
516    if (!mSessionOpen)
517        return MTP_RESPONSE_SESSION_NOT_OPEN;
518
519    int count = mStorages.size();
520    mData.putUInt32(count);
521    for (int i = 0; i < count; i++)
522        mData.putUInt32(mStorages[i]->getStorageID());
523
524    return MTP_RESPONSE_OK;
525}
526
527MtpResponseCode MtpServer::doGetStorageInfo() {
528    MtpStringBuffer   string;
529
530    if (!mSessionOpen)
531        return MTP_RESPONSE_SESSION_NOT_OPEN;
532    MtpStorageID id = mRequest.getParameter(1);
533    MtpStorage* storage = getStorage(id);
534    if (!storage)
535        return MTP_RESPONSE_INVALID_STORAGE_ID;
536
537    mData.putUInt16(storage->getType());
538    mData.putUInt16(storage->getFileSystemType());
539    mData.putUInt16(storage->getAccessCapability());
540    mData.putUInt64(storage->getMaxCapacity());
541    mData.putUInt64(storage->getFreeSpace());
542    mData.putUInt32(1024*1024*1024); // Free Space in Objects
543    string.set(storage->getDescription());
544    mData.putString(string);
545    mData.putEmptyString();   // Volume Identifier
546
547    return MTP_RESPONSE_OK;
548}
549
550MtpResponseCode MtpServer::doGetObjectPropsSupported() {
551    if (!mSessionOpen)
552        return MTP_RESPONSE_SESSION_NOT_OPEN;
553    MtpObjectFormat format = mRequest.getParameter(1);
554    MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
555    mData.putAUInt16(properties);
556    delete properties;
557    return MTP_RESPONSE_OK;
558}
559
560MtpResponseCode MtpServer::doGetObjectHandles() {
561    if (!mSessionOpen)
562        return MTP_RESPONSE_SESSION_NOT_OPEN;
563    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
564    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
565    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
566                                                            // 0x00000000 for all objects
567
568    if (!hasStorage(storageID))
569        return MTP_RESPONSE_INVALID_STORAGE_ID;
570
571    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
572    mData.putAUInt32(handles);
573    delete handles;
574    return MTP_RESPONSE_OK;
575}
576
577MtpResponseCode MtpServer::doGetNumObjects() {
578    if (!mSessionOpen)
579        return MTP_RESPONSE_SESSION_NOT_OPEN;
580    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
581    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
582    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
583                                                            // 0x00000000 for all objects
584    if (!hasStorage(storageID))
585        return MTP_RESPONSE_INVALID_STORAGE_ID;
586
587    int count = mDatabase->getNumObjects(storageID, format, parent);
588    if (count >= 0) {
589        mResponse.setParameter(1, count);
590        return MTP_RESPONSE_OK;
591    } else {
592        mResponse.setParameter(1, 0);
593        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
594    }
595}
596
597MtpResponseCode MtpServer::doGetObjectReferences() {
598    if (!mSessionOpen)
599        return MTP_RESPONSE_SESSION_NOT_OPEN;
600    if (!hasStorage())
601        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
602    MtpObjectHandle handle = mRequest.getParameter(1);
603
604    // FIXME - check for invalid object handle
605    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
606    if (handles) {
607        mData.putAUInt32(handles);
608        delete handles;
609    } else {
610        mData.putEmptyArray();
611    }
612    return MTP_RESPONSE_OK;
613}
614
615MtpResponseCode MtpServer::doSetObjectReferences() {
616    if (!mSessionOpen)
617        return MTP_RESPONSE_SESSION_NOT_OPEN;
618    if (!hasStorage())
619        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
620    MtpStorageID handle = mRequest.getParameter(1);
621
622    MtpObjectHandleList* references = mData.getAUInt32();
623    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
624    delete references;
625    return result;
626}
627
628MtpResponseCode MtpServer::doGetObjectPropValue() {
629    if (!hasStorage())
630        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
631    MtpObjectHandle handle = mRequest.getParameter(1);
632    MtpObjectProperty property = mRequest.getParameter(2);
633    ALOGV("GetObjectPropValue %d %s\n", handle,
634            MtpDebug::getObjectPropCodeName(property));
635
636    return mDatabase->getObjectPropertyValue(handle, property, mData);
637}
638
639MtpResponseCode MtpServer::doSetObjectPropValue() {
640    if (!hasStorage())
641        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
642    MtpObjectHandle handle = mRequest.getParameter(1);
643    MtpObjectProperty property = mRequest.getParameter(2);
644    ALOGV("SetObjectPropValue %d %s\n", handle,
645            MtpDebug::getObjectPropCodeName(property));
646
647    return mDatabase->setObjectPropertyValue(handle, property, mData);
648}
649
650MtpResponseCode MtpServer::doGetDevicePropValue() {
651    MtpDeviceProperty property = mRequest.getParameter(1);
652    ALOGV("GetDevicePropValue %s\n",
653            MtpDebug::getDevicePropCodeName(property));
654
655    return mDatabase->getDevicePropertyValue(property, mData);
656}
657
658MtpResponseCode MtpServer::doSetDevicePropValue() {
659    MtpDeviceProperty property = mRequest.getParameter(1);
660    ALOGV("SetDevicePropValue %s\n",
661            MtpDebug::getDevicePropCodeName(property));
662
663    return mDatabase->setDevicePropertyValue(property, mData);
664}
665
666MtpResponseCode MtpServer::doResetDevicePropValue() {
667    MtpDeviceProperty property = mRequest.getParameter(1);
668    ALOGV("ResetDevicePropValue %s\n",
669            MtpDebug::getDevicePropCodeName(property));
670
671    return mDatabase->resetDeviceProperty(property);
672}
673
674MtpResponseCode MtpServer::doGetObjectPropList() {
675    if (!hasStorage())
676        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
677
678    MtpObjectHandle handle = mRequest.getParameter(1);
679    // use uint32_t so we can support 0xFFFFFFFF
680    uint32_t format = mRequest.getParameter(2);
681    uint32_t property = mRequest.getParameter(3);
682    int groupCode = mRequest.getParameter(4);
683    int depth = mRequest.getParameter(5);
684   ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
685            handle, MtpDebug::getFormatCodeName(format),
686            MtpDebug::getObjectPropCodeName(property), groupCode, depth);
687
688    return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
689}
690
691MtpResponseCode MtpServer::doGetObjectInfo() {
692    if (!hasStorage())
693        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
694    MtpObjectHandle handle = mRequest.getParameter(1);
695    MtpObjectInfo info(handle);
696    MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
697    if (result == MTP_RESPONSE_OK) {
698        char    date[20];
699
700        mData.putUInt32(info.mStorageID);
701        mData.putUInt16(info.mFormat);
702        mData.putUInt16(info.mProtectionStatus);
703
704        // if object is being edited the database size may be out of date
705        uint32_t size = info.mCompressedSize;
706        ObjectEdit* edit = getEditObject(handle);
707        if (edit)
708            size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
709        mData.putUInt32(size);
710
711        mData.putUInt16(info.mThumbFormat);
712        mData.putUInt32(info.mThumbCompressedSize);
713        mData.putUInt32(info.mThumbPixWidth);
714        mData.putUInt32(info.mThumbPixHeight);
715        mData.putUInt32(info.mImagePixWidth);
716        mData.putUInt32(info.mImagePixHeight);
717        mData.putUInt32(info.mImagePixDepth);
718        mData.putUInt32(info.mParent);
719        mData.putUInt16(info.mAssociationType);
720        mData.putUInt32(info.mAssociationDesc);
721        mData.putUInt32(info.mSequenceNumber);
722        mData.putString(info.mName);
723        formatDateTime(info.mDateCreated, date, sizeof(date));
724        mData.putString(date);   // date created
725        formatDateTime(info.mDateModified, date, sizeof(date));
726        mData.putString(date);   // date modified
727        mData.putEmptyString();   // keywords
728    }
729    return result;
730}
731
732MtpResponseCode MtpServer::doGetObject() {
733    if (!hasStorage())
734        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
735    MtpObjectHandle handle = mRequest.getParameter(1);
736    MtpString pathBuf;
737    int64_t fileLength;
738    MtpObjectFormat format;
739    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
740    if (result != MTP_RESPONSE_OK)
741        return result;
742
743    const char* filePath = (const char *)pathBuf;
744    mtp_file_range  mfr;
745    mfr.fd = open(filePath, O_RDONLY);
746    if (mfr.fd < 0) {
747        return MTP_RESPONSE_GENERAL_ERROR;
748    }
749    mfr.offset = 0;
750    mfr.length = fileLength;
751    mfr.command = mRequest.getOperationCode();
752    mfr.transaction_id = mRequest.getTransactionID();
753
754    // then transfer the file
755    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
756    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
757    close(mfr.fd);
758    if (ret < 0) {
759        if (errno == ECANCELED)
760            return MTP_RESPONSE_TRANSACTION_CANCELLED;
761        else
762            return MTP_RESPONSE_GENERAL_ERROR;
763    }
764    return MTP_RESPONSE_OK;
765}
766
767MtpResponseCode MtpServer::doGetThumb() {
768    MtpObjectHandle handle = mRequest.getParameter(1);
769    size_t thumbSize;
770    void* thumb = mDatabase->getThumbnail(handle, thumbSize);
771    if (thumb) {
772        // send data
773        mData.setOperationCode(mRequest.getOperationCode());
774        mData.setTransactionID(mRequest.getTransactionID());
775        mData.writeData(mFD, thumb, thumbSize);
776        free(thumb);
777        return MTP_RESPONSE_OK;
778    } else {
779        return MTP_RESPONSE_GENERAL_ERROR;
780    }
781}
782
783MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
784    if (!hasStorage())
785        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
786    MtpObjectHandle handle = mRequest.getParameter(1);
787    uint64_t offset;
788    uint32_t length;
789    offset = mRequest.getParameter(2);
790    if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
791        // android extension with 64 bit offset
792        uint64_t offset2 = mRequest.getParameter(3);
793        offset = offset | (offset2 << 32);
794        length = mRequest.getParameter(4);
795    } else {
796        // standard GetPartialObject
797        length = mRequest.getParameter(3);
798    }
799    MtpString pathBuf;
800    int64_t fileLength;
801    MtpObjectFormat format;
802    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
803    if (result != MTP_RESPONSE_OK)
804        return result;
805    if (offset + length > fileLength)
806        length = fileLength - offset;
807
808    const char* filePath = (const char *)pathBuf;
809    mtp_file_range  mfr;
810    mfr.fd = open(filePath, O_RDONLY);
811    if (mfr.fd < 0) {
812        return MTP_RESPONSE_GENERAL_ERROR;
813    }
814    mfr.offset = offset;
815    mfr.length = length;
816    mfr.command = mRequest.getOperationCode();
817    mfr.transaction_id = mRequest.getTransactionID();
818    mResponse.setParameter(1, length);
819
820    // transfer the file
821    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
822    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
823    close(mfr.fd);
824    if (ret < 0) {
825        if (errno == ECANCELED)
826            return MTP_RESPONSE_TRANSACTION_CANCELLED;
827        else
828            return MTP_RESPONSE_GENERAL_ERROR;
829    }
830    return MTP_RESPONSE_OK;
831}
832
833MtpResponseCode MtpServer::doSendObjectInfo() {
834    MtpString path;
835    MtpStorageID storageID = mRequest.getParameter(1);
836    MtpStorage* storage = getStorage(storageID);
837    MtpObjectHandle parent = mRequest.getParameter(2);
838    if (!storage)
839        return MTP_RESPONSE_INVALID_STORAGE_ID;
840
841    // special case the root
842    if (parent == MTP_PARENT_ROOT) {
843        path = storage->getPath();
844        parent = 0;
845    } else {
846        int64_t length;
847        MtpObjectFormat format;
848        int result = mDatabase->getObjectFilePath(parent, path, length, format);
849        if (result != MTP_RESPONSE_OK)
850            return result;
851        if (format != MTP_FORMAT_ASSOCIATION)
852            return MTP_RESPONSE_INVALID_PARENT_OBJECT;
853    }
854
855    // read only the fields we need
856    mData.getUInt32();  // storage ID
857    MtpObjectFormat format = mData.getUInt16();
858    mData.getUInt16();  // protection status
859    mSendObjectFileSize = mData.getUInt32();
860    mData.getUInt16();  // thumb format
861    mData.getUInt32();  // thumb compressed size
862    mData.getUInt32();  // thumb pix width
863    mData.getUInt32();  // thumb pix height
864    mData.getUInt32();  // image pix width
865    mData.getUInt32();  // image pix height
866    mData.getUInt32();  // image bit depth
867    mData.getUInt32();  // parent
868    uint16_t associationType = mData.getUInt16();
869    uint32_t associationDesc = mData.getUInt32();   // association desc
870    mData.getUInt32();  // sequence number
871    MtpStringBuffer name, created, modified;
872    mData.getString(name);    // file name
873    mData.getString(created);      // date created
874    mData.getString(modified);     // date modified
875    // keywords follow
876
877    ALOGV("name: %s format: %04X\n", (const char *)name, format);
878    time_t modifiedTime;
879    if (!parseDateTime(modified, modifiedTime))
880        modifiedTime = 0;
881
882    if (path[path.size() - 1] != '/')
883        path += "/";
884    path += (const char *)name;
885
886    // check space first
887    if (mSendObjectFileSize > storage->getFreeSpace())
888        return MTP_RESPONSE_STORAGE_FULL;
889    uint64_t maxFileSize = storage->getMaxFileSize();
890    // check storage max file size
891    if (maxFileSize != 0) {
892        // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
893        // is >= 0xFFFFFFFF
894        if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
895            return MTP_RESPONSE_OBJECT_TOO_LARGE;
896    }
897
898    ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
899    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
900            format, parent, storageID, mSendObjectFileSize, modifiedTime);
901    if (handle == kInvalidObjectHandle) {
902        return MTP_RESPONSE_GENERAL_ERROR;
903    }
904
905  if (format == MTP_FORMAT_ASSOCIATION) {
906        mode_t mask = umask(0);
907        int ret = mkdir((const char *)path, mDirectoryPermission);
908        umask(mask);
909        if (ret && ret != -EEXIST)
910            return MTP_RESPONSE_GENERAL_ERROR;
911        chown((const char *)path, getuid(), mFileGroup);
912
913        // SendObject does not get sent for directories, so call endSendObject here instead
914        mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
915    } else {
916        mSendObjectFilePath = path;
917        // save the handle for the SendObject call, which should follow
918        mSendObjectHandle = handle;
919        mSendObjectFormat = format;
920    }
921
922    mResponse.setParameter(1, storageID);
923    mResponse.setParameter(2, parent);
924    mResponse.setParameter(3, handle);
925
926    return MTP_RESPONSE_OK;
927}
928
929MtpResponseCode MtpServer::doSendObject() {
930    if (!hasStorage())
931        return MTP_RESPONSE_GENERAL_ERROR;
932    MtpResponseCode result = MTP_RESPONSE_OK;
933    mode_t mask;
934    int ret, initialData;
935
936    if (mSendObjectHandle == kInvalidObjectHandle) {
937        ALOGE("Expected SendObjectInfo before SendObject");
938        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
939        goto done;
940    }
941
942    // read the header, and possibly some data
943    ret = mData.read(mFD);
944    if (ret < MTP_CONTAINER_HEADER_SIZE) {
945        result = MTP_RESPONSE_GENERAL_ERROR;
946        goto done;
947    }
948    initialData = ret - MTP_CONTAINER_HEADER_SIZE;
949
950    mtp_file_range  mfr;
951    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
952    if (mfr.fd < 0) {
953        result = MTP_RESPONSE_GENERAL_ERROR;
954        goto done;
955    }
956    fchown(mfr.fd, getuid(), mFileGroup);
957    // set permissions
958    mask = umask(0);
959    fchmod(mfr.fd, mFilePermission);
960    umask(mask);
961
962    if (initialData > 0) {
963        ret = write(mfr.fd, mData.getData(), initialData);
964    }
965
966    if (ret < 0) {
967        ALOGE("failed to write initial data");
968        result = MTP_RESPONSE_GENERAL_ERROR;
969    } else {
970        if (mSendObjectFileSize - initialData > 0) {
971            mfr.offset = initialData;
972            if (mSendObjectFileSize == 0xFFFFFFFF) {
973                // tell driver to read until it receives a short packet
974                mfr.length = 0xFFFFFFFF;
975            } else {
976                mfr.length = mSendObjectFileSize - initialData;
977            }
978
979            ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
980            // transfer the file
981            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
982            ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
983        }
984    }
985    close(mfr.fd);
986
987    if (ret < 0) {
988        unlink(mSendObjectFilePath);
989        if (errno == ECANCELED)
990            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
991        else
992            result = MTP_RESPONSE_GENERAL_ERROR;
993    }
994
995done:
996    // reset so we don't attempt to send the data back
997    mData.reset();
998
999    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
1000            result == MTP_RESPONSE_OK);
1001    mSendObjectHandle = kInvalidObjectHandle;
1002    mSendObjectFormat = 0;
1003    return result;
1004}
1005
1006static void deleteRecursive(const char* path) {
1007    char pathbuf[PATH_MAX];
1008    int pathLength = strlen(path);
1009    if (pathLength >= sizeof(pathbuf) - 1) {
1010        ALOGE("path too long: %s\n", path);
1011    }
1012    strcpy(pathbuf, path);
1013    if (pathbuf[pathLength - 1] != '/') {
1014        pathbuf[pathLength++] = '/';
1015    }
1016    char* fileSpot = pathbuf + pathLength;
1017    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
1018
1019    DIR* dir = opendir(path);
1020    if (!dir) {
1021        ALOGE("opendir %s failed: %s", path, strerror(errno));
1022        return;
1023    }
1024
1025    struct dirent* entry;
1026    while ((entry = readdir(dir))) {
1027        const char* name = entry->d_name;
1028
1029        // ignore "." and ".."
1030        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1031            continue;
1032        }
1033
1034        int nameLength = strlen(name);
1035        if (nameLength > pathRemaining) {
1036            ALOGE("path %s/%s too long\n", path, name);
1037            continue;
1038        }
1039        strcpy(fileSpot, name);
1040
1041        int type = entry->d_type;
1042        if (entry->d_type == DT_DIR) {
1043            deleteRecursive(pathbuf);
1044            rmdir(pathbuf);
1045        } else {
1046            unlink(pathbuf);
1047        }
1048    }
1049    closedir(dir);
1050}
1051
1052static void deletePath(const char* path) {
1053    struct stat statbuf;
1054    if (stat(path, &statbuf) == 0) {
1055        if (S_ISDIR(statbuf.st_mode)) {
1056            deleteRecursive(path);
1057            rmdir(path);
1058        } else {
1059            unlink(path);
1060        }
1061    } else {
1062        ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
1063    }
1064}
1065
1066MtpResponseCode MtpServer::doDeleteObject() {
1067    if (!hasStorage())
1068        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1069    MtpObjectHandle handle = mRequest.getParameter(1);
1070    MtpObjectFormat format = mRequest.getParameter(2);
1071    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1072    // FIXME - implement deleting objects by format
1073
1074    MtpString filePath;
1075    int64_t fileLength;
1076    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1077    if (result == MTP_RESPONSE_OK) {
1078        ALOGV("deleting %s", (const char *)filePath);
1079        result = mDatabase->deleteFile(handle);
1080        // Don't delete the actual files unless the database deletion is allowed
1081        if (result == MTP_RESPONSE_OK) {
1082            deletePath((const char *)filePath);
1083        }
1084    }
1085
1086    return result;
1087}
1088
1089MtpResponseCode MtpServer::doGetObjectPropDesc() {
1090    MtpObjectProperty propCode = mRequest.getParameter(1);
1091    MtpObjectFormat format = mRequest.getParameter(2);
1092    ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1093                                        MtpDebug::getFormatCodeName(format));
1094    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1095    if (!property)
1096        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1097    property->write(mData);
1098    delete property;
1099    return MTP_RESPONSE_OK;
1100}
1101
1102MtpResponseCode MtpServer::doGetDevicePropDesc() {
1103    MtpDeviceProperty propCode = mRequest.getParameter(1);
1104    ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1105    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1106    if (!property)
1107        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1108    property->write(mData);
1109    delete property;
1110    return MTP_RESPONSE_OK;
1111}
1112
1113MtpResponseCode MtpServer::doSendPartialObject() {
1114    if (!hasStorage())
1115        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1116    MtpObjectHandle handle = mRequest.getParameter(1);
1117    uint64_t offset = mRequest.getParameter(2);
1118    uint64_t offset2 = mRequest.getParameter(3);
1119    offset = offset | (offset2 << 32);
1120    uint32_t length = mRequest.getParameter(4);
1121
1122    ObjectEdit* edit = getEditObject(handle);
1123    if (!edit) {
1124        ALOGE("object not open for edit in doSendPartialObject");
1125        return MTP_RESPONSE_GENERAL_ERROR;
1126    }
1127
1128    // can't start writing past the end of the file
1129    if (offset > edit->mSize) {
1130        ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
1131        return MTP_RESPONSE_GENERAL_ERROR;
1132    }
1133
1134    const char* filePath = (const char *)edit->mPath;
1135    ALOGV("receiving partial %s %lld %" PRIu32 "\n", filePath, offset, length);
1136
1137    // read the header, and possibly some data
1138    int ret = mData.read(mFD);
1139    if (ret < MTP_CONTAINER_HEADER_SIZE)
1140        return MTP_RESPONSE_GENERAL_ERROR;
1141    int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1142
1143    if (initialData > 0) {
1144        ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
1145        offset += initialData;
1146        length -= initialData;
1147    }
1148
1149    if (ret < 0) {
1150        ALOGE("failed to write initial data");
1151    } else {
1152        if (length > 0) {
1153            mtp_file_range  mfr;
1154            mfr.fd = edit->mFD;
1155            mfr.offset = offset;
1156            mfr.length = length;
1157
1158            // transfer the file
1159            ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1160            ALOGV("MTP_RECEIVE_FILE returned %d", ret);
1161        }
1162    }
1163    if (ret < 0) {
1164        mResponse.setParameter(1, 0);
1165        if (errno == ECANCELED)
1166            return MTP_RESPONSE_TRANSACTION_CANCELLED;
1167        else
1168            return MTP_RESPONSE_GENERAL_ERROR;
1169    }
1170
1171    // reset so we don't attempt to send this back
1172    mData.reset();
1173    mResponse.setParameter(1, length);
1174    uint64_t end = offset + length;
1175    if (end > edit->mSize) {
1176        edit->mSize = end;
1177    }
1178    return MTP_RESPONSE_OK;
1179}
1180
1181MtpResponseCode MtpServer::doTruncateObject() {
1182    MtpObjectHandle handle = mRequest.getParameter(1);
1183    ObjectEdit* edit = getEditObject(handle);
1184    if (!edit) {
1185        ALOGE("object not open for edit in doTruncateObject");
1186        return MTP_RESPONSE_GENERAL_ERROR;
1187    }
1188
1189    uint64_t offset = mRequest.getParameter(2);
1190    uint64_t offset2 = mRequest.getParameter(3);
1191    offset |= (offset2 << 32);
1192    if (ftruncate(edit->mFD, offset) != 0) {
1193        return MTP_RESPONSE_GENERAL_ERROR;
1194    } else {
1195        edit->mSize = offset;
1196        return MTP_RESPONSE_OK;
1197    }
1198}
1199
1200MtpResponseCode MtpServer::doBeginEditObject() {
1201    MtpObjectHandle handle = mRequest.getParameter(1);
1202    if (getEditObject(handle)) {
1203        ALOGE("object already open for edit in doBeginEditObject");
1204        return MTP_RESPONSE_GENERAL_ERROR;
1205    }
1206
1207    MtpString path;
1208    int64_t fileLength;
1209    MtpObjectFormat format;
1210    int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1211    if (result != MTP_RESPONSE_OK)
1212        return result;
1213
1214    int fd = open((const char *)path, O_RDWR | O_EXCL);
1215    if (fd < 0) {
1216        ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1217        return MTP_RESPONSE_GENERAL_ERROR;
1218    }
1219
1220    addEditObject(handle, path, fileLength, format, fd);
1221    return MTP_RESPONSE_OK;
1222}
1223
1224MtpResponseCode MtpServer::doEndEditObject() {
1225    MtpObjectHandle handle = mRequest.getParameter(1);
1226    ObjectEdit* edit = getEditObject(handle);
1227    if (!edit) {
1228        ALOGE("object not open for edit in doEndEditObject");
1229        return MTP_RESPONSE_GENERAL_ERROR;
1230    }
1231
1232    commitEdit(edit);
1233    removeEditObject(handle);
1234    return MTP_RESPONSE_OK;
1235}
1236
1237}  // namespace android
1238