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