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