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